您的位置:首页 > 理论基础 > 数据结构算法

[数据结构]快速排序

2014-06-11 22:06 302 查看

简述

快速排序是非常常见的一种排序。其平均性能优于其他的基于比较类型的排序(如堆排序,归并排序)。但是一个需要注意的问题是,快速排序在数列基本有序时,退化成冒泡排序,其复杂度陡然上升至O(n2),其每次划分均为1个数和余下所有记录。针对此问题,可以在划分前进行一次比较,取序列首尾和中间三个记录中关键字大小为中的那个记录为划分时作为轴枢的取值。此种方法可大大改善其在“最坏情况”下的性能。但仍不能达到其他同类型排序在有序情况下的表现。
快速排序的思想是利用划分,每次将序列分成按照枢轴上下两部分。然后再对上下两部分递归进行快速排序。

本文后面给出如下代码,
1,分别给出采用首尾中三者取中设为枢轴原始采用第一个数作为枢轴的两个版本划分函数。
2,对于主函数部分,将给出递归调用形式非递归形式。非递归形式采用stack栈来存储每次划分结果。而实质上个人觉得还是没有本质区别,因为快速排序本身带有很强的递归性质。

实例代码

int quickSort_pivot_o(int raw[], int low, int high){
//划分函数
//取第一个数为枢轴,并返回其划分后位置
int pivot = raw[low];
while (low<high){
while (low<high && raw[high] >= pivot) high--;
raw[low] = raw[high];
while (low<high && raw[low] <= pivot) low++;
raw[high] = raw[low];
}
raw[low] = pivot;
return low;
}
int quickSort_pivot_n(int raw[], int low, int high){
//划分函数
//取第一个数,最后一个数,和中间的数之中大小位于中间的那个数。
int mid = (low + high)/2;
int temp;
if (raw[low] < raw[mid] && raw[low] < raw[high]){
if (raw[mid] < raw[high]) temp = mid;
else temp = high;
}
else if (raw[low] > raw[mid] && raw[low] > raw[high]){
if (raw[mid] < raw[high]) temp = high;
else temp = mid;
}
else temp = low;
if(temp != low)
swap(raw, low, temp);//如果取中的结果不是第一位置,则交换之。
//以上代码最后使得low对应的数字是一个“中间”数,后面代码又回归到原先划分函数。

int pivot = raw[low];
while (low<high){
while (low<high && raw[high] >= pivot) high--;
raw[low] = raw[high];
while (low<high && raw[low] <= pivot) low++;
raw[high] = raw[low];
}
raw[low] = pivot;
return low;
}

//上面的代码是两个版本的划分函数,后面的主函数调用哪个划分函数皆可。后面的两个版本主函数分别是递归形式和栈形式。

void quickSort(int raw[], int low, int high){
//recursion
if (low<high){
int pivotloc = quickSort_pivot_n(raw, low, high);
quickSort(raw, low, pivotloc - 1);
quickSort(raw, pivotloc + 1, high);
}
}
void quickSortSt(int raw[], int low, int high){
//with stack
//非递归方法,借用stack储存划分的结果。
if (low < high){
std::stack<int> st;
int pivot = quickSort_pivot_n(raw, low, high);
if (low < pivot - 1){ st.push(low); st.push(pivot - 1); }
if (high > pivot + 1){ st.push(pivot + 1); st.push(high); }
//每次进栈的是一对数字,这对数字是划分后上下两个半区的首尾位置。
//如果这俩位置不重叠,那么就说明需要再一次划分
//每次新的划分就将栈顶的两个位置弹出栈,划分,再将新的结果压入栈。
while (!st.empty()){
high = st.top(); st.pop();
low = st.top(); st.pop();
pivot = quickSort_pivot_n(raw, low, high);
if (low < pivot - 1){ st.push(low); st.push(pivot - 1); }
if (high>pivot + 1){ st.push(pivot + 1); st.push(high); }
}
}
}


复杂度分析

需要注意的一个问题是,对于快速排序,随机数列是它的“最好情况”,并且此时,它的复杂度低于其他的比较类型排序(对数底数达到e而不是2,knlnn)。但是当序列有序时却是其最坏情况,复杂度急速恶化。退至冒泡排序O(n2)。
由于递归调用时需要堆栈,所以递归形式的快速排序在到达一定数量级时性能也不好。

总结

1,不稳定排序
2,由划分函数知道,再进行每次划分前,通过一系列处理,可以避免最坏情况。这也是其改进的原理之一。
3,其在递归时的堆栈深度,在最坏情况时达到n量级,在最好情况是logn深度。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: