注:以下代码参考博客参考博客地址
快速排序的期望时间复杂度为O(nlogn),最坏时间复杂度为O(n²)。为了避免出现最坏情况,我们可以对其进行改进。
1.哨兵的改进,为了使每次划分的数不致于使两边相差过大,我们可以选择三者取中法选哨兵,即首尾和中间数的中位数来作为哨兵。
2.小数据量的改进,递归的快速排序在大概n<10的时候比插入排序要慢,所以我们在n<10的时候采用插入排序。
3.相同数字的改进,在存在大量相同数字的时候快速排序是很慢的,我们采用两个指针保存相同数字的信息,在划分时不用把它们算进去,这样就节省了大量的时间。
4.递归的优化。快排有两次递归调用,我们可以用循环代替后面的一次递归。
下面是具体的c代码
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#define Num 200000
void swap(int *a, int *b)
{
int t = *a;
*a = *b;
*b = t;
}
//函数作用:取待排序序列中low、mid、high三个位置上数据,选取他们中间的那个数据作为哨兵
int SelectPivotMedianOfThree(int arr[],int low,int high)
{
int mid = low + ((high - low) >> 1);//计算数组中间的元素的下标
//使用三数取中法选择哨兵
if (arr[mid] > arr[high])//目标: arr[mid] <= arr[high]
{
swap(arr+mid, arr+high);
}
if (arr[low] > arr[high])//目标: arr[low] <= arr[high]
{
swap(arr+low, arr+high);
}
if (arr[mid] > arr[low]) //目标: arr[low] >= arr[mid]
{
swap(arr+mid,arr+low);
}
//此时,arr[mid] <= arr[low] <= arr[high]
return arr[low];
//low的位置上保存这三个位置中间的值
//分割时可以直接使用low位置的元素作为哨兵,而不用改变分割函数了
}
//小数据的插入排序
void insert_sort(int a[], int low, int high)
{
int i, j;
for (i=low+1; i<=high; i++)
{
int key = a[i];
j = i-1;
while (j>=0 && key<a[j])
{
a[j+1] = a[j];
j--;
}
a[j+1] = key;
}
}
//快速排序,把哨兵左边和哨兵相同的元素移到最左边,右边和哨兵相同的元素移到最后边,在划分完之后又移到哨兵左右
void quick_sort(int arr[],int low,int high)
{
int first = low;
int last = high;
int left = low;
int right = high;
int leftLen = 0;
int rightLen = 0;
if (high - low + 1 < 10)
{
insert_sort(arr,low,high);
return;
}
//一次分割
int key = SelectPivotMedianOfThree(arr,low,high);//使用三数取中法选择枢轴
while(low < high)
{
while(high > low && arr[high] >= key)
{
if (arr[high] == key)//处理相等元素
{
swap(arr+right,arr+high);
right--;
rightLen++;
}
high--;
}
arr[low] = arr[high];
while(high > low && arr[low] <= key)
{
if (arr[low] == key)
{
swap(arr+left, arr+low);
left++;
leftLen++;
}
low++;
}
arr[high] = arr[low];
}
arr[low] = key;
//一次快排结束
//把与哨兵key相同的元素移到哨兵最终位置周围
int i = low - 1;
int j = first;
while(j < left && arr[i] != key)
{
swap(arr+i,arr+j);
i--;
j++;
}
i = low + 1;
j = last;
while(j > right && arr[i] != key)
{
swap(arr+i, arr+j);
i++;
j--;
}
quick_sort(arr,first,low - 1 - leftLen);
quick_sort(arr,low + 1 + rightLen,last);
}
int main()
{
int a[Num];
int i;
for (i=0; i<Num; i++)
{
a[i] = rand()%Num;//随机数来测试
//a[i] = 0; //用相同的值来测试,结果vc直接结束了
}
//for (i=0; i<Num; i++)
// printf("%d ", a[i]);
//printf("\nAferSort:\n");
int a1=clock(); //获取快排执行的时间
quick_sort(a, 0, Num-1);
int b=clock();//到这结束
int c=b-a1;//算出来的单位是毫秒
printf("%d\n", c);
// for (i=0; i<Num; i++)
// printf("%d ", a[i]);
getchar();
return 0;
}
递归的优化代码(经测试在数据量不是特别大的时候对效率的优化不大,所以列出来作为学习使用)
void quick_sort(int arr[],int low,int high)
{
int pivotPos = -1;
if (high - low + 1 < 10)
{
InsertSort(arr,low,high);
return;
}
while(low < high)
{
pivotPos = Partition(arr,low,high);
QSort(arr,low,pivot-1);
low = pivot + 1;
}
}
经测试,如果有大量数据相同的话,原来的快排是很慢的,而改良版的就很快了。