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

LeetCode:排序链表

一、题目描述

给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表
【刷题笔记10.5】LeetCode:排序链表_第1张图片
【刷题笔记10.5】LeetCode:排序链表_第2张图片
【刷题笔记10.5】LeetCode:排序链表_第3张图片

二、分析

这题咱们默认要求:空间复杂度为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时,该子链表也是有序的,合并两个链表之后得到的子链表一定也是有序的。

三、上代码

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;
    }
}

你可能感兴趣的:(笔记,leetcode,链表)