LeetCode.148 排序链表
方法一:自顶向下的归并排序(递归法)
1. 分割:找到当前链表中点,并从中点将链表断开(以便下次递归时,链表片段拥有正确边界);
- 首先对链表进行分割,使用
fast,slow
快慢双指针法,奇数个节点找到中点,偶数个节点找到中心左边的节点,对链表进行断开。 - 找到重点slow后,执行slow.next = null 将链表切断
- 递归分割时,输入当前链表左端点head和中心节点slow.next(因为链表是从slow切断的)
- 终止条件:head.next = null时,说明只有一个节点了,直接返回此节点。
2.合并(merge):将两个排序链表合并,转化为一个排序链表。
该方法的时间复杂度为O(nlogn),空间复杂度因为使用了递归的方法所以应该为O(n)
1 /** 2 * Definition for singly-linked list. 3 * public class ListNode { 4 * int val; 5 * ListNode next; 6 * ListNode() {} 7 * ListNode(int val) { this.val = val; } 8 * ListNode(int val, ListNode next) { this.val = val; this.next = next; } 9 * } 10 */ 11 class Solution { 12 public ListNode sortList(ListNode head) { 13 if(head == null || head.next == null) return head; 14 ListNode slow = head, fast = head.next; 15 while(fast != null && fast.next != null){ 16 slow = slow.next; 17 fast = fast.next.next; 18 } 19 ListNode temp = slow.next; 20 slow.next = null; 21 ListNode left = sortList(head); 22 ListNode right = sortList(temp); 23 ListNode h = new ListNode(); 24 ListNode res = h; 25 while(left != null && right != null){ 26 if(left.val <= right.val){ 27 res.next = left; 28 left = left.next; 29 }else{ 30 res.next = right; 31 right = right.next; 32 } 33 res = res.next; 34 } 35 res.next = left == null ? right : left; 36 return h.next; 37 } 38 }
方法二:自底向上归并排序
使用自底向上的方法实现归并排序,则可以达到 O(1)O(1) 的空间复杂度。
首先求得链表的长度 \textit{length}length,然后将链表拆分成子链表进行合并。
具体做法如下。
用 subLength 表示每次需要排序的子链表的长度,初始时 subLength=1。
每次将链表拆分成若干个长度为 subLength 的子链表(最后一个子链表的长度可以小于subLength),按照每两个子链表一组进行合并,合并后即可得到若干个长度为 subLength×2 的有序子链表(最后一个子链表的长度可以小于 subLength×2)。合并两个子链表仍然使用「21. 合并两个有序链表」的做法。
将 subLength 的值加倍,重复第 2 步,对更长的有序子链表进行合并操作,直到有序子链表的长度大于或等于 length,整个链表排序完毕。
如何保证每次合并之后得到的子链表都是有序的呢?可以通过数学归纳法证明。
初始时 subLength=1,每个长度为 11 的子链表都是有序的。
如果每个长度为 subLength 的子链表已经有序,合并两个长度为 subLength 的有序子链表,得到长度为 subLength×2 的子链表,一定也是有序的。
当最后一个子链表的长度小于 subLength 时,该子链表也是有序的,合并两个有序子链表之后得到的子链表一定也是有序的。
因此可以保证最后得到的链表是有序的。
1 class Solution { 2 public ListNode sortList(ListNode head) { 3 if (head == null) { 4 return head; 5 } 6 int length = 0; 7 ListNode node = head; 8 while (node != null) { 9 length++; 10 node = node.next; 11 } 12 ListNode dummyHead = new ListNode(0, head); 13 for (int subLength = 1; subLength < length; subLength <<= 1) { 14 ListNode prev = dummyHead, curr = dummyHead.next; 15 while (curr != null) { 16 ListNode head1 = curr; 17 for (int i = 1; i < subLength && curr.next != null; i++) { 18 curr = curr.next; 19 } 20 ListNode head2 = curr.next; 21 curr.next = null; 22 curr = head2; 23 for (int i = 1; i < subLength && curr != null && curr.next != null; i++) { 24 curr = curr.next; 25 } 26 ListNode next = null; 27 if (curr != null) { 28 next = curr.next; 29 curr.next = null; 30 } 31 ListNode merged = merge(head1, head2); 32 prev.next = merged; 33 while (prev.next != null) { 34 prev = prev.next; 35 } 36 curr = next; 37 } 38 } 39 return dummyHead.next; 40 } 41 42 public ListNode merge(ListNode head1, ListNode head2) { 43 ListNode dummyHead = new ListNode(0); 44 ListNode temp = dummyHead, temp1 = head1, temp2 = head2; 45 while (temp1 != null && temp2 != null) { 46 if (temp1.val <= temp2.val) { 47 temp.next = temp1; 48 temp1 = temp1.next; 49 } else { 50 temp.next = temp2; 51 temp2 = temp2.next; 52 } 53 temp = temp.next; 54 } 55 if (temp1 != null) { 56 temp.next = temp1; 57 } else if (temp2 != null) { 58 temp.next = temp2; 59 } 60 return dummyHead.next; 61 } 62 }