题目
给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。
进阶:你可以在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序吗?
思路
题目要求时间空间复杂度分别为O(nlogn)和O(1),根据时间复杂度不难想到二分法,从而想到归并排序
通过递归实现链表归并排序,主要有两个过程:
- 分割过程:找到当前链表中点,断开,可以采用快慢指针法,奇数个节点找到中点,偶数个节点找到中心左边的节点
- 合并过程:将两个排序链表合并,转化成一个排序链表,使用双指针法合并,分别比较两个指针处节点值大小,按从小到大的顺序插入链表
java代码如下:
class Solution{//先分,再排序,最后合并
public ListNode sortList(ListNode head){
if (head == null || head.next == null)
return head;
//快慢指针找链表中点
ListNode slow = head;
ListNode fast = head.next;//这里fast要后移一位,因为如果是偶数位,我们找的是中心点的左边一个节点,因为这样方便判断边界,即slow即为上个区间的最后一个节点,如果不后移找到的就是中心点右边的节点,不方便划分区间,自己画个图走一遍就知道了
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
}
ListNode tmp = slow.next;//记录下个区间的起始边界位置
slow.next = null;//断开链表,分成两个区间链表
ListNode left = sortList(head);//排序左区间链表
ListNode right = sortList(tmp);//排序右区间链表
ListNode dummy = new ListNode(0);//创建虚拟头结点,用于最后返回结果
ListNode h = dummy;//移动指针,用于添加节点
//合并两个有序链表
while (left != null && right != null) {//只要两个链表都不为空
if (left.val < right.val) {//分别比较两个链表的节点值大小并加入
h.next = left;//将小的节点加入结果链表
left = left.next;//同时指针后移
} else {
h.next = right;
right = right.next;
}
h = h.next;//加入完节点后,结果链表中的指针后移,方便继续添加后续节点
}
h.next = (left != null ? left : right);//如果有链表没有加入完毕,则直接加入剩下非空的节点
return dummy.next;//返回结果链表
}
}