148. 排序链表

给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。

进阶:

  • 你可以在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序吗?

 

示例 1:

输入:head = [4,2,1,3]
输出:[1,2,3,4]

示例 2:

输入:head = [-1,5,3,4,0]
输出:[-1,0,3,4,5]

示例 3:

输入:head = []
输出:[]

 

提示:

  • 链表中节点的数目在范围 [0, 5 * 104] 内
  • -105 <= Node.val <= 105

思路

bottom-to-up 的归并思路是这样的:先两个两个的 merge,完成一趟后,再 4 个4个的 merge,直到结束。举个简单的例子:[4,3,1,7,8,9,2,11,5,6].

step=1: (3->4)->(1->7)->(8->9)->(2->11)->(5->6)
step=2: (1->3->4->7)->(2->8->9->11)->(5->6)
step=4: (1->2->3->4->7->8->9->11)->(5->6)
step=8: (1->2->3->4->5->6->7->8->9->11)

链表里操作最难掌握的应该就是各种断链啊,然后再挂接啊。在这里,我们主要用到链表操作的两个技术:

  • merge(l1, l2),双路归并,我相信这个操作大家已经非常熟练的,就不做介绍了。
  • cut(l, n),可能有些同学没有听说过,它其实就是一种 split 操作,即断链操作。不过我感觉使用 cut 更准确一些,它表示,将链表 l 切掉前 n 个节点,并返回后半部分的链表头。
  • 额外再补充一个 dummyHead
  • //时间复杂度O(nlogn),空间复杂度O(1)

   代码实现

 public ListNode sortList(ListNode head) {

        //加一个头结点,处理边界节点时,逻辑相同
        ListNode dummyHead = new ListNode(Integer.MAX_VALUE);
        dummyHead.next = head;

        //计算长度
        int length = 0;
        ListNode work = head;
        while (work != null) {
            work = work.next;
            length++;
        }

        ListNode tail = dummyHead;
        //每次步长*2
        for (int step = 1; step < length; step <<= 1) {
            work = dummyHead.next;
            tail = dummyHead;//记录每一趟归并的结果,下趟归并时重置tail
            while (work != null) {
                /*
                 * 第一句left->@->@->@->@->@->@->null
                 * 第二句left->@->@->null   right->@->@->@->@->null
                 * 第三句left->@->@->null   right->@->@->null   work->@->@->null
                 */
                ListNode left = work;
                ListNode right = cut(left, step);//将链表拆成两部分,左边为step长链表,右边为剩余链表
                work = cut(right, step);//这步执行完毕,left为step链表,right为step链表,work为剩余链表,下一趟排序的基础链表

                //总是把tail移到当前链表的最后一个位置,用于拼接下一趟循环产生的结果
                tail.next = merge(left, right);
                while (tail.next != null) {
                    tail = tail.next;
                }
            }
        }
        return dummyHead.next;
    }

    //作用:在step位置断链,并返回后面部分的链头
    public ListNode cut(ListNode head, int step) {
        while (--step != 0 && head != null) {
            head = head.next;
        }
        //如果链表不够step长,就返回空
        if (head != null) {
            ListNode result = head.next;
            head.next = null;
            return result;
        }else {
            return null;
        }
    }

    //归并两条链表
    public ListNode merge(ListNode l1, ListNode l2) {
        ListNode work = new ListNode(0);
        ListNode head = work;
        while (l1 != null && l2 != null) {
            if (l1.val < l2.val) {
                work.next = l1;
                l1 = l1.next;
            } else {
                work.next = l2;
                l2 = l2.next;
            }
            work = work.next;
        }
        if (l1 != null) {
            work.next = l1;
        }
        if (l2 != null) {
            work.next = l2;
        }
        return head.next;
    }

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值