LeetCode 148.排序链表(bottom-to-up归并排序)

题目要求:

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

示例 1:

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

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

来源:力扣(LeetCode)
 

看到这道题真的忘记了用什么方法,即使自己之前学过各种排序,但是时间久了也忘得差不多了。这里要求的是时间复杂度o(nlogn)、空间复杂度为0(1)。我们都知道快速排序、桶排序和归并排序的时间复杂度是o(nlogn),但是快速排序的时间以及空间都是大于等于题目要求的,桶排序可以完成但这里不做解释了,主要说一下由底而上的归并排序。还有一点, 递归的归并排序这里不合适,因为递归深度为logn不符合题目空间复杂度的要求。以下都是从网上找的资料以及一些自己的理解。

首先是各种排序的复杂度,这里直接用的up主花花酱的图

然后自底而上的归并排序的思想是这样的,我们先从一个一个元素归并排序为两个,由两个再到四个,循环logn次。

代码做了注释:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode sortList(ListNode head) {
        if(head==null||head.next==null) return head;
         ListNode dummy = new ListNode(0); //前指针用于pre指针
         int len = getListLen(head);       
         dummy.next = head;                //链接链表
         int itrv = 1;                      //长度
         while(itrv < len){
             ListNode pre = dummy;     
             ListNode h = dummy.next;      //h指针指向下一次循环的开头节点
             while(h!=null){
                int i = itrv;
                ListNode h1 = h;
                for(;h != null && i > 0;i--)//选择需要归并的长度 结尾是h指针 不包含
                    h = h.next;
                if(i>0)                     //选择h1的长度为空时 链表也是空的
                    break;
                ListNode h2 = h;            //h2的开头是h1的结尾
                i = itrv;               
                for(;h != null && i > 0;i--) //截取h2的长度 同h1
                    h = h.next;
                int c1 = itrv;                //计算h1 h2 的长度 
                itn c2 = itrv - i;            //h2的长度可能因为总长的奇偶而变             
                while(c1 > 0 && c2 > 0){      //排序
                    if(h1.val < h2.val){
                        pre.next = h1;
                        h1 = h1.next;
                        c1--;
                    }else{
                        pre.next = h2;
                        h2 = h2.next;
                        c2--;
                    }
                    pre = pre.next;
                }
                pre.next = c1 > 0 ? h1 : h2;
                while(c1 > 0 || c2 > 0) {  //是用来移动pre的位置
                    pre = pre.next;
                    c1--;
                    c2--;
                }
                pre.next = h;           //将排好序的链表和后续未排序的链表链接
            }
            itrv *= 2;
        }
        return dummy.next;
    }
    private int getListLen(ListNode head) {
        ListNode cur = head;
        int len = 0;
        while(cur != null) {
            len++;
            cur = cur.next;
        }
        return len;
    }
}

难点在于排序,也是跟着代码画图走了好几遍才完全明白的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值