leetcode_148. 排序链表

帅比萌擦擦* 提交于 2020-01-21 19:01:33

在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序。

示例 1:

输入: 4->2->1->3
输出: 1->2->3->4

示例 2:

输入: -1->5->3->4->0
输出: -1->0->3->4->5

解析:对时间空间复杂度有要求,那么就必须思考用什么方法进行排序
对于时间复杂度O(nlogn) ,可以考虑归并排序或者快速排序
而归并排序在链表的情况下,可以优化为O(1)的空间复杂度。
因此本题采用归并排序

归并排序的要点在于两点,分治的拆分链表,以及合并两个链表
我们分别处理这两个功能,并归纳为函数

对于自顶向下的归并排序,通常采用递归调用,每个递归函数的功能便是找到目前的中心,并对两端进行递归调用,最后合并后返回上一层。
而这样的整个过程,必然会产生函数的递归调用,并因此消耗额外的函数栈空间。不符合题目要求的O(1)空间复杂度要求。
因此我们需要将递归修改为迭代。

我们转变方向,自下而上的进行归并排序。
对自顶向下的一般归并方法进行分析,发现最终的函数调用顺序是:
对1、2两个节点进行归并
对3、4两个节点进行归并
对1-4四个节点进行归并
对5、6两个节点进行归并
对7、8两个节点进行归并
对5-8四个节点进行归并
对1-8八个节点进行归并。。。
可以看出,
整个过程其实是先每两个一组进行合并,然后每四个一组,八个,十六个
每次合并的大小扩张为原来的两倍。
因此我们可以用迭代的方式手动模拟递归。两两合并、然后四个一组合并。。。

下面是最后代码

class Solution {
public:
    ListNode* sortList(ListNode* head) {
        //虚拟头节点,用来定位
        ListNode p(0);
        ListNode *pre = &p;
        pre->next = head;
        //获取链表长度
        ListNode *tmp = head;
        int n=0;
        while(tmp){
            ++n;
            tmp = tmp->next;
        }
        //按照1,2,4,8...的数量开始层层归并排序
        for(int i = 1;i<n;i*=2){
            ListNode *cur = pre->next; //目前的头节点
            ListNode *tail = pre;   //归并后链表的连接位置
            while(cur){
                ListNode *left = cur; //第一段链表的起点
                ListNode *right = cut(left,i); //第二段链表的起点
                cur = cut(right,i); //cur指向后序链表
                //此时left,right 分别指向两条长度为i的,从原链表上切下的子链表
                tail->next = merge(left,right); //归并两条子链表,连接至tail节点后
                while(tail->next) tail = tail->next; //将连接位置后移
            }
        }
        return pre->next;
        
    }
    
    //将链表从cur节点开始切下num个节点,返回被切下来节点之后的节点
    ListNode* cut(ListNode* cur,int num){
        ListNode* tmp = cur;
        while(tmp && --num) tmp = tmp->next;
        if(!tmp) return nullptr;
        ListNode* res = tmp->next;
        tmp->next = nullptr;
        return res;
    }
    
    //将给定的两个有序链表合并,返回新链表的头节点
    ListNode* merge(ListNode* left,ListNode* right){
        ListNode p(0);
        ListNode *pre = &p;
        ListNode *head = &p;
        while(left && right){
            if(left->val > right->val){
                pre->next = right;
                right = right->next;
                pre = pre->next;
            }
            else{
                pre->next = left;
                left = left->next;
                pre = pre->next;
            }
        }
        pre->next = (left?left:right);
        return head->next;
    }
};
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!