【数据结构】快速排序非递归算法及其改进
在学数据结构中排序这一章节的时候,有一道有关快速排序的作业题描述如下:
按下述要求编写快速排序的非递归算法:
定义一个栈(或队列),把整个序列的上、下界入栈(或队列)。当栈(或队列)非空时进行如下操作:
(1)取栈顶(或队头)元素作为序列的上、下界,在区间的头部、中间、尾部取关键字居中的元素作为中枢元素,进行一趟快速排序;
(2)在一趟排序过程中,如果子表已有序(没有发生元素交换),则该子序列排序结束,否则先对划分出的长度较短的子表进行排序,且将另一子表的上、下界入栈(或队列)保存;
(3)若待排序区间中数据元素数小于等于3,则不再进行分割,而是直接进行比较完成排序。
完成算法实现后,用大数据量进行测试,同原有快速排序算法在时间上进行对比分析。
这道题本质上是在优化快速排序。
1.用栈代替递归
函数的递归本身就利用了堆栈,用栈改写递归算法为非递归是常用思路。我们可以将序列的上下界入栈,排序时再取栈顶元素并出栈,本题中要求先对划分出的长度较短的子表进行排序,那么我们就先入栈较长的子表,将较短的子表后入栈以便下次循环中优先出栈。(部分)代码如下:
SeqStack<int> s;
while( !s.isEmpty()) //栈非空且未排好序{//得到某分区的左右边界int r;s.getTop(r);s.pop();int l;s.getTop(l);s.pop();boundary = Part(a, l, r, Flag); //boundary为枢轴元素所在位置if( boundary - l < r - boundary) ///如果左分区比右分区短{if( boundary + 1 < r ) //判断右分区是否存在{s.push(boundary + 1);s.push(r);}if( boundary - 1 > l ) //判断左分区是否存在{//将左分区端点后入栈以便优先排序s.push(l);s.push(boundary - 1);}}else //如果右分区比左分区短{if( boundary - 1 > l ) //判断左分区是否存在{s.push(l);s.push(boundary - 1);}if( boundary + 1 < r ) //判断右分区是否存在{///将右分区端点后入栈以便优先排序s.push(boundary + 1);s.push(r);}}}
2.中枢元素的取法
一般书本上的快速排序是取第一个待排序的元素作为中枢元素,而为了优化快速排序算法,如果能够尽量将中枢元素取在整个待排序数组的中位数附近,那么两个子表的长度就会接近,这样一来就减少了时间复杂度,优化了快速排序算法,本题中采取的方法是:在区间的头部、中间、尾部取关键字居中的元素作为中枢元素。其实我们也可以平均取五个点、七个点来找居中的元素。(头中尾取中值)代码如下:
int Mid(int first, int mid, int last)
//求头,中,尾中关键字居中的元素
{if ((first>=mid&&first<=last)||(first<=mid&&first>=last))return first;else if ((mid>=first&&mid<=last)||(mid<=first&&mid>=last))return mid;elsereturn last;
}
注意,要在Partition函数中交换首元素和中枢元素的位置:
int pivot = Mid(low, (low+high)/2, high);//选择区间的头部、中间、尾部取关键字居中的元素作为中枢元素Swap(elem[pivot], elem[low]); //保持代码的统一性
3.在一趟排序中,如果子表已有序,则该子序列排序结束
这一点便于理解,代码实现上可以定义一个bool类型的标记,来判断Partition中是否发生过元素交换,如果没有交换bool赋true。
4.待排区间中元素数<=3,则不再进行分割,则直接比较排序
快速排序是适用于大数据量的排序方式,在数据量比较小的时候,使用插入排序或者选择排序较好,所以很多实用的排序算法使用快排+插排的方式排序。本题中要求:若待排序区间中数据元素数小于等于3,则不再进行分割,而是直接进行比较完成排序,代码如下:
void JustSort(int a[], int left, int right)
//数组元素小于等于3时的直接比较排序
{if(right-left==1) //序列只有两个元素时{if(a[left] > a[right]){Swap(a[left], a[right]);}}else //三个元素时{if(a[left] > a[left+1])Swap(a[left], a[left+1]);if(a[left+1] > a[right])Swap(a[left+1], a[right]);if(a[left] > a[left+1])Swap(a[left], a[left+1]);}
}
应用以上优化方法后,用50000个随机元素的数组进行排序,与书本上的递归快排进行时间比较,如下:
可见有一定的优化效果。
全部代码如下:
#ifndef __ExQUICKSORT_H__
#define __ExQUICKSORT_H__
#include "SeqStack.h"int Mid(int first, int mid, int last)
///求头,中,尾中关键字居中的元素
{if ((first>=mid&&first<=last)||(first<=mid&&first>=last))return first;else if ((mid>=first&&mid<=last)||(mid<=first&&mid>=last))return mid;elsereturn last;
}int Part(int elem[], int low, int high, int &flag) ///参数flag用以判断是否已经有序,以便结束排序
//原快速排序算法中的划分部分,写成函数方便循环调用
{int pivot = Mid(low, (low+high)/2, high);//选择区间的头部、中间、尾部取关键字居中的元素作为中枢元素Swap(elem[pivot], elem[low]); /// 交换枢轴元素和首元素的位置,保持代码的统一性int e = elem[low]; // 取枢轴元素int i = low, j = high;while (i < j){while (i < j && elem[j] >= e) // 使j右边的元素不小于枢轴元素j--;if (i < j){elem[i++] = elem[j];flag = 1;}while (i < j && elem[i] <= e) // 使i左边的元素不大于枢轴元素i++;if (i < j){elem[j--] = elem[i];flag = 1;}}elem[i] = e;return i; //返回枢轴元素位置
}void JustSort(int a[], int left, int right)
///数组元素小于等于3时的直接比较排序
{if(right-left==1) //序列只有两个元素时{if(a[left] > a[right]){Swap(a[left], a[right]);}}else //三个元素时{if(a[left] > a[left+1])Swap(a[left], a[left+1]);if(a[left+1] > a[right])Swap(a[left+1], a[right]);if(a[left] > a[left+1])Swap(a[left], a[left+1]);}
}template <class ElemType>
void ExQuickSort(ElemType a[], int left, int right)
// 操作结果:对数组elem[low .. high]中的元素进行快速排序
{int Flag = 0; ///标记,用来判断是否已经排好序(未发生过交换)SeqStack<int> s;if( left<right ){if(right-left < 3) //如果序列小于等于3个元素{JustSort(a, left, right);return ;}int boundary = Part(a, left, right, Flag); //划分后的中枢所在位置if(Flag == 0)return ;if( boundary - left < right - boundary) ///如果左分区比右分区短{if( boundary + 1 < right ) //判断右分区是否存在{s.push(boundary + 1);s.push(right);}if( boundary - 1 > left ) //判断左分区是否存在{///将左分区端点后入栈以便优先排序s.push(left);s.push(boundary - 1);}}else ///如果右分区比左分区短{if( boundary - 1 > left ) //判断左分区是否存在{s.push(left);s.push(boundary - 1);}if( boundary + 1 < right ) //判断右分区是否存在{///将右分区端点后入栈以便优先排序s.push(boundary + 1);s.push(right);}}while( !s.isEmpty()) //栈非空且未排好序{//得到某分区的左右边界int r;s.getTop(r);s.pop();int l;s.getTop(l);s.pop();boundary = Part(a, l, r, Flag); //boundary为枢轴元素所在位置if(Flag == 0) ///如果已经有序,结束排序。return ;if(right-left < 3) //如果序列小于等于3个元素{JustSort(a, left, right);return ;}if( boundary - l < r - boundary) ///如果左分区比右分区短{if( boundary + 1 < r ) //判断右分区是否存在{s.push(boundary + 1);s.push(r);}if( boundary - 1 > l ) //判断左分区是否存在{///将左分区端点后入栈以便优先排序s.push(l);s.push(boundary - 1);}}else ///如果右分区比左分区短{if( boundary - 1 > l ) //判断左分区是否存在{s.push(l);s.push(boundary - 1);}if( boundary + 1 < r ) //判断右分区是否存在{///将右分区端点后入栈以便优先排序s.push(boundary + 1);s.push(r);}}}}
}#endif // __ExQUICKSORT_H__
【数据结构】快速排序非递归算法及其改进相关推荐
- 快速排序非递归算法c语言实现,数据结构与算法----3.5 非递归的快速排序方法
[c++]代码库#include using namespace std; #include using namespace std; typedef int KeyType; struct LEle ...
- 递归算法到非递归算法的转换
递归实质在定义自身的同时又出现了对自身的调用.递归算法是许多软件编程人员常用的方法,结构简单.清晰.可读性好.但在实际应用中也存在一些问题:1.并不是每一门语言都支持递归,比较典型的FORTRAN语言 ...
- 非递归算法——快速排序、归并排序
哈喽大家好,我是保护小周ღ,本期为大家带来的是常见排序算法中的快速排序.归并排序,非递归算法,分享所有源代码,粘贴即可运行,保姆级讲述,包您一看就会,快来试试吧~ 目录 一.递归的缺陷 1.1 栈是什 ...
- 数据结构 | 二叉树 先根、中根、后根遍历的非递归算法
上期文章: 数据结构 | 树与二叉树 参考教材:<数据结构>,刘大有 编程语言: C++ 目录 (一)二叉树的存储结构 (二)二叉树的遍历 先根遍历非递归算法 中根遍历非递归算法 后根遍历 ...
- 数据结构——二叉树的非递归算法
二叉树的非递归算法 先序遍历非递归算法1 先序遍历非递归算法2 非递归交换左右孩子算法 使用栈来实现二叉树的非递归算法 栈的基本算法 #include<stdio.h> #include& ...
- 数据结构-----后序遍历二叉树非递归算法(利用堆栈实现)
一.非递归后序遍历算法思想 后序遍历的非递归算法中节点的进栈次数是两个,即每个节点都要进栈两次,第二次退栈的时候才访问节点. 第一次进栈时,在遍历左子树的过程中将"根"节点进栈,待 ...
- 数据结构二叉树中序遍历递归和非递归算法
2022.11.19 二叉树中序遍历递归和非递归算法 任务描述 相关知识 编程要求 测试说明 C/C++代码 任务描述 本关任务:给定一棵二叉树,使用递归和非递归的方法实现二叉树的中序遍历结果. 相关 ...
- 数据结构-二叉树(统计二叉树的结点个数递归与非递归算法)
文章目录 思路 Java 实现 思路 求结点个数为什么能用递归? 二叉树求结点个数,从根结点开始,求二叉树结点个数,对于根结点就是求左右子树所有结点数之和再加一,对于左右子树又是如此计算,这样的形式满 ...
- 汉诺塔递归与非递归算法
问题描述: 在印度,有这么一个古老的传说:在世界中心贝拿勒斯(在印度北部)的圣庙里,一块黄铜板上插着三根宝石针.印度教的主神梵天在创造世界的时候,在其中一根针上从下到上地穿好了由大到小的64片金片,这 ...
最新文章
- 转:Siri之父:语音交互或将主导未来十年发展
- 【持续更新】JAVA面向对象多线程编程的一些tips
- mysql 字符编码
- 等差数列划分 II - 子序列(动态规划)
- 快速弄懂陌生领域是一项“赚钱”的能力
- Java中如何使用非阻塞异步编程——CompletableFuture
- java netty socket_Netty对socket的抽象
- 三款旗舰手机、四大高端生态新品,Redmi发布K50系列等七大重磅新品
- 2013年1月5号第一次事故
- OA系统选型:选择好的工作流引擎
- PHP对图片按照一定比例缩放并生成图片文件
- ATmega16 单片机 AVR单片机 智能风扇控制器
- windows 网络监控_如何在Windows 10中监控网络使用情况
- ios 侧滑返回停顿_iOS侧滑卡死解决方法
- python入门容器-列表ListDay05
- PHP中的网络编程 -- Socket篇
- 详细详解One Hot编码-附代码
- 火影推荐程序连载14-Vue开源项目使用探索
- STM32F103实现激光测距传感器测距WT-VL53L0 L1
- Android平台上集成萤石SDK
热门文章
- mysql语句在node.js中的写法
- redis学习笔记——(1)
- 炒房客身家过亿 曝炒房心得及地产10大真相
- 我们的生命,不因别人的喜欢而存在
- socket read time out解决方法_time_after方法对jiffies回绕问题的解决
- 计算机一级应用于段落还是文字,计算机一级复习资料
- python自动补全库_这个库厉害了,自动补全Python代码,节省50%敲码时间
- java重载方法math_Java语言程序设计(十二)Math数学类,方法重载及变量作用域...
- java怎样访问servlet_如何访问URL并从java servlet获取响应?
- 中tr不能显示字符_垃圾文本识别中基本操作指南和错误总结,第三部分