排序
排序分类
- 排序
- 插入排序
- 直接插入排序
- 折半插入排序
- 表插入排序
- 希尔排序
- 交换排序
- 冒泡排序
- 快速排序
- 选择排序
- 直接选择排序
- 堆排序
- 归并排序
- 插入排序
快速排序(Quick)
快速排序采用了分治法(将原问题分解为若干个规模更小但结构与原问题相似的子问题。递归地解这些子问题,然后将这些子问题的解组合为原问题的解。)
下面是将基准数缓存,我个人感觉直接交换也很方便。
如下图所示,假设最开始的基准数据为数组第一个元素23,则首先用一个临时变量去存储基准数据,即tmp=23;然后分别从数组的两端扫描数组,设两个指示标志:low指向起始位置,high指向末尾.
首先从后半部分开始,如果扫描到的值大于基准数据就让high减1,如果发现有元素比该基准数据的值小(如上图中18<=tmp),就将high位置的值赋值给low位置 ,结果如下:
然后开始从前往后扫描,如果扫描到的值小于基准数据就让low加1,如果发现有元素大于基准数据的值(如上图46=>tmp),就再将low位置的值赋值给high位置的值,指针移动并且数据交换后的结果如下:
然后再开始从后向前扫描,原理同上,发现上图11<=tmp,则将low位置的值赋值给high位置的值,结果如下:
然后再开始从前往后遍历,直到low=high结束循环,此时low或high的下标就是基准数据23在该数组中的正确索引位置.如下图所示.
最后得到 {18 11 0 8} 23 {46},再对左子数列与右子数列进行同样的操作。最终得到一个有序的数列。
希尔排序(Shell)
希尔排序也是一种插入排序,它是简单插入排序经过改进之后的一个更高效的版本,也称为缩小增量排序。希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。
直接选择排序(Select)
每一趟从待排序的数据元素中选出最小(最大)的元素,顺序放在待排序的数列最前,直到全部待排序的数据元素全部排完。
注意这里区别冒泡排序,不是交换相邻元素,而是把满足条件的元素与指定的排序位置交换(如从最后一个元素开始排序),这样排序好的位置逐渐扩大,最后整个数组都成为已排序好的格式。所以虽然它们的时间复杂度一样,但是直接选择排序进行的交换操作很少。
堆排序(Heap)
堆排序是利用堆这种数据结构而设计的一种排序算法,堆排序是一种选择排序。
堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。如下图:
同时,我们对堆中的结点按层进行编号,将这种逻辑结构映射到数组中就是下面这个样子
假设给定无序序列结构如下:
从叶子结点开始,将最大的值不断交换,最后放到堆顶,再将对顶元素与末尾元素交换,使末尾元素最大。重新调整结构,使其继续满足堆定义。再将堆顶元素与无序数组中的末尾元素交换,找第二大值。后续过程,继续进行调整,交换,如此反复进行,最终使得整个序列有序。
具体过程可以看:https://www.cnblogs.com/chengxiao/p/6129630.html
直接插入排序(Straight Insertion Sort)
直接插入排序的基本思想是:把n个待排序的元素看成为一个有序表和一个无序表。开始时有序表中只包含1个元素,无序表中包含有n-1个元素,排序过程中每次从无序表中取出第一个元素,将它插入到有序表中的适当位置,使之成为新的有序表,重复n-1次可完成排序过程。
具体过程:当插入第i(i >= 1)时,前面的V[0],V[1],……,V[i-1]已经排好序。这时,用V[I]的排序码与V[i-1],V[i-2],…的排序码顺序进行比较,找到插入位置即将V[i]插入,原来位置上的元素向后顺移。
从后往前找到合适的位置后插入
折半插入排序
折半插入排序是对直接插入排序的改进。
我们看直接插入排序的步骤简单而言其实就2步,第1步是从已经排好序的数组中找到该插入的点,第2步是将数据插入,然后后面的数据整体后移。那么直接插入排序是如何找到该插入的点的呢?是无脑式的从头到尾的遍历。
折半插入改进的地方就是第二步,寻找插入的位置时采用折半查找法。
折半插入排序减少了比较元素的次数,约为O(nlogn),比较的次数取决于表的元素个数n。因此,折半插入排序的时间复杂度仍然为O(n²),但它的效果还是比直接插入排序要好。
冒泡排序(Bubble)
我无话可说,你懂得。
时间复杂度我再啰嗦一下:若原数组本身就是有序的(这是最好情况),仅需 n − 1 n-1 n−1次比较就可完成;若是倒序,比较次数为 n − 1 + n − 2 + . . . + 1 = n ( n − 1 ) / 2 n-1+n-2+...+1=n(n-1)/2 n−1+n−2+...+1=n(n−1)/2,交换次数和比较次数等值。所以,其时间复杂度依然为 O ( n 2 ) O(n^2) O(n2)。综合来看,冒泡排序最好时间复杂度为是 O ( n ) O(n) O(n)。
冒泡排序的优化
- 假设我们现在排序ar[]={1,2,3,4,5,6,7,8,10,9}这组数据,按照上面的排序方式,第一趟排序后将10和9交换已经有序,接下来的8趟排序就是多余的,什么也没做。所以我们可以在交换的地方加一个标记,如果那一趟排序没有交换元素,说明这组数据已经有序,不用再继续下去。
- 优化一仅仅适用于连片有序而整体无序的数据(例如:1, 2,3 ,4 ,7,6,5)。但是对于前面大部分是无序而后边小半部分有序的数据(1,2,5,7,4,3,6,8,9,10)排序效率也不可观,对于种类型数据,我们可以继续优化。既我们可以记下最后一次交换的位置,后边没有交换,必然是有序的,然后下一次排序从第一个比较到上次记录的位置结束即可。
- 一次排序可以确定两个值,正向扫描找到最大值交换到最后,反向扫描找到最小值交换到最前面。例如:排序数据1,2,3,4,5,6,0
参考:https://blog.youkuaiyun.com/hansionz/article/details/80822494
归并排序(Merge)
归并排序是利用归并的思想实现的排序方法,该算法采用经典的分治(divide-and-conquer)策略。
归并排序我们采用递归去实现(也可采用迭代的方式去实现)。
下面是将[4,5,7,8]和[1,2,3,6]两个已经有序的子序列,合并为最终序列[1,2,3,4,5,6,7,8],来看下实现步骤。
表插入排序
表插入排序,即使用链表的存储结构对数据进行插入排序。在对记录按照其关键字进行排序的过程中,不需要移动记录的存储位置,只需要更改结点间指针的指向。
在表插入排序算法求得的有序表是用链表表示的,也就注定其只能进行顺序查找。而如果想用折半查找的算法,就需要对链表进行再加工,即对链表中的记录进行重新排列,具体做法为:遍历链表,将链表中第 i 个结点移动至数组的第 i 个下标位置中。
具体看:https://www.cnblogs.com/ciyeer/p/9075303.html
时间复杂度
排序方法 | 时间复杂度 (最坏) | 时间复杂度 (最好) | 时间复杂度 (平均) | 空间复杂度 | 稳定性(stability) |
---|---|---|---|---|---|
快速排序 | O ( n 2 ) O(n^2) O(n2) | O ( n l o g n ) O(nlogn) O(nlogn) | O ( n l o g n ) O(nlogn) O(nlogn) | O ( l o g n ) O(logn) O(logn) | 不稳定 |
希尔排序 | O ( n 2 ) O(n^2) O(n2) | O ( n 1.3 ) O(n^{1.3}) O(n1.3) | 不确定 | O ( 1 ) O(1) O(1) | 不稳定 |
直接选择排序 | O ( n 2 ) O(n^2) O(n2) | O ( n 2 ) O(n^2) O(n2) | O ( n 2 ) O(n^2) O(n2) | O ( 1 ) O(1) O(1) | 不稳定 |
堆排序 | O ( n l o g n ) O(nlogn) O(nlogn) | O ( n l o g n ) O(nlogn) O(nlogn) | O ( n l o g n ) O(nlogn) O(nlogn) | O ( 1 ) O(1) O(1) | 不稳定 |
直接插入排序 | O ( n 2 ) O(n^2) O(n2) | O ( n 2 ) O(n^2) O(n2) | O ( n 2 ) O(n^2) O(n2) | O ( 1 ) O(1) O(1) | 稳定 |
折半插入排序 | O ( n 2 ) O(n^2) O(n2) | O ( n 2 ) O(n^2) O(n2) | O ( n ) O(n) O(n) | O ( 1 ) O(1) O(1) | 稳定 |
表插入排序 | O ( n 2 ) O(n^2) O(n2) | O ( n 2 ) O(n^2) O(n2) | O ( n ) O(n) O(n) | O ( 1 ) O(1) O(1) | 稳定 |
冒泡排序 | O ( n 2 ) O(n^2) O(n2) | O ( n 2 ) O(n^2) O(n2) | O ( n ) O(n) O(n) | O ( 1 ) O(1) O(1) | 稳定 |
归并排序 | O ( n l o g n ) O(nlogn) O(nlogn) | O ( n l o g n ) O(nlogn) O(nlogn) | O ( n l o g n ) O(nlogn) O(nlogn) | O ( n ) O(n) O(n) | 稳定 |
算法稳定性 – 假设在数列中存在a[i]=a[j],若在排序之前,a[i]在a[j]前面;并且排序之后,a[i]仍然在a[j]前面。则这个排序算法是稳定的!
注意:全文原是仅供自己学习参考的,参考了各种大牛们的解析,有些没有加链接,如有侵权,请告知。