题目来源:排序链表

就是对链表进行排序,题目中另外一个要求就是使用\(O(nlogn)\)时间复杂度的算法,对于链表排序,比较好的办法就是递归排序
基本的算法流程如下
- 确定链表中点(可以使用快慢指针法)
- 将链表对半分,分别排序
- 有序链表合并
1、快慢指针
对于一个链表,想要获取其中点,可以定义两个指针,一个快指针,一个慢指针。初始化全部指向头节点,然后快指针每次移动两个,慢指针每次移动一个,当快指针到达尾部的时候,慢指针就在中间了
2、有序链表合并
这个其实在leetcode上有专门的一道题:合并两个有序链表
也可以参考我的另一个文章:合并两个有序链表
有递归和非递归的版本,由于这道题目相对于合并链表那道题数据量要大很多,实际测试发现使用非递归版本更快
自顶向下归并排序
使用归并排序需要注意的就是,当你找到了链表的重点,要记得斩断链表,方便使用Merge进行归并
点击查看代码
/**
* 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 process(ListNode head){
if(head.next == null)return head;
//快慢指针法 寻找链表中点
ListNode fast = head;
ListNode slow = head;
while(fast != null){
fast = fast.next;
if(fast != null) fast = fast.next;
if(fast != null)slow = slow.next;
}
ListNode second = slow.next; //第二段链表的开始
slow.next = null; //斩断两部分
ListNode left = process(head);
ListNode right = process(second);
return Merge(left,right);
}
public ListNode Merge(ListNode list1,ListNode list2){
ListNode ans = new ListNode();
ListNode prev = ans;
while(list1 != null && list2 != null){
if(list1.val < list2.val){
prev.next = list1; //将前面排序好的节点的next指向list
list1 = list1.next;
prev = prev.next;
}else{
prev.next = list2;
list2 = list2.next;
prev = prev.next;
}
}
if(list1 != null)prev.next = list1;
if(list2 != null)prev.next = list2;
return ans.next;
}
public ListNode sortList(ListNode head) {
return head == null ? head : process(head);
}
}
注意这段代码:
ListNode fast = head;
ListNode slow = head;
while(fast != null){
fast = fast.next;
if(fast != null) fast = fast.next;
if(fast != null)slow = slow.next;
}
如果使用这段代码却会报错
ListNode fast = head;
ListNode slow = head;
while(fast != null){
fast = fast.next;
if(fast != null){
fast = fast.next;
slow = slow.next;
}
}
看起来似乎是一样的,但是第二种是错误的,会死递归
快慢指针法用来找中点,然后进行合并
但是要保证一点:节点数为奇数,slow指向中点;节点数为偶数,slow指向中点左边那个
保证这个条件的目的就是:假如分到最后,最后一段链表长度为2,要保证可以分成两段
如果采用第二种方式,slow就会永远指向第二个节点,就会造成链表一直没办法被分成两段,从而死递归
ListNode second = slow.next; //第二段链表的开始
slow.next = null; //斩断两部分
ListNode left = process(head);
ListNode right = process(second);
return Merge(left,right);
所以一定要保证fast动了两个以后,slow再动
浙公网安备 33010602011771号