1. 解题思路
这题想到要排序,是链表。用快慢指针,还需要扩展一下,归并排序。
1、拆分链表
- 用快慢指针(
slow
,fast
)找到链表的中点。fast
走两步,slow
走一步。- 当
fast
到尾时,slow
正好在中间。
- 把链表从中点断开,就分成了两半:
head → slow
,slow.next → end
。
2、递归排序左右两半
- 对左半部分递归调用
Sort
,直到只剩下一个节点(自然是有序的)。 - 对右半部分递归调用
Sort
。
3、合并两个有序链表
- 有序的左半链表
leftNode
和右半链表rightNode
,通过双指针逐个比较,合并成一个有序链表。 - 使用哨兵节点
prev
简化操作(避免频繁处理头节点为空的情况)。
但是要注意,这里的归并排序和 给数组归并排序有一丢丢的不同,因为这个链表不支持随机访问
2. 代码
/**
* 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;
}
return Sort(head);
}
/**
* 返回拆分后的链表头节点,方便归并排序
*
* @param head
* @return
*/
public ListNode Sort(ListNode head) {
if (head == null || head.next == null) {
return head;
}
ListNode slow = head;
ListNode fast = head;
while (fast.next != null && fast.next.next != null) {
slow = slow.next;
fast = fast.next.next;
}
ListNode tempNext = slow.next;
slow.next = null;
//找到两个新的起点,二路归并
ListNode leftNode = Sort(head);
ListNode rightNode = Sort(tempNext);
return merge(leftNode, rightNode);
}
public ListNode merge(ListNode leftNode, ListNode rightNode) {
ListNode prev = new ListNode(-1);//哨兵节点
ListNode tmp = prev;
while (leftNode != null && rightNode != null) {
if (leftNode.val <= rightNode.val) {
prev.next = leftNode;
leftNode = leftNode.next;
} else {
prev.next = rightNode;
rightNode = rightNode.next;
}
prev = prev.next;
}
prev.next = (leftNode == null) ? rightNode : leftNode;
return tmp.next;
}
}