1) 选择基准:在待排序列中,按照某种方式挑出一个元素,作为 “基准”(pivot);
2) 分割操作:以该基准在序列中的实际位置,把序列分成两个子序列。此时,在基准左边的元素都比该基准小,在基准右边的元素都比基准大;
3) 递归地对两个序列进行快速排序,直到序列为空或者只有一个元素;
对于分治算法,当每次划分时,算法若都能分成两个等长的子序列时,那么分治算法效率会达到最大。也就是说,基准的选择是很重要的。选择基准的方式决定了两个分割后两个子序列的长度,进而对整个算法的效率产生决定性影响。
最理想的方法是,选择的基准恰好能把待排序序列分成两个等长的子序列。
方法1 固定基准元引入的原因:虽然随机选取基准时,减少出现不好分割的几率,但是还是最坏情况下还是O(n^2),要缓解这种情况,就引入了三数取中选取基准。
分析:最佳的划分是将待排序的序列分成等长的子序列,最佳的状态我们可以使用序列的中间的值,也就是第N/2个数。可是,这很难算出来,并且会明显减慢快速排序的速度。这样的中值的估计可以通过随机选取三个元素并用它们的中值作为基准元而得到。事实上,随机性并没有多大的帮助,因此一般的做法是使用左端、右端和中心位置上的三个元素的中值作为基准元。显然使用三数中值分割法消除了预排序输入的不好情形,并且减少快排大约5%的比较次数。
举例:待排序序列为:8 1 4 9 6 3 5 2 7 0
左边为:8,右边为0,中间为6
我们这里取三个数排序后,中间那个数作为枢轴,则枢轴为6。
下面是实现代码:
//交换子表的记录,使枢轴记录到位,并返回枢轴所在的位置
int Partition(int array[], int low, int high){
/*三数中值分割法*/
int m = low + (high - low) / 2;//数组中间元素的下标
if (array[low]>array[high]) //保证左端较小
swap(array, low, high);
if (array[m] > array[high]) //保证中间较小
swap(array, high, m);
if (array[m] > array[low])
swap(array, m, low); //保证左端最小
//此时array[low]已经为整个序列左中右三个关键字的中间值
int pivotkey = array[low];
/*固定基准元
int pivotkey = array[low];
*/
/*随机基准元
int randomIndex = rand() % (high - low) + low;//取数组中随机下标
swap(array, randomIndex, low); //与第一个数交换
int pivotkey = array[low];
*/
int i = low, j = high;
while(i<j) //从表的两端交替向中间扫描,当没有相遇
{
while (array[j] >= pivotkey&&i<j){
j--;
}
while (array[i] <= pivotkey&&i<j){
i++;
}
if (i<j)
{
swap(array, i, j);
}
}
//最终将基准数归位
swap(array, low, i);
return i; //返回枢轴所在的位置
}
void QSort(int array[], int low, int high){
int pivot;
if (low<high)
{
pivot = Partition(array, low, high);//算出枢轴值
QSort(array, low, pivot - 1); //对低子表递归排序
QSort(array, pivot + 1, high); //对高子表递归排序
}
}
//对array做快速排序
void QuickSort(int array[], int n){
QSort(array, 0, n - 1);
}
void QSort(int array[], int low, int high){
int pivot;
if (high-low+1>=10)
{
pivot = Partition(array, low, high);//算出枢轴值
QSort(array, low, pivot - 1); //对低子表递归排序
QSort(array, pivot + 1, high); //对高子表递归排序
}
else{
InsertSort(array+low, high-low+1); //插入排序
}
}
插入排序代码:
/************************************************************************/
/* 插入排序 时间复杂度:O(n^2),空间复杂度:O(1),稳定 */
/************************************************************************/
void InsertSort(int array[], int n){
int j;
for (int i = 1; i < n;++i)
{
int key = array[i];
for (j = i; j>0 && array[j - 1] > key;j--)
{
array[j] = array[j - 1];
}
array[j] = key;
}
}