题目
Given the head of a linked list, return the list after sorting it in ascending order.
Follow up: Can you sort the linked list in O(n logn) time and O(1) memory (i.e. constant space)?
Example 1:
Input: head = [4,2,1,3]
Output: [1,2,3,4]
Example 2:
Input: head = [-1,5,3,4,0]
Output: [-1,0,3,4,5]
Example 3:
Input: head = []
Output: []
Constraints:
The number of nodes in the list is in the range [0, 5 * 104].
-105 <= Node.val <= 105
思路
题目提示要
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn),很自然的想到用merge sort。
因为题给的数据结构是链表,不像数组可以直接除2就找到中间的位置分开,所以第一步要做的是找到分割点,这里使用的是快慢指针,一个走到底的时候,另外一个走到一半,我使用的停止条件是不满足fast.next != null && fast.next.next != null
,举两个例子:
链表长度为5(奇数):
快指针:1 -> 3 -> 5
慢指针:1 -> 2 -> 3
期望分成:3+2个节点(slow.next为第二个链表起点)或2+3个节点(slow为第二个链表起点)都可以
链表长度为6(偶数):
快指针:1 -> 3 -> 5
慢指针:1 -> 2 -> 3
期望分成:3+3个节点(slow.next为第二个链表起点)
所以使用slow.next为第二个链表的起点可以统一以上两种情况。
回到上面为什么停止条件要放fast.next != null && fast.next.next != null
,首先我们知道代码更新时有fast = fast.next.next
,所以停止条件一定不是不满足fast != null
(最后一点可能产生空指针),那为什么不选择fast.next != null
,再次考虑上面两个例子。
链表长度为5(奇数):
快指针:1 -> 3 -> 5
慢指针:1 -> 2 -> 3
期望分成:3+2个节点(slow.next为第二个链表起点)或2+3个节点(slow为第二个链表起点)都可以
链表长度为6(偶数):
快指针:1 -> 3 -> 5 -> null
慢指针:1 -> 2 -> 3 -> 4
期望分成:3+3个节点(slow为第二个链表起点)
这时发现使用slow为第二个链表的起点可以统一以上两种情况。但是进一步考虑,进入递归的时候我们需要把第一个链表的后半截去掉,在这种情况下时slow.prev.next = null
,但是ListNode是不支持prev操作的,只能在遍历快慢指针的时候加上一个prev在找到这个点;但是如果使用前面一种停止条件,直接记录下slow.next再使得slow.next = null
即可,两种都是可行的只是前者更加方便。
分开后递归解决两个子问题返回两个sorted list的头,base case为只有一个节点时直接返回。比较两者头的大小决定新的头curr,接下来的过程和merge sort中merge的过程是一致的,不断比较两个链表头元素的大小来选择下一个添加的元素,如果一个链表先被使用完则直接接上另一个链表返回结果。
整个过程中只是修改ListNode中的指针而没有用到new ListNode()的操作,因而有 O ( 1 ) O(1) O(1)的内存使用。
代码
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode sortList(ListNode head) {
if (head == null || head.next == null) return head;
ListNode fast = head, slow = head;
while (fast.next != null && fast.next.next != null) {
fast = fast.next.next;
slow = slow.next;
}
ListNode head2 = slow.next;
slow.next = null;
ListNode l1 = sortList(head);
ListNode l2 = sortList(head2);
ListNode curr;
if (l1.val < l2.val) {
curr = l1;
l1 = l1.next;
} else {
curr = l2;
l2 = l2.next;
}
ListNode res = curr;
while (l1 != null && l2 != null) {
if (l1.val < l2.val) {
curr.next = l1;
l1 = l1.next;
} else {
curr.next = l2;
l2 = l2.next;
}
curr = curr.next;
}
if (l1 == null) {
curr.next = l2;
} else {
curr.next = l1;
}
return res;
}
}