[leetcode] Sort List

Sort List

  • 问题描述:尝试用O(nlgn)的时间复杂度,O(1)的空间复杂度对一个链表排序
  • 分析
    • 因为是O(nlgn)的时间复杂度,所以我们第一感觉就是快排是否可以?
      • 因为对于每一段数组来说,快排即需要从前向后也需要从后向前。所以对于我们的单向链表来说有点不适合。
    • 那么另一个排序方法:归并排序呢?
      • 归并排序常规的做法是up-to-bottom,即先分只一个元素,然后再合并。
      • 但是这对于链表来说我们需要计算mid,而我们不能在O(1)的时间复杂度得到mid,而是需要遍历才能得到第mid个元素,所以也不行。
      • 所以我们能否可以考虑buttom-to-up呢?
      • 对于一个链表,我们首先对长度为1的元素进行排序,然后两两合并。然后再对长度为2的子集进行切分(因为前面长度为1的时候我们进行了俩俩合并操作,所以此时这个子集内的元素是有序的),然后再进行合并。一直到我们对长度为整个list长进行切分。合并。这样我们就完成了对list 的排序。
      • 对于边界情况
        • 因为每次我们会得到两个长度为X的子集。如果第一个子集的起始点是H。假设说链表的长度已经不足X,则第二个子集的起始点是NULL。如果说第一个子集的起始点就是NULL,则第二个子集的起始点必定也是NULL。
        • 我们得到了两个划分好的子集后,我们要对他们进行合并。如何存储合并后的结果呢?我们必须新建一个节点,他是第一个子集的起始元素的前一个A。那这样两个子集中的最小元素就是A的下一个元素。
  • 代码
ListNode* sortList(ListNode* head) {
        if(head == NULL || head->next == NULL)
            return head;
        int len = 0; // 得到链表的长度
        ListNode* cur = head;
        while (cur){
            cur = cur->next;
            len += 1;
        }
        ListNode* dummy = new ListNode(0);
        dummy->next = head;
        ListNode* prior;
        for(int step=1;step<len;step <<= 1){
            // step 代表的是每个ele的长度,一开始都是1,每次乘以2
            ListNode* cur = dummy->next;
            prior = dummy;
            while(cur){
                ListNode* first = cur;
                ListNode* second = split(first, step);
                cur = split(second, step);
                prior = merge(first, second, prior);
            }
        }
        return dummy->next;
    }
ListNode* split(ListNode* head, int n){
        // 截取从head开始的n个节点,去到第n个时候next等于NULL,并且返回下一个stage开始的节点
        for(int i=0;i<(n-1) && head;i++)
            head = head->next;
        if(head == NULL)
            return NULL;
        ListNode* second = head->next;
        head->next = NULL; // 代表本段的结束
        return second;
    }
    ListNode* merge(ListNode* l1, ListNode* l2, ListNode* prior){
        // l1 第一stage的起点,l2第二stage的起点,cur 总的起点
        while(l1 && l2){
            if(l1->val < l2->val){
                prior->next = l1;
                l1 = l1->next;
                prior = prior->next;
            }else{
                prior->next = l2;
                l2 = l2->next;
                prior = prior->next;
            }
        }
        if(l1)
            prior->next = l1;
        if(l2)
            prior->next = l2;
        while (prior->next){
            prior = prior->next;
        }
        return prior;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值