一.相关概念
快速排序(Quicksort)是对冒泡排序的一种改进。
快速排序由C. A. R. Hoare在1962年提出。它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列
如图:
1.我们首先把表中的第一个元素作为枢纽,用一个变量暂存并空开那个位置
2.然后在表中从后往前开始遍历(j–),找到一个比枢纽小的元素放在那个空开的位置,并空开该元素的位置
3.接着在表中从前往后开始遍历(i++),找到一个比枢纽大的元素放在空开的位置,并空开该元素的位置
4.就这样反复前后交换,直至表被分割成独立的两部分,其中一部分的所有元素都比另外一部分的所有元素都要小(i=j时),然后把枢纽元素放在空开的位置(即i位置或j位置)
5.这样枢纽就把表分割成了两个子表,然后我们同样对子表分别做1,2,3,4操作,直到子表不可被分割(小于等于1个元素的表),最后整个序列便排序完成
如图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-q9XVKjAG-1593866840878)(http://www.th7.cn/d/file/p/2015/08/27/a42438122d1f874cce061a57a0206e7c.jpg)]
二.思路分析
很多书上写的快速排序算法都使用了递归,但是如果排序数据很多,递归堆栈会占用很大的内存,使得程序被终止,所以我这里教大家使用非递归算法实现快速排序,其实就是使用栈来实现递归转化为非递归.
1.首先我们需要一个存储表高低端位置序号的栈,初始我们把表的高低端位置(首尾位置)序号入栈,也就是入栈两个位置序号
2.我们用一个循环(栈非空就执行循环),循环里这样操作:出栈一个表(子表)的高低端位置,也就是出栈两个位置序号,然后对改子表进行排序分割
3.分割完成后,表被一分为二,然后分别入栈两个子表的高低端位置,也就是入栈四个位置序号
4.循环1,2,3,直到栈空,排序完成
三.代码实现
注意:顺序表中零单元不用,从索引1开始存储元素
//非递归快速排序(排列为递增序列)
void QuickSort(SqList &L)
{
//快速排序的非递归方法,用栈来保存每个子表的高低端位置
int MAX=L.length+2; //在最坏情况下栈需要的最大内存
int *stack=(int*)malloc(sizeof(int)*MAX); //构建栈
int top=0; //栈顶
int low=1,high=L.length; //低端位置,高端位置
int pivotkey; //枢纽
stack[top++]=high; //初始高端位置入栈
stack[top++]=low; //初始低端位置入栈
while(top!=0) //栈非空循环
{
low=stack[--top]; //要处理的子表的低端位置出栈
high=stack[--top]; //要处理的子表的高端位置出栈
L.elem[0]=L.elem[low]; //用子表的第一个记录作为枢纽记录,零单元作为辅助单元暂存枢纽
pivotkey=L.elem[low]; //枢纽记录关键字
int head=low; //记录首端
int end=high; //记录末端
while(low<high) //分割子表操作
{
while(low<high&&L.elem[high]>=pivotkey)
high--; //从后往前找到一个比枢纽关键字小的数据
L.elem[low]=L.elem[high]; //把比枢纽小的数据项放在低端
while(low<high&&L.elem[low]<=pivotkey)
low++; //从前往后找到一个比枢纽关键字大的数据
L.elem[high]=L.elem[low]; //把比枢纽大的数据项放在高端
}
L.elem[low]=L.elem[0]; //此时low=high,low为枢纽的插入位置,所以也可写成L.elem[high]=L.elem[0];
if(head>=end) //如果该子表不能再分割(即该子表小于等于1个单元),不再进行高低端位置入栈操作
continue;
stack[top++]=end; //枢纽右侧的子表高端位置入栈
stack[top++]=low+1; //枢纽右侧的子表低端位置入栈
stack[top++]=low-1; //枢纽左侧的子表高端位置入栈
stack[top++]=head; //枢纽左侧的子表低端位置入栈
}
free(stack); //释放栈内存
}
操作结果: