【刷题笔记10.5】LeetCode:排序链表

LeetCode:排序链表

一、题目描述

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


二、分析

这题咱们默认要求:空间复杂度为O(1) 。所以这把咱们用自底向上的方法实现归并排序,则可以达到O(1) 的空间复杂度。

具体算法如下:

1、首先,判断如果所给的 head 为 null 则返回null

2、求出所给链表head的长度length,然后将链表拆分成子链表进行合并。具体算法如下:

  • 2.1、用subLen表示每次需要排序的子链表的长度,初始值subLen为1.
  • 2.2、每次将链表拆分成若干个长度为subLen的子链表 (最后一个子链表的长度可以小于subLen),按照每两个子链表一组进行合并 (通过使用合并两个有序链表 的做法),合并后即可得到若干个长度为 subLen2 的有序子链表(最后一个子链表的长度可以小于 subLen2)。合并两个子链表仍然使用合并两个有序链表的做法。
  • 2.3、将subLen的值加倍(通过位运算左移1位的方式),重复第2步,对更长的有序子链表进行合并操作,直到有序子链表的长度大于或等于length,整个链表排序完毕。

如何保证每次合并后得到的子链表都是有序的呢?可以通过数学归纳法证明。

  • 1、初始时subLen为1,每个长度为1的子链表都是有序的

  • 2、如果每个长度为subLen的子链表已经有序,那么合并两个长度为subLen的子链表后,得到长度为subLen * 2

    的子链表,一定也是有序的。

  • 3、当最后一个子链表的长度小于subLen时,该子链表也是有序的,合并两个链表之后得到的子链表一定也是有序的。

三、上代码

java 复制代码
public class Deal11 {
    public ListNode sortList(ListNode head) {
        if (head == null) {
            return null;
        }

        //1、从头向后遍历链表,统计链表长度
        int length = 0;
        ListNode p = head;
        while(p != null) {
            length++;
            p=p.next;
        }

        //2、设定result用于记录最终返回结果,并对其进行最终的初始化
        ListNode result = new ListNode(-1);
        result.next = head;

        //3、将链表拆分成若干个长度为subLen的子链表,并按照没两个子链表一组进行合并
        for (int subLen = 1; subLen < length; subLen <<= 1) {//将subLen的值加倍(通过位运算左移1位的方式)
            ListNode pre = result;
            ListNode cur = result.next;   //用于记录拆分链表的位置

            while (cur != null) { //如果链表没有被拆完
                //3.1 拆分出链表1,其长度为subLen
                ListNode head_1 = cur;    //第一个链表的头,即cur
                for (int i = 1; i < subLen && cur != null && cur.next != null; i++) {
                    cur = cur.next;
                }

                //3.2 拆分出链表2,其长度也为subLen
                ListNode head_2 = cur.next; //第二个链表的头,即第一个链表尾部的下一个位置
                cur.next = null; //断开第一个链表和第二个链表的连接
                cur = head_2;    //第二个链表的头重新赋给cur
                for (int i = 1; i < subLen && cur != null && cur.next != null; i++) {
                    cur = cur.next;
                }

                //3.3 再次断开第二个链表的的连接
                ListNode next = null;
                if (cur != null) {
                    next = cur.next;  //用于记录拆分完两个链表后结束的后序位置
                    cur.next = null;
                }

                //3.4 合并两个有序链表head_1 和 head_2
                ListNode merge = mergeTwo(head_1, head_2);
                pre.next = merge;
                while (pre.next != null) {
                    pre = pre.next;
                }
                cur = next;
            }
        }
        return result.next;
    }

    //合并两个有序链表
    public ListNode mergeTwo(ListNode head1, ListNode head2) {
        ListNode result = new ListNode(-1);
        ListNode p = result;

        ListNode p1 = head1;
        ListNode p2 = head2;
        while(p1 != null && p2 != null) {
            if (p1.val > p2.val) {
                p.next = p2;
                p2 = p2.next;
            } else {
                p.next = p1;
                p1 = p1.next;
            }
            p = p.next;
        }
        if (p1 == null) {
            p.next = p2;
        }
        if (p2 == null) {
            p.next = p1;
        }
        return result.next;
    }
}
相关推荐
YuTaoShao1 小时前
【LeetCode 每日一题】1653. 使字符串平衡的最少删除次数——(解法三)DP 空间优化
算法·leetcode·职场和发展
r i c k1 小时前
数据库系统学习笔记
数据库·笔记·学习
TracyCoder1232 小时前
LeetCode Hot100(26/100)——24. 两两交换链表中的节点
leetcode·链表
季明洵2 小时前
C语言实现单链表
c语言·开发语言·数据结构·算法·链表
shandianchengzi2 小时前
【小白向】错位排列|图文解释公考常见题目错位排列的递推式Dn=(n-1)(Dn-2+Dn-1)推导方式
笔记·算法·公考·递推·排列·考公
only-qi2 小时前
leetcode19. 删除链表的倒数第N个节点
数据结构·链表
浅念-2 小时前
C语言编译与链接全流程:从源码到可执行程序的幕后之旅
c语言·开发语言·数据结构·经验分享·笔记·学习·算法
The森3 小时前
Linux IO 模型纵深解析 01:从 Unix 传统到 Linux 内核的 IO 第一性原理
linux·服务器·c语言·经验分享·笔记·unix
tq10863 小时前
Skills 的问题与解决方案
笔记
三水不滴3 小时前
有 HTTP 了为什么还要有 RPC?
经验分享·笔记·网络协议·计算机网络·http·rpc