简介:从基本的O(n^2)时间复杂度的冒泡排序、选择排序、插入排序以及希尔排序到高级的O(nlogn)时间复杂度的归并排序、快速排序、堆排序。分析各个算法的时间复杂度,空间复杂度等。
基本排序算法
一、冒泡排序(BubbleSort)
基本思想
相邻两个元素比较大小,大的元素下沉,小的元素上浮
过程
- 从前往后比较相邻两个元素,如果后面的元素小,则交换位置,遍历完数组,最大元素将被放在数组尾部。
- 重复上述循环,遍历数组第1到n-i(i=1,2,…n-2)个元素,依次将第2,3,…,n-1大的元素放在数组尾部,循环结束,最小的元素自然就在数组头部。
算法分析
平均时间复杂度:O(n^2)
推导:外层循环n-1次 ,内层循环n-i-1(i=0,1,2,…n-2)次,故时间复杂度为: (n-1)+(n-2)+…1=(n-1)n/2=O(n^2)
空间复杂度:O(1)
稳定排序
由于下沉中遇到相等元素,不交换位置,故属于稳定排序
原地排序
代码实现
1 | public class BubbleSort { |
二、选择排序(SelectionSort)
基本思想
每次选择未排好序中最小的元素与当前未排好序的第一个元素交换位置,不断遍历,直到整个数组有序
过程
- 从前往后遍历整个数组,找到最小的那个元素,并与第1个元素交换位置
- 重复上述过程,遍历数组中第i(i=2,3,…n-1)个到第n个元素,找到其中最小的元素与第i个元素交换位置
算法分析
平均时间复杂度:O(n^2)
空间复杂度:O(1)
稳定排序
原地排序
代码实现
1 | public class SelectionSort { |
三、插入排序(InsertionSort)
基本思想
从前往后访问数组元素,每次保证所访问元素插入到之前数组的合适位置。
过程
- 第2个元素与第1个元素比较大小,如果比第1个元素小,则交换两个元素位置
- 访问第i(i=3,4,….n)个元素,将该元素插入到前面数组中的合适位置,访问完数组所有元素,则排序完成
算法分析
平均时间复杂度:O(n^2)
空间复杂度:O(1)
稳定排序
原地排序
代码实现
1 | public class InsertionSort { |
四、希尔排序(ShellSort)
基本思想
给定增量序列,按照增量由大到小,直到增量为1,将数组依照增量序列分为k个序列,对所有序列进行插入排序。完成数组排序。
过程
- 对于最大增量m,对序列(1,1+m,…,1+km),…,(a,a+m,…,a+km),…, (n-km,n-(k-1)m,…,n)进行插入排序
- 减小增量,重复上述过程,直到增量为1,对整个数组进行一次插入排序,使数组有序
算法分析
平均时间复杂度:
希尔增量( ht=[N/2] hk=[hk-1 /2] ):O(n^2)
Hibbard增量(hk=2k-1):O(n^(3/2))
空间复杂度:O(1)
不稳定排序
原地排序
代码实现
1 | public class ShellSort { |
高级排序算法
五、归并排序(MergeSort)
基本思想
先递归的分解数列:一分为二,二分为四…,最后再合并数列,完成归并过程。待归并完成后,数组排序完成
过程
- 将数组从中间分为两部分,分别对左边右边数组进行归并排序,然后对两部分进行归并。
- 归并过程,额外开辟大小为n的数组空间,将每一次归并的结果放入新的数组,从头到尾依次访问两部分数组元素,较小的元素优先进行归并操作
算法复杂度分析
平均时间复杂度:O(nlogn)
推导:数组不断对分,递归层数logn, 每层遍历n个数组元素。故时间复杂度为nlogn
空间复杂度:O(n)
需要开辟和数组同样大小的空间n,放置归并操作的元素,以及递归层数logn,占用栈空间.故空间复杂度为n+logn=O(n)
稳定排序
非原地排序
代码实现
1. 自顶向下的归并排序(递归实现)
1 | import java.util.Arrays; |
2. 自底向上的归并排序(非递归实现)
1 | public class MergeSortBU { |
3. 基于链表的归并排序
1 | public class MergeSortList { |
六、快速排序(QuickSort)
基本思想
从数组中随机选定枢纽元(pivot)v,将数组其他元素分为
- < v 和 >=v的两部分或者<=和>v的两部分,分别对两部分进行快速排序——单路快排
- <=v和>=v的两部分,分别对两部分进行快速排序——双路快排
- <v、==v以及>v的三部分,分别对<v和>v两部分进行快速排序——三路快排
过程
主要是三类的分割(Partition)的过程
算法分析
平均时间复杂度:O(nlogn)
空间复杂度:O(logn)
递归层数平均为logn,占用栈空间
不稳定排序
原地排序
代码实现
1. 单路快排
1 | public class QuickSort { |
2. 双路快排
1 | public class QuickSort2Ways { |
3. 三路快排
1 | public class QuickSort3Ways { |
七、堆排序(HeapSort)
基本思想
将待排序序列构造成一个最大堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了
过程
- 构造初始堆。将给定无序序列构造成一个最大堆,动态建堆-(参考基础堆排序以及优化堆排序代码)和静态建堆(参考原地堆排序)
- 将堆顶元素与末尾元素进行交换,使末尾元素最大。然后继续调整堆,再将堆顶元素与末尾元素交换,得到第二大元素。如此反复进行交换、重建、交换。
算法分析
平均时间复杂度:O(nlogn)
空间复杂度:O(1)
不稳定排序
原地排序
代码实现
1. 建堆
1 | public class MaxHeap <T extends Comparable<? super T>> { |
2. 基本堆排序
1 | public class HeapSort1 { |
3. 优化堆排序
1 | public class HeapSort2 { |
3. 原地堆排序
1 | public class HeapSort { |