快速排序
基本概念
分治法思想。
把一个未排序的元素作 主元 / 基准 / 枢轴(pivot,通常取首元素);根据主元将所有元素划分为两个子序列,一个子序列的元素均比主元大,另一个均比主元小;递归地用相同的方法对两个子序列排序;直至每个子序列均有一个元素 or 为空。
图示
- 第一轮
2. 第二、三轮
代码
//划分左右子表
int Partition(int A[],int low,int high){ //升序
int pivot = A[low]; //选第一个元素为主元
while( low < high){ //low为左指针,high为右指针,所指元素与主元比较大小
//左<右 且 右>主 => 右指针左移 —— 符合条件,仅动指针
while( low < high && A[high] >= pivot ) --high;
A[low] = A[high]; //if右<主, 将右指针所指元素赋给左端low所指位置
//左<右 且 左<主 => 左指针右移 —— 符合条件,仅动指针
while( low < high && A[low] <= pivot ) ++low;
A[high] = A[low]; //if左>主, 将左指针所指元素赋给右端high所指位置
}
A[low] = pivot; //两指针指向同一位置,放入主元
return low; //返回主元所在位置
}
//快排
void QuickSort(int A[],int low,int high){
if(low < high){ // 递归跳出的条件
int pivotpos = Partition(A,low,high); //划分左右子表
QuickSort(A,low,pivotpos-1); //划分该趟主元的左子表
QuickSort(A,pivotpos+1,high); //划分该趟主元的右子表
}
}
效率
- 时间复杂度 = 关键代码执行次数 = O(n*递归层数)
最好:O(nlog₂n)
最坏:O(n²) —— 可优化,尽量选可以把数据中分的主元 - 空间复杂度 = O(递归层数)
最好:O(log₂n)
最坏:O(n) - 递归层数 = 调用栈的高度 (可看做 log₂n)
算法分析
- 不稳定
- 原地排序,但借助递归工作栈(空间复杂度来源于此,不是O(1))
- 对 小数组,快排 慢于 插入排序
- 初始大多有序 或 所选主元划分的左右子表不均匀(如: _ O _ _ _ _ _ _ ),均会导致性能变差(递归栈深度增加)
- ⼀次划分可确定⼀个元素的最终位置(主元),⼀趟排序至少可确定1个元素的最终位置(1趟maybe确定多个)
听说快排还有稳定版本的,目前没空深究,后面抽空研究研究,要是可行,我再补上
上述图片取自王道课件。内容用于自学,如有错误,还请各位指正!