版权声明:我的GitHub:https://github.com/617076674。真诚求星! https://blog.csdn.net/qq_41231926/article/details/86215067
我的LeetCode代码仓:https://github.com/617076674/LeetCode
原题链接:https://leetcode-cn.com/problems/sort-list/description/
题目描述:
知识点:归并排序、链表
思路:自顶向下的归并排序
本题和LeetCode147——对链表进行插入排序其实是同一个问题——对链表进行排序操作,但是本题多了时间复杂度O(nlogn)和空间复杂度O(1)这两个要求。将LeetCode147——对链表进行插入排序提交到本题,也是能获得通过的,只不过时间复杂度高了些。
实际上要满足O(nlogn)的时间复杂度的要求,常规来说有3种排序手段:
(1)归并排序
a:自顶向下的归并排序
b:自底向上的归并排序
(2)快速排序
a:单路快排
b:双路快排
c:三路快排
(3)堆排序
对于(3)堆排序而言,其空间复杂度显然是O(n)的,不满足题意。对于(2)快速排序而言,由于链表的特殊性,其寻找第k个节点的时间复杂度是O(k),会使得快速排序的时间复杂度不满足题意。对于(1)中的自底向上的归并排序,有着和快排同样的问题。因此,我们选择自顶向下的归并排序。
如何将链表分成两部分呢?
参考LeetCode141——环形链表中的思路二,用快慢双指针遍历链表,当快指针到达链表尾时,慢指针恰好处于链表中部。
此算法的时间复杂度是O(nlogn),这点没问题。但是空间复杂度真的是O(1)吗?由于实现时使用了递归,用到了系统栈,因此其空间复杂度实际上是O(logn),从这点上来说,此算法是不满足题意的。希望有小伙伴能实现更优秀的算法!
JAVA代码:
class Solution {
public ListNode sortList(ListNode head) {
if(head == null || head.next == null) {
return head;
}
ListNode pre = null;
ListNode cur1 = head;
ListNode cur2 = head;
while(null != cur2 && null != cur2.next){
pre = cur1;
cur1 = cur1.next;
cur2 = cur2.next.next;
}
ListNode node1 = sortList(pre.next);
pre.next = null;
ListNode node2 = sortList(head);
return mergeTwoLists(node1, node2);
}
private ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode cur1 = l1;
ListNode cur2 = l2;
ListNode dummyHead = new ListNode(-1);
ListNode cur = dummyHead;
while(cur1 != null || cur2 != null) {
if(cur1 == null) {
cur.next = cur2;
cur2 = cur2.next;
}else if(cur2 == null) {
cur.next = cur1;
cur1 = cur1.next;
}else if(cur1.val > cur2.val) {
cur.next = cur2;
cur2 = cur2.next;
}else {
cur.next = cur1;
cur1 = cur1.next;
}
cur = cur.next;
}
return dummyHead.next;
}
}
LeetCode解题报告: