前言
排序算法面试中经常被考到,这里对排序算法做一次总结,方便巩固复习。
名称 | 时间 | 空间 | 说明 |
---|---|---|---|
冒泡排序 | 1 | 按顺序依次两个比较,通过交换位置保证大的排后面,依次循环 | |
选择排序 | 1 | 选出最小的数,放在首位;再次选,放在第二位 | |
快速排序 | 1 | 先选择中间值,然后把比它小的放在左边,大的放在右边(具体实现就是从两边找,找到一对后交换);然后分别对两边重复进行上述操作 | |
堆排序 | 1 | 利用堆的性质进行的选择排序 | |
插入排序 | 1 | 逐一取出元素,在已经排序的元素序列中从后向前扫描,放到适当的位置 | |
希尔排序 | 1 | 选择一个步长,然后按间隔为步长的单元进行排序,递归,步长逐渐变小,直至为1 | |
归并排序 | n | 逐层对半拆分,然后逐层从最小分组开始排序,合并成大的分组 |
冒泡排序
算法原理
1、遍历数组,从左到右,若右边的数比左边的大,则交换;
2、第一遍:最大的数沉下去;
3、第二遍:第二大的数沉在倒数第二个位置。
复杂度
时间复杂度最差是
两层循环,最差循环次数是
属于稳定排序
注:稳定排序:排序前后两个相等的数相对位置不变,则算法稳定;
非稳定排序:排序前后两个相等的数相对位置发生了变化,则算法不稳定
比如[5,5,1,4,3],排序后第一个5还是在前,第二个5还是在后,即稳定
Code
1 | // 这个排序次数是固定的,不论元素排列怎么样,时间复杂度都是O(n^2) |
选择排序
算法原理
1、线性遍历数组,选出最小的数,放在第一个位置;
2、然后,选出第二小的数,放在第二个位置。依此类推。
复杂度
时间复杂度最差是
两层循环,最差循环次数是
属于不稳定排序
Code
1 | void SelectSort(vector<int> & nums) { |
快速排序
算法原理
1、确定一个分界点;
2、调整区间:使得左边所有值 <= 分界点, 右边所有值 >= 分界点;
3、递归处理左右两段。
复杂度
时间复杂度最差是
属于不稳定排序
和归并相比,都是分治思想,但归并是稳定的,他是不稳定的
Code
1 | /* 看上面的图示, i 从左边移动, j 从右边移动,以最左边作为基准数 pivot ,所以先是 j 移动 |
堆排序
算法原理
1、将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点;
2、将其与末尾元素进行交换,此时末尾就为最大值;
3、然后将剩余 n-1 个元素重新构造成一个堆,这样会得到n个元素的次小值;
4、如此反复执行,便能得到一个有序序列了
注:升序采用大顶堆,降序采用小顶堆
复杂度
时间复杂度最差是
属于不稳定排序
详见堆排序
Code
1 | void display(int array[], int size) { |
插入排序
算法原理
对于数组a[10],
1、把a[0]认为已经排好序的;
2、从a[1]开始向前和已经排好序的对比,小于排好的就调换,否则停止,并认为a[0,1]已经排好;
3、然后a[2]又开始按步骤2进行,一直到a[10]。
复杂度
时间复杂度最差是
两层循环,最差循环次数是
属于稳定排序
Code
1 | void InsertSort(vector<int>& nums) { |
希尔排序
算法原理
希尔排序又称 “缩小增量排序”,它也是一种插入类排序的方法。
希尔排序属于插入类排序,是将整个有序序列分割成若干小的子序列分别进行插入排序。
其实和插入排序一样,只不过比较间隔不局限于1,而是 n/2,其余都一样。
复杂度
时间复杂度是
属于不稳定排序
Code
1 | void ShellSort(vector<int>& nums) { |
归并排序
算法原理
1、确定分界点mid = (left + right)>>1,逐层折半分组;
2、然后从最小分组开始比较排序,合并成一个大的分组,逐层进行;
3、最终所有的元素都是有序的。
注:>>1:除以2取整(进1法),<<1:乘以2取整
复杂度
时间复杂度最差是
假设待排序的数组元素个数为n,设高度为x,x意味着n个元素需要连续二分x次才剩下1个元素,即
属于稳定排序
Code
1 | void MergeSort(vector<int>& nums, int left, int right) { |