148.排序链表
148.排序链表
题目
给你链表的头结点 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
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/sort-list
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
题解-递归法
看见排序,想到昨天做的K个链表排序,时间复杂度O(n log n),第一个想到的是归并排序。
- 对数组做归并排序需要的空间复杂度为O(n)-->新开辟数组O(n)+递归调用函数O(logn);
- 对链表做归并排序可以通过修改引用来更改节点位置,因此不需要向数组一样开辟额外的O(n)空间,但是只要是递归就需要消耗log(n)的空间复杂度,要达到O(1)空间复杂度的目标,得使用迭代法。
因为之前做了k个链表的排序,使用了递归法,这里就先使用递归法解
问题1:数组的中间点很好找,链表的中间节点怎么找?使用快慢指针
注意:这里需要对链表进行断链,不断链的话,归并时候分左右两边是没有用的!因为左边排序的时候,还是通过左边最后一个节点的next到了右边节点。
public ListNode sortList(ListNode head) {
//递归终止条件
if(head==null||head.next ==null) return head;
//本层递归需要做的事情,断链并合并
ListNode dummy = new ListNode(0);
dummy.next = head;
ListNode slow = dummy;
ListNode fast = dummy;
//或者这样定义快慢指针
//ListNode slow = head;
//ListNode fast = head.next;
while(fast != null && fast.next != null){
slow = slow.next;
fast = fast.next.next;
}
//slow指向的就是中间节点,开始断链,左右两边分别开始排序
fast = slow.next; //记录右边链表的开头
slow.next = null; //断链
ListNode l1 = sortList(head);
ListNode l2 = sortList(fast);
return merge2Lists(l1,l2);//返回合并好的头节点
}
ListNode merge2Lists(ListNode l1,ListNode l2){
ListNode dummy = new ListNode();
ListNode tail = dummy;
ListNode tmp;
while(l1 != null && l2 != null){
if(l1.val <= l2.val){
tmp = l1;
l1 = l1.next;
} else{
tmp = l2;
l2 = l2.next;
}
tail.next = tmp;
tail = tail.next;
}
tail.next = l1==null?l2:l1;
return dummy.next;
}
题解-迭代法
从头开始,先2个节点2个节点分别排序,然后再4个节点,4个节点分别排序...
排序时需要先断链再排序,上面已经描述了。
通过头节点传入需要切的链表,切掉之后就断链了,所以我们需要记录切掉之后的链表用于下次切割。
每次切割的长度是在变化的,这里作为参数传入,返回切割之后剩下的表头
ListNode cut(ListNode head,int size){
while(--size!=0 && head!=null){
head = head.next;
}
if(head == null) return null;
ListNode next = head.next;
head.next = null;
return next
}
合并两个有序链表
ListNode merge2Lists(ListNode l1,ListNode l2){
ListNode dummy = new ListNode();
ListNode tail = dummy;
ListNode tmp;
while(l1 != null && l2 != null){
if(l1.val <= l2.val){
tmp = l1;
l1 = l1.next;
} else{
tmp = l2;
l2 = l2.next;
}
tail.next = tmp;
tail = tail.next;
}
tail.next = l1==null?l2:l1;
return dummy.next;
}
从头开始,先2个节点2个节点分别排序,然后再4个节点,4个节点分别排序...
ListNode dummy = new ListNode(0);
dummy.next = head;
int len = 0;
while(head!=null){
head=head.next;
len++;
}
for(int i=1;i<len;i*=2){
ListNode l1,l2;
ListNode tail = dummy; //切割之后会断链,tail指向结尾把断链的连成一个链表
ListNode cur = dummy.next; //每一轮切割的节点数增加都要从头开始
while(cur!=null){
l1 = cut(cur,i);
l2 = cut(l1,i);
tail.next = merge2Lists(cur,l1); //重新连成一个链表
cur=l2; //下一次循环的切割
while(tail.next !=null){
tail =tail.next;//保持在最尾部
}
}
}
return dummy.next;
代码
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode sortList(ListNode head) {
if(head==null||head.next ==null) return head;
ListNode dummy = new ListNode(0);
dummy.next = head;
int len = 0;
while(head!=null){
head=head.next;
len++;
}
for(int i=1;i<len;i*=2){
ListNode l1,l2;
ListNode tail = dummy;
ListNode cur = dummy.next;
while(cur!=null){
l1 = cut(cur,i);
l2 = cut(l1,i);
tail.next = merge2Lists(cur,l1);
cur=l2;
while(tail.next !=null){
tail =tail.next;
}
}
}
return dummy.next;
}
ListNode cut(ListNode head,int size){
while(--size!=0 && head!=null){
head = head.next;
}
if(head == null) return null;
ListNode next = head.next;
head.next = null;
return next;
}
ListNode merge2Lists(ListNode l1,ListNode l2){
ListNode dummy = new ListNode();
ListNode tail = dummy;
ListNode tmp;
while(l1 != null && l2 != null){
if(l1.val <= l2.val){
tmp = l1;
l1 = l1.next;
} else{
tmp = l2;
l2 = l2.next;
}
tail.next = tmp;
tail = tail.next;
}
tail.next = l1==null?l2:l1;
return dummy.next;
}
}
浙公网安备 33010602011771号