本文描述了LeetCode 148题 sort-list 的解法。

题目描述如下:

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

题目要求我们在O(n log n)时间复杂度下完成对单链表的排序,我们知道平均时间复杂度为O(n log n)的排序方法有快速排序、归并排序和堆排序。而一般是用数组来实现二叉堆,当然可以用二叉树来实现,但是这么做太麻烦,还得花费额外的空间构建二叉树,于是不采用堆排序。

故本文采用快速排序和归并排序来对单链表进行排序。

快速排序

在一般实现的快速排序中,我们通过首尾指针来对元素进行切分,下面采用快排的另一种方法来对元素进行切分。

我们只需要两个指针p1和p2,这两个指针均往next方向移动,移动的过程中保持p1之前的key都小于选定的key,p1和p2之间的key都大于选定的key,那么当p2走到末尾时交换p1与key值便完成了一次切分。

图示如下:

代码如下:

public ListNode sortList(ListNode head) {
//采用快速排序
quickSort(head, null);
return head;
}
public static void quickSort(ListNode head, ListNode end) {
if (head != end) {
ListNode node = partion(head, end);
quickSort(head, node);
quickSort(node.next, end);
}
} public static ListNode partion(ListNode head, ListNode end) {
ListNode p1 = head, p2 = head.next; //走到末尾才停
while (p2 != end) { //大于key值时,p1向前走一步,交换p1与p2的值
if (p2.val < head.val) {
p1 = p1.next; int temp = p1.val;
p1.val = p2.val;
p2.val = temp;
}
p2 = p2.next;
} //当有序时,不交换p1和key值
if (p1 != head) {
int temp = p1.val;
p1.val = head.val;
head.val = temp;
}
return p1;
}

归并排序

归并排序应该算是链表排序最佳的选择了,保证了最好和最坏时间复杂度都是nlogn,而且它在数组排序中广受诟病的空间复杂度在链表排序中也从O(n)降到了O(1)。

归并排序的一般步骤为:

  1. 将待排序数组(链表)取中点并一分为二;
  2. 递归地对左半部分进行归并排序;
  3. 递归地对右半部分进行归并排序;
  4. 将两个半部分进行合并(merge),得到结果。

首先用快慢指针(快慢指针思路,快指针一次走两步,慢指针一次走一步,快指针在链表末尾时,慢指针恰好在链表中点)的方法找到链表中间节点,然后递归的对两个子链表排序,把两个排好序的子链表合并成一条有序的链表。

代码如下:

public ListNode sortList(ListNode head) {
//采用归并排序
if (head == null || head.next == null) {
return head;
}
//获取中间结点
ListNode mid = getMid(head);
ListNode right = mid.next;
mid.next = null;
//合并
return mergeSort(sortList(head), sortList(right));
} /**
* 获取链表的中间结点,偶数时取中间第一个
*
* @param head
* @return
*/
private ListNode getMid(ListNode head) {
if (head == null || head.next == null) {
return head;
}
//快慢指针
ListNode slow = head, quick = head;
//快2步,慢一步
while (quick.next != null && quick.next.next != null) {
slow = slow.next;
quick = quick.next.next;
}
return slow;
} /**
*
* 归并两个有序的链表
*
* @param head1
* @param head2
* @return
*/
private ListNode mergeSort(ListNode head1, ListNode head2) {
ListNode p1 = head1, p2 = head2, head;
//得到头节点的指向
if (head1.val < head2.val) {
head = head1;
p1 = p1.next;
} else {
head = head2;
p2 = p2.next;
} ListNode p = head;
//比较链表中的值
while (p1 != null && p2 != null) { if (p1.val <= p2.val) {
p.next = p1;
p1 = p1.next;
p = p.next;
} else {
p.next = p2;
p2 = p2.next;
p = p.next;
}
}
//第二条链表空了
if (p1 != null) {
p.next = p1;
}
//第一条链表空了
if (p2 != null) {
p.next = p2;
}
return head;
}

完整代码放在:

https://github.com/morethink/algorithm/blob/master/src/main/java/algorithm/leetcode/L_148_SortList.java

参考文档

  1. 链表排序(冒泡、选择、插入、快排、归并、希尔、堆排序)

