leetcode148. 排序链表

题目描述

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

示例 1:

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

示例 2:

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

code

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    //二路归并,返回归并后链表头结点
    ListNode* Merge(ListNode* l1, ListNode* l2)
    {
        //设置虚拟头结点
        ListNode dummyHead(0);
        ListNode* p = &dummyHead;

        while(l1&&l2)
        {
            if(l1->val<l2->val){
                p->next = l1;
                l1 = l1->next;
                p = p->next;
            }
            else {
                p->next = l2;
                l2 = l2->next;
                p = p->next;
            }
        }
        p->next = l1 ? l1 : l2;
        return dummyHead.next;//返回排序链表的真正头结点
    }
    //剪下链表的前step个节点,返回剩余链表的头结点
    ListNode* cut(ListNode* head, int step)
    {
        ListNode* p = head;
        while(--step && p){//由head前进step-1步即可
            p = p->next;
        }

        if(!p) //当剩余链表不足step个节点时,返回空链表
            return NULL;

        ListNode* next = p->next;//next指向剩余链表头结点
        p->next = NULL;
        return next;
    }
    ListNode* sortList(ListNode* head) {
        //设置虚拟头结点
        ListNode dummyHead(0);
        dummyHead.next = head;
        //计算链表长度
        ListNode* p = &dummyHead;
        int len = 0;
        while(p->next){
            ++len;
            p = p->next;
        }
        //bottom-to-up自底向上归并排序
        //step = 1,2,4,8,...当step大于等于len时,排序结束
        for(int step = 1; step < len; step <<= 1){
            ListNode* cur = dummyHead.next;//待排序链表头指针
            ListNode* tail = &dummyHead;//已排序链表尾指针

            while(cur){//待排序链表不为空
                ListNode* left = cur;//left指向待归并链表l1
                ListNode* right = cut(left,step);//right事项待归并链表l2
                cur = cut(right,step);//cur指向剩余待排序链表

                //归并l1和l2,返回归并后链表头结点,利用tail连接前面已排序的链表和该链表
                tail->next = Merge(left,right);
                while(tail->next){//更新tail:tail永远指向已排序链表的尾结点
                    tail = tail->next;
                }
            }
        }
        return dummyHead.next;
    }
};

解析

  • 链表排序的最佳方案:归并排序,优于快排
    • 使用bottom-to-up:时间o(nlogn) ;空间;o(1) //原地排序
    • 使用递归:时间o(nlogn);空间o(n)
  • dummyHead:虚拟头结点
    • 链表设置虚拟头结点dummyhead,其位置所对应的元素是根本不存在的,只是为了编写逻辑方便。
    • 这样对链表来说,第一个元素(索引为0位置的元素)就是dummyhead的next所对应的节点元素
    • 设置dummyhead后,为链表添加一个元素,就不需要对头结点进行特殊处理了,因为此时对于链表来说,所有位置都有前一个节点
    • 参考:为链表设置虚拟头结点dummyhead
    • 注意dummyHead是节点,不是指针(下面的tail/left/right/cur是指针)
      构造初始化:ListNode* dummyHead(0);
  • 使用的几个节点
    • dummyHead:虚拟头结点,便于编写代码(不用对真正头结点做特殊处理,保持逻辑一致)
    • tail:已排序链表的尾指针,始终指向已排好序的链表的非空尾结点处,便于与下一段排序链表进行连接
    • left:指向当前待归并的左边链表的头结点的指针
    • right:指向当前待归并的右边链表的头结点的指针
    • cur:未排序链表的头结点,每一路归并终止条件:当cur指向NULL时
  • 排序j结束条件:step >= len时(step:归并步数;len:链表总长度)
    在这里插入图片描述
©️2020 CSDN 皮肤主题: 像素格子 设计师:CSDN官方博客 返回首页