题目链接:https://leetcode.com/problems/sort-list/
最先想到的是用数组把节点存下来再用快排,十分简单粗暴:
public ListNode sortList(ListNode head) {
ListNode node = head;
int cnt = 0;
while (node != null) {
cnt++;
node = node.next;
}
int[] nums = new int[cnt];
node = head;
cnt = 0;
while (node != null) {
nums[cnt++] = node.val;
node = node.next;
}
Arrays.sort(nums);
node = head;
cnt = 0;
while (node != null) {
node.val = nums[cnt++];
node = node.next;
}
return head;
}
但问题要求在O(nlogn)的时间复杂度和O(1)空间复杂度下完成排序,这个首先想到的是快排 、归并、堆排序这类O(nlogn)的时间复杂度的算法,又要求在O(1)空间复杂度下完成这个操作,那堆排序肯定是不行的。快排的递归中的合并两个子数组时是基于两个子数组与哨兵进行比较来实现的,严格依赖数组下标,用在链表上可能十分吃力。只能归并试试。
代码与数组的归并排序类似,归并排序可以参考这一篇详细理解https://blog.csdn.net/To_be_to_thought/article/details/83988767,都是先对整个链表进行切分直到子链表大小为1,然后对相邻两有序子链表进行合并,对了合并两个有序链表为一个有序链表的题目我们原来做过,请参见https://blog.csdn.net/To_be_to_thought/article/details/85057542。
关键是如何切分呢,这里采用快慢指针法将整个链表且分为两个长度基本相等的子链表,切分代码如下:
public ListNode sortList(ListNode head) {
if(head==null || head.next==null)
return head;
//子链表长度为1就return
ListNode pFast=head,pSlow=head;
while(pFast.next!=null && pFast.next.next!=null)
{
pSlow=pSlow.next;
pFast=pFast.next.next;
}
ListNode mid=pSlow.next;
pSlow.next=null;
ListNode half1=sortList(head);
ListNode half2=sortList(mid);
//相邻两个有序子链表进行合并
ListNode sorted=mergeTwoLists(half1,half2);
return sorted;
}
快慢指针的用法可以参考这一篇:https://blog.csdn.net/To_be_to_thought/article/details/83958314
整个代码性能还不错,如下:
class Solution {
public ListNode sortList(ListNode head) {
if(head==null || head.next==null)
return head;
ListNode pFast=head,pSlow=head;
while(pFast.next!=null && pFast.next.next!=null)
{
pSlow=pSlow.next;
pFast=pFast.next.next;
}
ListNode mid=pSlow.next;
pSlow.next=null;
ListNode half1=sortList(head);
ListNode half2=sortList(mid);
ListNode sorted=mergeTwoLists(half1,half2);
return sorted;
}
public static ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if(l1==null)
return l2;
if(l2==null)
return l1;
ListNode head=null;
if(l1.val<l2.val)
{
head=l1;
l1=l1.next;
}
else
{
head=l2;
l2=l2.next;
}
ListNode p=head;
while(l1!=null && l2!=null)
{
if(l1.val<l2.val)
{
p.next=l1;
p=l1;
l1=l1.next;
}
else
{
p.next=l2;
p=l2;
l2=l2.next;
}
}
if(l1==null)
p.next=l2;
if(l2==null)
p.next=l1;
return head;
}
}
严格来说,递归的空间复杂度为O(logn),但在LeetCode里不把隐式递归的空间开销算进来,这是在鼓励用递归这种简洁隽永的方式来解题,但在实际开发中还是要避免使用。