Sorting
“除了时间复杂度、空间复杂度以外,稳定性也是评价排序算法性能的重要指标 ”
“什么叫做稳定的排序算法?”
”排序算法的分类 “
Insertion Sort
”插入排序 “
”每次将一个待排序的记录插入前面已排好序的子序列,直至全部记录插入完成 “
直接插入排序
- 当前待插入元素为 L(i), 该元素之前的序列 L[1 ... i-1] 均已有序, 其后为无序序列 L[i+1 ... n]
- 接下来要做的就是把L(i)正确插入到L[1 ... i-1] 的有序序列中
- i++重复上述步骤直至序列有序
显然核心操作为比较和移动,因此可以通过优化比较操作提高算法性能,下面通过折半查找寻找L(i)的插入位置:
折半插入排序
这里需要注意,为了保证算法稳定性,当折半查找找到A[mid] == A[0]时算法并不在此停止,而是继续调整low,high指针直到low==high+1
相较直接插入排序而言,折半插入排序减少了比较元素的次数,但由于移动次数不变,因此整体的时间复杂度仍然为O(n^2)
以上代码是针对顺序存储的线性表,如果序列通过链式存储,那么就不能使用折半查找而只能使用依次比对,因此虽然链式存储减少了元素的移动次数,但比较的时间复杂度仍然会达到O(n2),整体的时间复杂度同样为**O(n2)**
Shell Sort
”希尔排序 “
”部分有序 → 全局有序 “
算法实现
第一趟处理完毕后开始第二趟,此时序列如下:
性能分析
小结
Swap Sort
”交换排序 “
”对关键字进行两两比较并做相应交换从而实现排序“
Bubble Sort
”第 i 趟排序会使关键字值第 i 小的元素位于最终正确位置“
”冒泡有序产生的有序子序列是全局有序的,即已经排好的有序子序列中所有元素都小于后面无需子序列的任意元素“
算法实现
性能分析
小结
Quick Sort
”快速排序 “
”每次划分会使得基准元素 pivot位于最终正确位置(左边所有元素 < pivot < 右边所有元素)“
算法概述
通常以第一个元素A[low]作为枢轴pivot,此时A[low]可以视作”空位“,从high开始向左找比pivot小的数,将其填入A[low]所在的”空位“(我理解为一次”左移“);然后新的空位出现在了A[high]处,这时从low向右找比pivot大的数,将其填入刚产生的空位A[high](一次”右移“);这时空位又跑到了左边的A[low]处,重复上述步骤...
通过不断交替的左移右移来填补空位,直至low==high,此时low与high指向最后的空位,将pivot填入即结束这”一次划分“,此时pivot左侧所有元素均比它小,而右侧所有元素均比它大。这个过程也称为一趟快速排序,接下来按照分治法的思想,递归地对左右子序列重复上述划分过程直至排序完毕。
代码实现
https://github.com/AndyLeezCode/ClionProjects/blob/master/Sort/main.cpp
性能分析
时间复杂度&空间复杂度
递归层数也可以通过二叉树分析:
显然递归层数越少快排的性能越好:
因此,快排的优化思路就是尽量使选择的pivot可以将数组尽量划分均匀,例如随机取元素作为pivot等...
快排不具有稳定性,快排在平均性能上是所有内部排序算法中最优的
最后留个坑:关于一趟排序在概念上是否可以等同认为是一次划分的问题,408原题中有指出两者不可相提并论,但王道书上似乎没有对这两个概念做区分