#include<iostream>
#include<ctime>
using namespace std;
/*
*快速排序
*1、所选问题描述:给定一个无序数组序列,将其按降序排列
例如输入【10, 43, 52, 6, 8, 2, 23, 1, 67, 89, 90, 27】,输出【90 89 67 52 43 27 23 10 8 6 2 1】
*2、算法思想
把n个元素划分为三段:左端Left,中间段middle和右端right。中段仅有一个元素。左端的元素都不大于中间段的元素,
右端的元素都不小于中间段的元素。因此可以对lefe和right对立排序,所以,快速排序是一种分治思想,把大问题分
为了若干个小问题。middle的元素称为支点或分割元素。
举例
考察元素【4,8,3,7,1,5,6,2】。假设选择元素6作为支点。因此6属于middle;4,3,1,5,2属于left,8和7属于right。
left排序结果为1,2,3,4,5;right排序为7,8。把右端的元素放在支点之后,左端left元素放在支点之前,即可得到有序序列。
*3实现步骤
1、指定一个支点
注意,是“指定”,并没有明确的约束条件,也就是说这个支点是任意一个元素,一般我们选择两种支点:当前序列首元,或者随机选取
两种方式各有优劣,前者胜在简单,但可能影响算法效率
快排中,支点的最终位置越靠近中间位置效率越高,读起来可能有点怪怪的,注意支点是一个值(具体元素),而不是字面意思的位置,
当支点在最终序列中的位置靠前或者靠后时算法效率都不高(类似于“最坏情况”)。
因此,后者在一定程度上减少了最坏情况的发生次数,但随机选取需要耗费额外的时间
所以在具体应用中一般采用第一种方式来指定“支点”,也就是直接把当前序列的首元作为“支点”。
2、进行一趟快排
快排中,一趟操作的最终目的是把“支点”放到它应该去的地方,同时,支点左边的元素都小于支点,右边的元素都大于支点,举个例子,
已知序列{7, -1, 5, 23, 100, 101},那么第一趟快排的结果是{_, _, 7, _, _, _},可以看到,首元(支点)已经去了它该去的地方
3、对子序列进行快排。
第二步我们已经成功用支点将序列分成了3个部分,left,middle right,这三个部分总体是有序的,但是每个元素内部确实无序的,
因此我们需要让这3个部分的内部也有序,对left和right继续使用快速排序就好(递归思想)。
*效率分析
1、快速排序算法的平均时间复杂度为O(nlogn),
2、具体到实际的快速排序操作,它的时间复杂度与基准元素的选取有关。如果每趟排序都将大部分元素划分到基准元素的一侧,那么快速排序将退化为冒泡排序,
时间复杂度为O(n^2);一种更为特殊的情形就是序列初始化状态按关键字排序,而每趟排序选取的基准元素为当前子序列中的第一个元素,这种情况下快速排序
相当于冒泡排序,时间复杂度为O(n^2)。
3、快速排序算法相比于其他排序算法来说比较耗费空间资源,因为快速排序需要栈空间来实现递归。如果每趟排序都将序列均匀划分成长度相近的两个子序列,
则栈的最大深度为[log2n]+1。但是如果每趟排序的基准元素都偏向于子序列的一端,最坏情况下栈的深度为n。平均起来,快速排序的空间复杂度为O(logn)。
*/
int partition(int *array, int low, int high) {
//二分
int mid = low + (high - low) / 2;//创建变量存储中间数
int temp;//创建交换数值时临时存的变量
//快排优化 : 三数取中 即将 low mid high 中的三个数中的中间数放在low位置上 作为基准数
//如果mid处元素大于high元素 则交换之
if (array[mid] > array[high]) {
temp = array[mid];
array[mid] = array[high];
array[high] = temp;
}
//如果mid处元素大于low元素 则交换之
if (array[mid] > array[low]) {
temp = array[mid];
array[mid] = array[low];
array[low] = temp;
}
//如果mlow处元素大于high元素 则交换之
if (array[low] > array[high]) {
temp = array[low];
array[low] = array[high];
array[high] = temp;
}
//得到基准值
int key = array[low];
//快排核心
while (low < high) {
//从尾端往前扫描找到一个比当前基准值大的元素
while (array[high] <= key && high > low) high--;
//将得到的high位置元素写入low位置
array[low] = array[high];
//从前端往后扫描找到一个比当前基准值小的元素
while (array[low] >= key && low < high) low++;//比key大的放右边
//将得到的low位置元素写入high位置
array[high] = array[low];
}
//将key写入最终位置
array[high] = key;
return high;
}
void sort(int *array, int low, int high) {
//递归出口
if (low >= high) return;//当要划分为两部分前先判断能不能划分,当没办法划分时(low>=high) ,就不用划分了,该数的位置对了。
//得到分割下标
int index = partition(array, low, high);//完成一次单元排序
//递归调用
sort(array, low, index-1);//对左边单元进行排序
sort(array, index + 1, high);//对右边单元进行排序
}
// 生成元素为随机数的数组
void Random(int a[],int n)
{
int i=0;
srand( (unsigned)time( NULL ) );
while(i<n)
{
a[i++]=rand();
}
}
int main()
{
int a[30] = {0};
Random(a,30);
for(int i = 0; i < sizeof(a) / sizeof(a[0]); i++)//循环输出排序好的数组内容
cout<<a[i]<<" ";
cout<<endl;
//传入数组进行排序
sort(a, 0,sizeof(a) / sizeof(a[0]) - 1);
//打印数组进行验证
for(int i = 0; i < sizeof(a) / sizeof(a[0]); i++)//循环输出排序好的数组内容
cout<<a[i]<<" ";
cout<<endl;
return 0;
}
关于快速排序的一些理解
最新推荐文章于 2022-02-06 22:15:10 发布