题目:排序链表
给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。
示例 1:
输入: head = [4,2,1,3]
输出: [1,2,3,4]
示例 2:
输入: head = [-1,5,3,4,0]
输出: [-1,0,3,4,5]
示例 3:
输入: head = []
输出: []
提示:
- 链表中节点的数目在范围 内
解题思路
归并的核心思想就是把一个大集合拆分为两个小集合,再分别对两个小集合进行拆分,直到不能拆分为止,然后对拆分后的小集合进行排序,排序完成后再向上合并,最终合并为一个有序的集合。
首先要先确立链表的长度,然后通过for循环对链表元素进行倍速遍历(每次将上一步的两部分合为一部分)
定义first和second指针指向每一部分的头结点,每次事先保存每一部分的后继结点再将其断链送入Merge进行排序
将排序好的链表用pre指针连接起来,再将剩下的remain部分链表挂到尾部进入下一轮翻倍循环
等到最后一轮for循环结束,如有剩余元素就挂在已排序链表尾部,如果恰好等分排好整条链表,就返回dummy.next即可。
迭代版的归并排序,实现递归中的归的过程。
i表示当前层中每两个需要归并的同序链表的一个链表长度;
j表示第一个链表的开头位置,并判断第二个链表的开头位置是否合法;
k表示第二个链表的开头位置。
代码实现
public ListNode sortList(ListNode head) {
return sortList(head, null);
}
private ListNode sortList(ListNode head, ListNode tail) {
//无法继续拆分的情况
if (head == null) {
return null;
}
//无法继续拆分的情况
if (head.next == tail) {
head.next = null;
return head;
}
//快慢指针找到中间节点
ListNode slow = head, fast = head;
while (fast != tail && fast.next != tail) {
slow = slow.next;
fast = fast.next.next;
}
ListNode mid = slow;
//左边继续拆分
ListNode left = sortList(head, mid);
//右边继续拆分
ListNode right = sortList(mid, tail);
//有序链表合并
return merge(left, right);
}
private ListNode merge(ListNode left, ListNode right) {
ListNode mergeNode = new ListNode();
ListNode help = mergeNode;
//比较两个链表当前的值,值小的链表就把引用赋给mergeNode,并向后移动一位重新赋值给自己,同时help指向值小的那个节点
while (left != null && right != null) {
if (left.val < right.val) {
help.next = left;
left = left.next;
} else {
help.next = right;
right = right.next;
}
help = help.next;
}
//最后如果有剩余的节点,就一次性链上去
help.next = left == null ? right : left;
return mergeNode.next;
}
运行结果
复杂度分析
- 空间复杂度:O(n)
- 时间复杂度:O(n)
在掘金(JUEJIN) 一起分享知识, Keep Learning!