148.排序链表
题目描述
在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序。
示例 1:
输入: 4->2->1->3
输出: 1->2->3->4
示例 2:
输入: -1->5->3->4->0
输出: -1->0->3->4->5
解法一:归并排序
1.递归法(空间复杂度O(n))
- 分割环节:用快慢指针找中间节点,分割为左右部分,重复操作
- 合并环节:逐个排序,当有一条链为空,将剩下一条加到末尾。返回结果
class Solution {
public ListNode sortList(ListNode head) {
return mergeSort(head);
}
//分割环节
public ListNode mergeSort(ListNode head){
//没有节点或只有1个节点,不用排序
if(head==null||head.next==null) return head;
//fast=head会出现栈溢出
ListNode slow = head,fast = head.next.next,l,r;
//快慢指针分割找中点
while(fast!=null&&fast.next!=null){
slow=slow.next;
fast=fast.next.next;
}
//分割右边
r = mergeSort(slow.next);
//断开中间连接
slow.next=null;
//分割左边
l = mergeSort(head);
return mergeList(head,l,r);
}
//合并环节
public ListNode mergeList(ListNode head,ListNode l,ListNode r){
ListNode p =new ListNode(0);
ListNode node =p;
while(l!=null&&r!=null){
if(l.val<r.val){
p.next=l;
l=l.next;
}
else{
p.next = r;
r = r.next;
}
p=p.next;
}
p.next=l==null?r:l;
return node.next;
}
}
2.Bottom-to-up
- 获取链表长度,创建哨兵结点
- 循环n次,根据步长分割链表并两两合并。
- 重复步骤2 logn次
class Solution {
public ListNode sortList(ListNode head) {
if(head==null||head.next==null) return head;
//哨兵
ListNode headnote = new ListNode(0);
headnote.next=head;
//链表长度
int len = countList(head);
//遍历logn次,从起点开始
for(int i=1;i<len;i<<=1){
//两条链,pre连接合并的结点,cur用于分割结点
ListNode pre = headnote;
ListNode cur = headnote.next;
//遍历n次
while(cur!=null){
//取cur链起点
ListNode left = cur;
//分割两个片段,并合并,放在起点后面
ListNode right = spilt(left,i);
cur = spilt(right,i);
pre.next = mergeList(left,right);
//更新pre链的终点
while(pre.next!=null) {
pre=pre.next;
}
}
}
return headnote.next;
}
//根据步长拆分结点
public ListNode spilt(ListNode head,int step){
if(head==null) return null;
for(int i=1;i<step&&head.next!=null;i++){
head=head.next;
}
//取还没分割链的起点
ListNode right=head.next;
//断开链表
head.next=null;
return right;
}
//计算链表长度
public int countList(ListNode head){
ListNode cur = head;
int count =0;
while(cur!=null){
count++;
cur=cur.next;
}
return count;
}
//合并链表
public ListNode mergeList(ListNode l,ListNode r){
ListNode p =new ListNode(0);
ListNode node =p;
while(l!=null&&r!=null){
if(l.val<r.val){
p.next=l;
l=l.next;
}
else{
p.next = r;
r = r.next;
}
p=p.next;
}
p.next=l==null?r:l;
return node.next;
}
}
解法二:快排(空间复杂度O(n))
- 需要创建一个头头节点,用于连接临时链表
- 将小于划分点的结点放到临时链表
- 原链表接在临时链表后面,头节点后面接临时链表
class Solution {
public ListNode sortList(ListNode head) {
if(head==null||head.next==null) return head;
ListNode headnode = new ListNode(0);
headnode.next = head;
return quickSort(headnode,null);
}
public ListNode quickSort(ListNode head,ListNode end){
//终止条件,只有1个数,不用排
if(head==end||head.next==end||head.next.next==end) return head;
//临时链表,将小于划分点值存入
ListNode tmpLink = new ListNode(0);
//partition为划分点,p为链表指针,tmp为临时链表指针
ListNode partition = head.next,p = partition,tmp = tmpLink;
//将小于划分点的结点放入临时链表
while (p.next!=end){
if (p.next.val<partition.val){
tmp.next=p.next;
tmp=tmp.next;
p.next=p.next.next;
}else {
p=p.next;
}
}
//原链表放在临时链表后面
//用tmp而不是tmpLink
tmp.next=partition;
//临时链表插回头结点
head.next=tmpLink.next;
quickSort(head,partition);
quickSort(partition,end);
return head.next;
}
}