方法一:归并排序递归实现,空间复杂度O(logN)
思路:
- 自上而下从中间切分链表,当两个链表都为一个节点时,直接merge,然后上层调用。
- 这里找中点用的是
fast.next != null && fast.next.next != null,偶数链表slow会停在中轴前一个节点,方便findMid(head).next = null截取
//递归版,空间复杂度O(logN)
class Solution {
public ListNode sortList(ListNode head) {
//只剩一个node返回,head==null为特判
if (head == null || head.next == null) {
return head;
}
ListNode mid = findMid(head).next;//后半截
findMid(head).next = null;//截取前半截
ListNode left = sortList(head);//排好序的前半截
ListNode right = sortList(mid);//排好序的后半截
return merge(left, right);
}
//合并两个有序链表,返回新链表的头节点
public ListNode merge(ListNode node1, ListNode node2) {
ListNode dummy = new ListNode(0);
ListNode cur = dummy;
//挨个比较两根链表的节点,大的先接到后面
while (node1 != null && node2 != null) {
if (node1.val <= node2.val) {
cur.next = node1;
cur = cur.next;
node1 = node1.next;
} else {
cur.next = node2;
cur = cur.next;
node2 = node2.next;
}
}
//如果两根链表不一样长,直接把剩下的接上
if (node1 != null) {
cur.next = node1;
}
//如果两根链表不一样长,直接把剩下的接上
if (node2 != null) {
cur.next = node2;
}
return dummy.next;
}
//快慢指针找链表的中间节点,1->2->3->4返回2之后,1->2->3->4->5返回3之后
public ListNode findMid(ListNode node) {
ListNode fast = node;
ListNode slow = node;
while (fast.next != null && fast.next.next != null) {
fast = fast.next.next;
slow = slow.next;
}
return slow;
}
}
方法二:归并排序迭代实现,空间复杂度O(1)
思路:
- 用1,2,4,8,...为stride扫链表,直到stride超出链表长度。这样可以做到两两合并。
- 在每个固定的stride下,用cur把链表扫一遍,把相邻stride的两个链表头merge。
- 细节处理:当链表长度无法被strtide整除,那么最后一组的长度肯定小于stride,getStrideEnd()中注意实现。
//迭代版,空间复杂度O(1)
class Solution {
public ListNode sortList(ListNode head) {
ListNode dummy = new ListNode();
dummy.next = head;
int length = getLength(head);
//以不同的stride扫链表。由于是两两归并,stride应以2的指数扩大
for (int stride = 1; stride <= length; stride *= 2) {
ListNode pre = dummy;
ListNode cur = dummy.next;
//以当前stride大小扫一遍链表
while (cur != null) {
ListNode end1 = getStrideEnd(cur, stride);
ListNode end2 = getStrideEnd(end1.next, stride);//end1.next可能为null,导致end2为null
ListNode head1 = cur;//第一段的起始节点
ListNode head2 = end1.next;//第二段的起始节点
end1.next = null;//截取第一段,长度为stride
//防止空指针:end2可能为null,当stride=1时。
if (end2 != null) {
cur = end2.next ;
end2.next = null;//截取第二段,长度为stride
} else {
cur = null;
}
//把前面排好序的和刚刚merge的两段接起来,pre是前面排好序的最后一个节点
pre.next = merge(head1, head2);
//pre移到merge后的链表的最后一个节点
while (pre.next != null) {
pre = pre.next;
}
}
}
return dummy.next;
}
//获取每个stride中的最后一个节点
public ListNode getStrideEnd(ListNode node, int stride) {
if (node == null) {
return null;
}
int i = 1;//注意这里是1,当上面的循环中stride为1,表明是自己merge自己。
ListNode cur = node;
while (i < stride && cur.next != null) {
cur = cur.next;
i++;
}
return cur;
}
//求链表长度
public int getLength(ListNode head) {
int length = 0;
ListNode cur = head;
while (cur != null) {
cur = cur.next;
length++;
}
return length;
}
//合并两个有序链表,返回新链表的头节点
public ListNode merge(ListNode node1, ListNode node2) {
ListNode dummy = new ListNode(0);
ListNode cur = dummy;
//挨个比较两根链表的节点,大的先接到后面
while (node1 != null && node2 != null) {
if (node1.val <= node2.val) {
cur.next = node1;
cur = cur.next;
node1 = node1.next;
} else {
cur.next = node2;
cur = cur.next;
node2 = node2.next;
}
}
//如果两根链表不一样长,直接把剩下的接上
if (node1 != null) {
cur.next = node1;
}
//如果两根链表不一样长,直接把剩下的接上
if (node2 != null) {
cur.next = node2;
}
return dummy.next;
}
}