在 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.归并排序,一般都是使用递归版本,递归版本不能保证常数级的时间复杂度
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode sortList(ListNode head) {
//归并排序
if(head == null||head.next == null)//head.next == null别忘记了
return head;
ListNode fast = head.next;
ListNode slow = head;
int len = 0;
while(fast != null&&fast.next != null){
slow = slow.next;
fast = fast.next.next;
len++;
}
fast = slow.next;
slow.next = null;//把前后两段给切开
ListNode k1 = sortList(head);
ListNode k2 = sortList(fast);
return mergeList(k1,k2);
}
public ListNode mergeList(ListNode k1,ListNode k2){
if(k1 == null && k2 == null){
return null;
}
ListNode res = new ListNode(0);//我新建了一个链表头res,这里的合并链表的空间复杂度其实是O(1)
//,因为它后面连接的节点都是已经存在了的,都是直接指过去的
ListNode pre = res;
ListNode p1 = k1;
ListNode p2 = k2;
while(p1 != null && p2 != null){
if(p1.val <= p2.val){
res.next = p1;
p1 = p1.next;
}else{
res.next = p2;
p2 = p2.next;
}
res = res.next;
}
res.next = p1==null? p2,p1;
return pre.next;
}
}
2.归并排序的非递归版本
bottom-to-up 的归并思路是这样的:先两个两个的 merge,完成一趟后,再 4 个4个的 merge,直到结束。举个简单的例子:[4,3,1,7,8,9,2,11,5,6].
step=1: (3->4)->(1->7)->(8->9)->(2->11)->(5->6)
step=2: (1->3->4->7)->(2->8->9->11)->(5->6)
step=4: (1->2->3->4->7->8->9->11)->5->6
step=8: (1->2->3->4->5->6->7->8->9->11)
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode sortList(ListNode head) {
ListNode tmp = head;
int len = 0;
while(tmp != null){
tmp = tmp.next;
//fast = fast.next.next;
len++;
}
ListNode left = head;
ListNode right = null;
ListNode cur = head;
ListNode tail = new ListNode(0);
ListNode res = tail;
for(int step = 1;step < len;step = step*2){
cur = head;
while(cur != null){
left = cur;
tmp = cut(cur,step);
if(tmp == null){
right = null;
cur = null;
}else{
right = tmp.next;
tmp.next = null;//将左半部分砍断
tmp = cut(right,step);
if(tmp == null)
cur = null;
else{
cur = tmp.next;
tmp.next = null;
}
}
res.next = mergeList(left,right);
while(res.next != null){
res = res.next;
}
}
head = tail.next;
res = tail;
}
return head;
}
public ListNode cut(ListNode cur,int step){
while(step != 1){
if(cur == null)
return null;
cur = cur.next;
step--;
}
return cur;
}
public ListNode mergeList(ListNode k1,ListNode k2){
if(k1 == null && k2 == null){
return null;
}
ListNode res = new ListNode(0);
ListNode pre = res;
ListNode p1 = k1;
ListNode p2 = k2;
while(p1 != null && p2 != null){
if(p1.val <= p2.val){
res.next = p1;
p1 = p1.next;
}else{
res.next = p2;
p2 = p2.next;
}
res = res.next;
}
res.next = p1 == null? p2:p1;
return pre.next;
}
}
3.快速排序
借鉴别人的代码
class Solution {
public ListNode sortList(ListNode head) {
if(head==null||head.next==null) return head;
// 没有条件,创造条件。自己添加头节点,最后返回时去掉即可。
ListNode newHead=new ListNode(-1);
newHead.next=head;
return quickSort(newHead,null);
}
// 带头结点的链表快速排序
private ListNode quickSort(ListNode head,ListNode end){
if (head==end||head.next==end||head.next.next==end) return head;
// 将小于划分点的值存储在临时链表中
ListNode tmpHead=new ListNode(-1);
// partition为划分点,p为链表指针,tp为临时链表指针
ListNode partition=head.next,p=partition,tp=tmpHead;
// 将小于划分点的结点放到临时链表中
while (p.next!=end){
if (p.next.val<partition.val){
tp.next=p.next;
tp=tp.next;
p.next=p.next.next;
}else {
p=p.next;
}
}
// 合并临时链表和原链表,将原链表接到临时链表后面即可
tp.next=head.next;
// 将临时链表插回原链表,注意是插回!(不做这一步在对右半部分处理时就断链了)
head.next=tmpHead.next;
quickSort(head,partition);
quickSort(partition,end);
// 题目要求不带头节点,返回结果时去除
return head.next;
}
}
来源:CSDN
作者:fsdgfsf
链接:https://blog.csdn.net/fsdgfsf/article/details/103723915