Java实现单链表的快速排序和归并排序的更多相关文章

  1. Java实现单链表的各种操作

    Java实现单链表的各种操作 主要内容:1.单链表的基本操作 2.删除重复数据 3.找到倒数第k个元素   4.实现链表的反转   5.从尾到头输出链表 6.找到中间节点 7.检测链表是否有环 8.在 ...

  2. java实现单链表的增删功能

    JAVA 实现单链表的增删功能 package linked; class LinkedTable{ } public class LinkedTableTest { public static vo ...

  3. JAVA数据结构——单链表

    链表:一. 顺序存储结构虽然是一种很有用的存储结构,但是他有如下几点局限性:1. 因为创造线性表的时候已经固定了空间,所以当需要扩充空间时,就需要重新创建一个地址连续的更大的存储空间.并把原有的数据元 ...

  4. 使用java实现单链表(转载自:https://www.cnblogs.com/zhongyimeng/p/9945332.html)

    使用java实现单链表----(java中的引用就是指针)转载自:https://www.cnblogs.com/zhongyimeng/p/9945332.html ? 1 2 3 4 5 6 7 ...

  5. Java: 实现顺序表和单链表的快速排序

    快速排序 快速排序原理 快速排序(Quick Sort)的基本思想是,通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可对这两部分记录继续进行排序,以达到 ...

  6. 148. Sort List (java 给单链表排序)

    题目:Sort a linked list in O(n log n) time using constant space complexity. 分析:给单链表排序,要求时间复杂度是O(nlogn) ...

  7. 用Java实现单链表的基本操作

    笔试题中经常遇到单链表的考题,下面用java总结一下单链表的基本操作,包括添加删除节点,以及链表转置. package mars; //单链表添加,删除节点 public class ListNode ...

  8. java实现单链表常见操作

    一.概述: 本文主要总结单链表常见操作的实现,包括链表结点添加.删除:链表正向遍历和反向遍历.链表排序.判断链表是否有环.是否相交.获取某一结点等. 二.概念: 链表: 一种重要的数据结构,HashM ...

  9. 数据结构——Java实现单链表

    一.分析 单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素.链表中的数据是以结点来表示的,每个结点由元素和指针构成.在Java中,我们可以将单链表定义成一个类,单链表的基 ...

随机推荐

  1. koala 编译scss不支持中文(包括中文注释),解决方案如下

    进入Koala安装目录,例如:C:\Program Files (x86)\Koala\rubygems\gems\sass-3.4.9\lib\sass 找到engine.rb文件,在该文件中找到最 ...

  2. HDU 4763 Theme Section

    题目: It's time for music! A lot of popular musicians are invited to join us in the music festival. Ea ...

  3. 算法,java代码实现打印万年历

      万年历 以1900年1月1号星期一为时间原点 星期日 第一天 星期一 第二天 星期二 第三天 星期三 第四天 星期四 第五天 星期五 第六天 星期六 第七天            1.计算出当前日 ...

  4. PHP截取中英文字符串

    //如果字符串长度超过10,则截取并以省略号结尾 function sub($str){ $str=(string)$str; if( mb_strlen($str,'utf-8') >10){ ...

  5. removeClass()

    定义和用法 removeClass() 方法从被选元素移除一个或多个类. 注释:如果没有规定参数,则该方法将从被选元素中删除所有类. 语法 $(selector).removeClass(class) ...

  6. dedesms中的导致生成静态页面多出空白行解决办法

    前几天给朋友用织梦仿一个手机网站,保存处理好的代码,页面头部在浏览里莫名其妙多了一段空白区域,用调试工具查看,发现是代码里多了一行&#65279.后来发现很多网友在生成织梦页面的时候,头部 ...

  7. iOS ASIHTTPRequest详解

    ASIHTTPRequest对CFNetwork API进行了封装,并且使用起来非常简单,用Objective-C编写,可以很好的应用在Mac OS X系统和iOS平台的应用程序中.ASIHTTPRe ...

  8. Centos6.9安装Node.js+npm爬坑

    Node.js选择 1.下载 wget https://nodejs.org/dist/v8.4.0/node-v8.4.0-linux-x86.tar.gz 2.解压 tar zxvf node-v ...

  9. 动态添加div及对应的js、css文件

    动态添加div及对应的js.css文件 在近期的项目开发中需要在首页中添加很多面板型的div,直接加载代码显得很繁琐,于是利用js封装一个动态添加div及其对应css文件和js文件的方法供大家参考使用 ...

  10. TF-卷积函数 tf.nn.conv2d 介绍

    转自 http://www.cnblogs.com/welhzh/p/6607581.html 下面是这位博主自己的翻译加上测试心得 tf.nn.conv2d是TensorFlow里面实现卷积的函数, ...