1、算法思想
快速排序是C.R.A.Hoare于1962年提出的一种划分交换排序。它采用了一种分治的策略,通常称其为分治法(Divide-and-ConquerMethod) ,分治法的基本思想是:将原问题分解为若干个规模更小但结构与原问题相似的子问题。递归地解这些子问题,然后将这些子问题的解组合为原问题的解。
2、快排步骤:
①分解:
在R[low..high]中任选一个记录作为基准(Pivot),以此基准将当前无序区划分为左、右两个较小的子区间R[low..pivotpos-1)和R[pivotpos+1..high],并使左边子区间中所有记录的关键字均小于等于基准记录(不妨记为pivot)的关键字pivot.key,右边的子区间中所有记录的关键字均大于等于pivot.key,而基准记录pivot则位于正确的位置(pivotpos)上,它无须参加后续的排序。
注意:划分的关键是要求出基准记录所在的位置pivotpos。划分的结果可以简单地表示为(注意pivot=R[pivotpos]): R[low..pivotpos-1].keys≤R[pivotpos].key≤R[pivotpos+1..high].keys 其中low≤pivotpos≤high。
②求解:
通过递归调用快速排序对左、右子区间R[low..pivotpos-1]和R[pivotpos+1..high]快速排序。
③组合:
因为当"求解"步骤中的两个递归调用结束时,其左、右两个子区间已有序。对快速排序而言,"组合"步骤无须做什么,可看作是空操作。
通俗理解核心步骤:
1.i =L; j = R; 将基准数备份形成第一个可以看成空值的a[i]。
2.j--由后向前找比它小的数,找到后把此数填前一个a[i]中。
3.i++由前向后找比它大的数,找到后也把此数填到前一个坑a[j]中。
4.再重复执行2,3二步,直到i==j,将基准数填入a[i]中
3、算法性能
时间复杂度:O(n*lgn)
最坏:O(n^2)
空间复杂度:O(n*lgn)
不稳定。
实现代码:
//先上递归版
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <time.h>
#include <stack>
using namespace std;
#define MAX 101
int Partition(int num[], int low, int high)
{ //int pivotkey;这个枢轴的选取很关键,改进版的可以用三数取中,就九数取中等
int pivotkey = num[low]; //在这里就取第一个元素
num[0] = pivotkey; //备份到num【0】,
while (low < high)
{
while (low < high&&num[high] >= pivotkey)
high--;
num[low] = num[high]; //不满足循环条件就执行这个语句,因为备份出【low】,所以可以覆盖他,这也是这个while在 //前面的原因。否则【high】会丢失的。
while (low < high&&num[low] <= pivotkey)
low++;
num[high] = num[low];
//交换low,是因为low=pivotkey,比较了。
}
num[low] = num[0];
return low; //最后high和low在某一个位置相遇,就是切分部的值。
}
void Qsort1(int num[], int low, int high) //对随机产生的无序数组进行快速排序,这也是二分法的缺陷之一。
{ //快排对很多数据更有优势,可以设置一个数据个数的阈值,才进行快排,这里也是一个优化的地方
int pivot;//
if (low < high)
{
pivot = Partition(num, low, high); //轴值所在的位置
Qsort1(num, low, pivot - 1); //递归的深度决定了时间的复杂度。
Qsort1(num, pivot + 1, high);
}
}
//非递归版,模拟栈的
inline void push2(stack<int> &s, int l, int r)
{
s.push(r);
s.push(l);
}
void Qsort(int num[], int l, int r)
{
stack<int> s;
push2(s, l, r);
int lwalker, rwalker, mid;
while (!s.empty())
{
int left = s.top(); s.pop();
int right = s.top(); s.pop();
lwalker = left;
rwalker = right;
mid = num[(lwalker + rwalker) / 2];
while (lwalker < rwalker)
{
while (num[lwalker] < mid) lwalker++;
while (num[rwalker]>mid) rwalker--;
if (lwalker <= rwalker)
{
int tmp = num[lwalker];
num[lwalker] = num[rwalker];
num[rwalker] = tmp;
lwalker++;
rwalker++;
}
}
if (lwalker < right) push2(s, lwalker,right);
if (rwalker>left) push2(s, left, rwalker);
}
}
void input(int num[])//实参传入的数组的首地址,而不是整个数组
{
int i;
srand((unsigned)time(NULL));//产生随机函数的随机数种子
for (i = 1; i < MAX; i++)
{
num[i] = rand() % 100;
}
}
void output(int num[])
{
int i;
for (i = 1; i <= MAX; i++)
{
printf("%5d", num[i]);
if (i % 100 == 0)
printf("\n");
}
}
void main()
{
int num[MAX],num2[MAX];
time_t start, end;
time(&start);
input(num);
cout << "递归排序前:" << endl;
output(num);
Qsort1(num, 0,MAX - 1);
cout << "递归排序后:" << endl;
output(num);
input(num2);
cout << "开始非递归排序,排序前:"<<endl;
output(num2);
cout << "非递归排序后:" << endl;
Qsort(num2, 0, MAX - 1);
output(num2);
time(&end);
cout << "用时为" << end-start << endl;
system("pause");
}