屏幕又脏了
难度:中等
题目:
给你链表的头结点 head
,请将其按 升序 排列并返回 排序后的链表 。
进阶:
-
你可以在
O(n log n)
时间复杂度和常数级空间复杂度下,对链表进行排序吗?
示例 1:
输入:head = [4,2,1,3] 输出: [1,2,3,4]示例 2:
输入:head = [-1,5,3,4,0]
输出:[-1,0,3,4,5]
示例 3:
输入:head = []
输出:[]
提示:
-
链表中节点的数目在范围
[0, 5 * 104]
内
-
-105 <= Node.val <= 105
思考:
还记得前天写了一个链表插入排序的,咋一看跟这个好像是一样的,最大的差别只是那个部分有序,那可不可以也拿那份代码跑一边呢?
纸上得来终觉浅。
结果:
我裂开了!
居然要三位数,一般这种实例都不会超过10ms的。
其实主要是因为这个是直接插入排序(插入排序不会的自sha,,,我是说自行查找,这里不展开),原排序的越多就越省时间,例如前天的【链表插入】就是多数有序,固然很快。
但是如果用在这种又长又全无序的,很可能直接就是插入排序的最坏情况,导致时间复杂度就是O(n²)。
换个做法:
其实我们应该想到归并排序,这是稳定的排序,无论乱不乱序,长度多少,时间复杂度都是O(nlogn);而且适合大数据量排序。
顺带唠嗑一下归并,核心思想:分而治之。利用递归,先不停将数组分成两半,分到剩下一个再把两半按大小顺序合起来,形成一个新的数组。所以要一个跟原数组大小相同的辅助空间,故空间复杂度O(n)(数组情况下)。
大概或许几乎或者差不多也许可能应该是这样子:
思路:
那么这个题目的链表操作当然也使用这种排序法,只不过要代码稍微比数组排序多一丢丢,而已。
主要是先要找到中间指针的位置,通过设置快指针和慢指针两个指针,当慢的走一步,快的走两步,当快的到达终点时,慢的就在中点。
然后就是合并链表了,这个没啥好说的。
public ListNode sortList(ListNode head) { if(head==null || head.next==null) return head; ListNode slow = head; //慢指针 ListNode fast = head.next; //快指针 while(fast!=null && fast.next!=null){ //快慢指针找到链表中点 slow = slow.next; //慢指针走一步 fast = fast.next.next; //快指针走两步 } ListNode rightHead = slow.next; //链表第二部分的头节点 slow.next = null; //cut 链表 ListNode left = sortList(head); //递归排序前一段链表 ListNode right = sortList(rightHead); //递归排序后一段链表 return merge(left,right);} public ListNode merge(ListNode h1,ListNode h2){ //合并两个有序链表 ListNode dummy = new ListNode(-1); ListNode p = dummy; while(h1!=null && h2!=null){ if(h1.val < h2.val){ p.next = h1; h1 = h1.next; }else{ p.next = h2; h2 = h2.next; } p = p.next; } if(h1!=null) p.next = h1; else if(h2!=null) p.next = h2; return dummy.next;}
这个主要是归并排序的思想,会的就觉挺简单的,
有空整理下8大排序
--------------------------------------完------------------------------------
你站的方向吹过来的风是烫的。