归并排序
对于归并排序,大家怕是都不陌生,也可以非常熟练的写出归并排序的代码,因此本篇文章的重点并不是向大家介绍归并排序,而是讨论归并排序的拓展题型——力扣:148.排序链表。至于归并排序的原理可以参考:图解排序算法(四)之归并排序,下面也贴出代码实现
代码实现
/**
归并排序
分而治之
通过递归分,然后merge
*/
public class MergeSort {
public void sort(int[] a,int begin,int end,int[] tmp){
int mid=(begin+end)/2;
if(begin<end){//if begin<end 则分
sort(a,begin,mid,tmp);
sort(a,mid+1,end,tmp);
merge(a,begin,mid,end,tmp);
}
}
public void merge(int a[] ,int begin,int mid,int end,int[] tmp){
int i=begin;
int j=mid+1;
int t=0;
while(i<=mid&&j<=end){
if(a[i]<=a[j]){
tmp[t++]=a[i++];
}else{
tmp[t++]=a[j++];
}
}
while(i<=mid){
tmp[t++]=a[i++];
}
while(j<=end){
tmp[t++]=a[j++];
}
/*
// System.arraycopy(a,0,tmp,0,a.length);
for(int index=begin;index<=end;index++){
a[index]=tmp[index];
}*/
t=0;
while(begin<=end){
a[begin++]=tmp[t++];
}
}
public static void main(String[] args) {
MergeSort mergeSort=new MergeSort();
int[] a= {8,4,5,7,1,3,6,2};
int[] tmp=new int[a.length];
mergeSort.sort(a,0,a.length-1,tmp);
for(int e :tmp){
System.out.println(e);
}
}
}
排序链表
题目描述:
在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序。
示例 1:
输入: 4->2->1->3
输出: 1->2->3->4
示例 2:
输入: -1->5->3->4->0
输出: -1->0->3->4->5
归并排序的时间复杂度为O(nlogn),这个大家都知道,但是数组的归并排序,我们需要创建一个临时数组来保存中间结果,空间复杂度为n。所以这道题的主要思想就是如何利用链表的将归并排序的空间复杂度降为1。
结合归并排序的思想,我们主要需要考虑以下几个问题:
- 如何找到中间节点
- 如何进行拆分
- 如何进行合并
结合之前做链表类题型,我们可以很轻松的想到答案,这里直接贴出关键代码:
(1) 通过快慢指针找中点。
ListNode fast=head.next,slow=head;
//快慢指针 fast先置为 head.next,当它走到末尾时 slow指向的正好是中间节点的下标
//fast 如果也置为 head, slow最终指向的是中间节点的先一个的下标 这里需要明确一下
while(fast!=null&&fast.next!=null){
slow=slow.next;
fast=fast.next.next;
}
(2)拆分链表
ListNode tmp=slow.next;
slow.next=null;//拆分
ListNode left=sortList(head);
ListNode right=sortList(tmp);
(3)通过虚假头结点进行链表合并
ListNode h=new ListNode(0);//构造虚假链表头
ListNode res=h;
while(left!=null&&right!=null){
if(left.val<=right.val){
h.next=left;
left=left.next;
}else{
h.next=right;
right=right.next;
}
h=h.next;
}
h.next=left!=null?left:right;
return res.next;
总结
总体来说这是一道非常不错的题目,链表相关的知识点和排序都考察到了,非常值得学习!!