https://leetcode.com/problems/sort-list/

Sort a linked list in O(n log n) time using constant space complexity.

解题思路:

常见的O(nlogn)算法,快速排序、归并排序,堆排序。大概讲讲优缺点,在数据量很大并且很乱的时候,快速排序有着最快的平均速度,比如Java和C++语言的库排序函数就主要是快排,但基本上是优化过的,因为快排有缺点。对于本来就已经排好序的数列,快排反而要花到O(n^2)的时间,因为如果总是选取第一个元素作为pivot,每次只能将n的数列化为1和n-1两部分,而不是相等的两部分。而且快速排序不是稳定的,因为pivot最后被交换到某一个位置,是不确定的。由于快速排序一般用递归,所以需要O(logn)的额外空间,(递归树的高度)。最坏情况需要O(n),也就是上面说的情况,递归树一边倒。

堆排序在最坏情况下,也能有O(nlogn)的时间,这点比快排好,而且不需要额外的存储空间。但是它也是不稳定的。

归并排序用在数组上的时候,好处就是稳定的,而且最坏情况下,也能有O(nlogn)的时间,这点比快排好。但是它需要花O(n)的额外空间,来存放归并的结果。但是用在链表上的时候,可以不需要这O(n)的空间,因为链表的排序完全可以做到随意移动-插入,类似in-place。归并排序还有一个重要的用途,用在外排序上,比如海量数据存在n个文件中。

好了,看题目,这道题是链表,用归并排序是最合适的。题目 Merge Two Sorted Lists 中我们已经解决了,两个已经排序的链表的归并,那么这道题就是类似数组归并排序的概念,用递归从顶之下。将原链表不断分成两个链表,再分为两个,直到只有一个元素。然后再从低至上,两两归并排序它们,直到合成一个整的。

这里要注意理解的就是,每个递归的返回值应该怎么用。

/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode sortList(ListNode head) {
return mergeSortList(head);
} public ListNode mergeSortList(ListNode head){
if(head == null || head.next == null) {
return head;
}
ListNode fast = head;
ListNode slow = head;
//取终点的方法,是判断fast.next和fast.next.next,而不是fast和fast.next
while(fast.next != null){
fast = fast.next;
if(fast.next != null){
fast = fast.next;
slow = slow.next;
}
}
ListNode mid = slow.next;
//重要,将原链表从中间断开,可以不要考虑前后两段的连接了,这个问题交给下面的mergeList方法
slow.next = null;
head = mergeSortList(head);
mid = mergeSortList(mid);
return mergeList(head, mid);
} public ListNode mergeList(ListNode l1, ListNode l2) {
ListNode dummy = new ListNode(0);
ListNode current = dummy;
while(l1 != null && l2 != null){
if(l1.val < l2.val){
current.next = l1;
l1 = l1.next;
current = current.next;
}else{
current.next = l2;
l2 = l2.next;
current = current.next;
}
}
if(l1 != null){
current.next = l1;
}else{
current.next = l2;
}
return dummy.next;
}
}

update 2015/06/21:

三刷,把取中间节点的方法提取了出来。

/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
public class Solution {
public ListNode sortList(ListNode head) {
if(head == null || head.next == null) {
return head;
}
ListNode mid = getMidNode(head);
head = sortList(head);
mid = sortList(mid);
return mergeList(head, mid);
} public ListNode getMidNode(ListNode head) {
if(head == null || head.next == null) {
return head;
}
ListNode slow = new ListNode(0);
slow.next = head;
while(head != null) {
head = head.next;
if(head != null) {
head = head.next;
slow = slow.next;
}
}
ListNode res = slow.next;
slow.next = null;//这一步很重要
return res;
} public ListNode mergeList(ListNode l1, ListNode l2) {
ListNode dummy = new ListNode(0);
ListNode current = dummy;
while(l1 != null && l2 != null){
if(l1.val < l2.val){
current.next = l1;
l1 = l1.next;
current = current.next;
}else{
current.next = l2;
l2 = l2.next;
current = current.next;
}
}
if(l1 != null){
current.next = l1;
}else{
current.next = l2;
}
return dummy.next;
}
}

//20180920

https://www.geeksforgeeks.org/merge-sort/

注意的是针对链表的归并取的并不是mid node,而是mid的下一个node。

归并的一般步骤

