链表(二)

本文详细介绍了一种链表的排序方法——自底向上的归并排序,通过迭代而非递归实现,避免了额外的空间开销,实现了O(nlogn)的时间复杂度和O(1)的空间复杂度。

148.排序链表

(归并排序) 时间:O(nlogn),空间O(1)时间:O(nlogn),空间O(1)
自顶向下递归形式的归并排序,由于递归需要使用系统栈,递归的最大深度是 logn,所以需要额外O(logn) 的空间。
所以我们需要使用自底向上非递归形式的归并排序算法。
基本思路是这样的,总共迭代 logn 次:

第一次,将整个区间分成连续的若干段,每段长度是2:[a0,a1],[a2,a3],…[an−1,an−1][a0,a1],[a2,a3],…[an−1,an−1], 然后将每一段内排好序,小数在前,大数在后;
第二次,将整个区间分成连续的若干段,每段长度是4:[a0,…,a3],[a4,…,a7],…[an−4,…,an−1][a0,…,a3],[a4,…,a7],…[an−4,…,an−1],然后将每一段内排好序,这次排序可以利用之前的结果,相当于将左右两个有序的半区间合并,可以通过一次线性扫描来完成;
依此类推,直到每段小区间的长度大于等于 nn 为止;
另外,当 nn 不是2的整次幂时,每次迭代只有最后一个区间会比较特殊,长度会小一些,遍历到指针为空时需要提前结束。

时间复杂度分析:整个链表总共遍历 lognlogn 次,每次遍历的复杂度是 O(n)O(n),所以总时间复杂度是 O(nlogn)O(nlogn)。
空间复杂度分析:整个算法没有递归,迭代时只会使用常数个额外变量,所以额外空间复杂度是 O(1)

#include<iostream>
using namespace std;
struct ListNode 
{
	int val;
	ListNode *next;
	ListNode(int x) : val(x), next(NULL) {}
};
class Solution 
{
public:
    ListNode* sortList(ListNode* head)
	{
        /*
		自顶向下递归形式的归并排序,由于递归需要使用系统栈,递归的最大深度是 lognlogn,所以需要额外 O(logn)O(logn) 的空间。
		所以我们需要使用自底向上非递归形式的归并排序算法。  O(nlogn)  O(1)
		*/
		auto dummy = new ListNode(-1);   //定义一个虚拟结点
		dummy->next = head;                  
		int n = 0;

		for(auto p = head;p;p=p->next)    //求出链表的长度n
			n++;

		for(int i = 1;i<n;i *= 2)     //i从第一个开始遍历
		{
			auto cur = dummy;    
			for(int j=0; j+i<n; j+=i*2)   //枚举每一对
			{
				auto left = cur->next, right = cur->next;   //l表示左边那一对的起点,r表示右边那一对的起点
				for(int k = 0;k<i;k++)
				{
					right = right->next;
				}
				int l = 0,r = 0;
				while(l<i && r<i && right)
				{
					if(left->val <= right->val)
					{
						cur->next = left;
						cur = left;
						left = left->next;
						l++;
					}
					else
					{
						cur->next = right;
						cur=right;
						right = right->next;
						r++;
					}
				}
				while(l<i)
				{
					cur->next = left;
					cur = left;
					left = left->next;
					l++;
				}
				while(r<i && right)
				{
					cur->next = right;
					cur=right;
					right = right->next;
					r++;
				}
				cur->next = right;
			}
		}
		return dummy->next;
    }
};

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值