MergeSort(arr[], l,  r)
If r > l
1. Find the middle point to divide the array into two halves:
middle m = (l+r)/2
2. Call mergeSort for first half:
Call mergeSort(arr, l, m)
3. Call mergeSort for second half:
Call mergeSort(arr, m+1, r)
4. Merge the two halves sorted in step 2 and 3:
Call merge(arr, l, m, r)
/**
* 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) {
return head;
}
ListNode midNext = findMidNext(head);
ListNode head1 = sortList(head);
ListNode head2 = sortList(midNext);
return merge(head1, head2);
} public ListNode findMidNext(ListNode head) {
if (head == null || head.next == null) {
return head;
}
ListNode dummy = new ListNode(-1);
dummy.next = head;
ListNode fast = head;
ListNode slow = head;
while (fast.next != null && fast.next.next != null) {
fast = fast.next.next;
slow = slow.next;
}
ListNode midNext = slow.next;
slow.next = null;
return midNext;
} public ListNode merge(ListNode head1, ListNode head2) {
ListNode dummy = new ListNode(-1);
ListNode iter = dummy;
while (head1 != null && head2 != null) {
if (head1.val <= head2.val) {
iter.next = head1;
head1 = head1.next;
} else {
iter.next = head2;
head2 = head2.next;
}
iter = iter.next;
}
if (head1 == null) {
iter.next = head2;
} else {
iter.next = head1;
}
return dummy.next;
}
}

Sort List 典型链表的更多相关文章

  1. 9. Sort List && Insertion Sort List (链表排序总结)

    Sort List Sort a linked list in O(n log n) time using constant space complexity.                   H ...

  2. leetcode:Sort List(一个链表的归并排序)

    Sort a linked list in O(n log n) time using constant space complexity. 分析:题目要求时间复杂度为O(nlogn),所以不能用qu ...

  3. sort list(给链表排序)

    Sort a linked list in O(n log n) time using constant space complexity. 题目要求使用O(nlogn)时间复杂度,可以考虑使用归并排 ...

  4. leetcode Sort List 对链表进行排序

    描述: Sort a linked list in O(n log n) time using constant space complexity. 在O(n*log(n))的时间复杂度,常数级空间复 ...

  5. 45.Sort List(链表排序)

    Level:   Medium 题目描述: Sort a linked list in O(n log n) time using constant space complexity. Example ...

  6. leetcode——Insertion Sort List 对链表进行插入排序(AC)

    Sort a linked list using insertion sort. class Solution { public: ListNode *insertionSortList(ListNo ...

  7. Leetcode148. Sort List排序链表

    在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序. 示例 1: 输入: 4->2->1->3 输出: 1->2->3->4 示例 2: 输入 ...

  8. Leetcode147. Insertion Sort List对链表进行插入排序

    对链表进行插入排序. 从第一个元素开始,该链表可以被认为已经部分排序(用黑色表示). 每次迭代时,从输入数据中移除一个元素(用红色表示),并原地将其插入到已排好序的链表中. 插入排序算法: 插入排序是 ...

  9. Java数据结构和算法 - 链表

    Q: 为什么要引入链表的概念?它是解决什么问题的? A: 数组作为数据存储结构有一定的缺陷,在无序数组中,搜索是低效的:而在有序数组中,插入效率又很低:不管在哪一个数组中删除效率都很低:况且一个数组创 ...

随机推荐

  1. Linux常用命令——目录处理命令

    1.建立目录:mkdir mkdir -p [目录名] -p 递归创建 命令英文原意:make directories 实例: [root@localhost ~]# ls anaconda-ks.c ...

  2. Django - 模版之继承

    模版继承: 当多个html有共同之处时,需要建立一个模版html(master.html) 1.在模版html中,写入公共的部分: 备注:在模版中,我们可以指定多个block ,其中,可以在标题,cs ...

  3. STL源码分析之空间配置器

    前言 SGI STL将new的申请空间和调用构造函数的两个功能分开实现, 如果对new不太清楚的, 可以先去看看这一篇new实现再来看配置器也不迟. 本节是STL分析的第一篇, 主要分析STL各个部分 ...

  4. Semi-colon expected (eclipse 引入 json文件报错)

    最近做的项目用到的前端框架有一个json文件夹,里面全是json文件,所以导入Eclipse的时候会在整个项目上都是红叉,但是其实不影响项目运行的,之前忙着码代码也没时间管他,这个红叉存留了一个月!今 ...

  5. VS C#报错CS1056意外的字符"(Unexpected Character")

    今天重装了下VS2017,再打开之前的项目发现运行不了了,报错如图 解决方法: 找到.NETFramework,Version=v4.5.2.AssemblyAttributes.cs这个文件删除就好 ...

  6. 升级 HTTPS,价值何在?

    HTTPS 实质上是一种面向安全信息通信的协议.从最终的数据解析的角度上看,HTTPS 与 HTTP 没有本质上的区别.对于接收端而言,SSL/TSL 将接收的数据包解密,将数据传给 HTTP 协议层 ...

  7. HTTP 请求的 GET 与 POST 方式的区别

    HTTP 请求的 GET 与 POST 方式的区别 在客户机和服务器之间进行请求-响应时,两种最常被用到的方法是:GET 和 POST. GET - 从指定的资源请求数据. POST - 向指定的资源 ...

  8. pace.js – 网页自动加载进度条插件

    网站顶部的页面加载进度条是怎么实现的,页面的加载进度百分比,有时候获取是比较麻烦的,当然也可以利用一些优秀的JavaScript插件来实现,今天就为大家介绍这样子的一款插件:pace.js. [官方网 ...

  9. CodeForces 220B(B. Little Elephant and Array)

    http://codeforces.com/contest/220/problem/B 题意:给出一个数组,给出m组询问,问区间中出现a[i] 次的有多少个. sl: 很显然的离线问题了. 大视野菜花 ...

  10. noip模拟赛 abcd

    [问题描述]有4个长度为N的数组a,b,c,d.现在需要你选择N个数构成数组e,数组e满足a[i]≤e[i]≤b[i]以及 并且使得 最大.[输入格式]输入文件名为abcd.in.输入文件共 N+1 ...