在学数据结构中排序这一章节的时候,有一道有关快速排序的作业题描述如下:

按下述要求编写快速排序的非递归算法:
定义一个栈(或队列),把整个序列的上、下界入栈(或队列)。当栈(或队列)非空时进行如下操作:
(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__

【数据结构】快速排序非递归算法及其改进相关推荐

  1. 快速排序非递归算法c语言实现,数据结构与算法----3.5 非递归的快速排序方法

    [c++]代码库#include using namespace std; #include using namespace std; typedef int KeyType; struct LEle ...

  2. 递归算法到非递归算法的转换

    递归实质在定义自身的同时又出现了对自身的调用.递归算法是许多软件编程人员常用的方法,结构简单.清晰.可读性好.但在实际应用中也存在一些问题:1.并不是每一门语言都支持递归,比较典型的FORTRAN语言 ...

  3. 非递归算法——快速排序、归并排序

    哈喽大家好,我是保护小周ღ,本期为大家带来的是常见排序算法中的快速排序.归并排序,非递归算法,分享所有源代码,粘贴即可运行,保姆级讲述,包您一看就会,快来试试吧~ 目录 一.递归的缺陷 1.1 栈是什 ...

  4. 数据结构 | 二叉树 先根、中根、后根遍历的非递归算法

    上期文章: 数据结构 | 树与二叉树 参考教材:<数据结构>,刘大有 编程语言: C++ 目录 (一)二叉树的存储结构 (二)二叉树的遍历 先根遍历非递归算法 中根遍历非递归算法 后根遍历 ...

  5. 数据结构——二叉树的非递归算法

    二叉树的非递归算法 先序遍历非递归算法1 先序遍历非递归算法2 非递归交换左右孩子算法 使用栈来实现二叉树的非递归算法 栈的基本算法 #include<stdio.h> #include& ...

  6. 数据结构-----后序遍历二叉树非递归算法(利用堆栈实现)

    一.非递归后序遍历算法思想 后序遍历的非递归算法中节点的进栈次数是两个,即每个节点都要进栈两次,第二次退栈的时候才访问节点. 第一次进栈时,在遍历左子树的过程中将"根"节点进栈,待 ...

  7. 数据结构二叉树中序遍历递归和非递归算法

    2022.11.19 二叉树中序遍历递归和非递归算法 任务描述 相关知识 编程要求 测试说明 C/C++代码 任务描述 本关任务:给定一棵二叉树,使用递归和非递归的方法实现二叉树的中序遍历结果. 相关 ...

  8. 数据结构-二叉树(统计二叉树的结点个数递归与非递归算法)

    文章目录 思路 Java 实现 思路 求结点个数为什么能用递归? 二叉树求结点个数,从根结点开始,求二叉树结点个数,对于根结点就是求左右子树所有结点数之和再加一,对于左右子树又是如此计算,这样的形式满 ...

  9. 汉诺塔递归与非递归算法

    问题描述: 在印度,有这么一个古老的传说:在世界中心贝拿勒斯(在印度北部)的圣庙里,一块黄铜板上插着三根宝石针.印度教的主神梵天在创造世界的时候,在其中一根针上从下到上地穿好了由大到小的64片金片,这 ...

最新文章

  1. 转:Siri之父:语音交互或将主导未来十年发展
  2. 【持续更新】JAVA面向对象多线程编程的一些tips
  3. mysql 字符编码
  4. 等差数列划分 II - 子序列(动态规划)
  5. 快速弄懂陌生领域是一项“赚钱”的能力
  6. Java中如何使用非阻塞异步编程——CompletableFuture
  7. java netty socket_Netty对socket的抽象
  8. 三款旗舰手机、四大高端生态新品,Redmi发布K50系列等七大重磅新品
  9. 2013年1月5号第一次事故
  10. OA系统选型:选择好的工作流引擎
  11. PHP对图片按照一定比例缩放并生成图片文件
  12. ATmega16 单片机 AVR单片机 智能风扇控制器
  13. windows 网络监控_如何在Windows 10中监控网络使用情况
  14. ios 侧滑返回停顿_iOS侧滑卡死解决方法
  15. python入门容器-列表ListDay05
  16. PHP中的网络编程 -- Socket篇
  17. 详细详解One Hot编码-附代码
  18. 火影推荐程序连载14-Vue开源项目使用探索
  19. STM32F103实现激光测距传感器测距WT-VL53L0 L1
  20. Android平台上集成萤石SDK

热门文章

  1. mysql语句在node.js中的写法
  2. redis学习笔记——(1)
  3. 炒房客身家过亿 曝炒房心得及地产10大真相
  4. 我们的生命,不因别人的喜欢而存在
  5. socket read time out解决方法_time_after方法对jiffies回绕问题的解决
  6. 计算机一级应用于段落还是文字,计算机一级复习资料
  7. python自动补全库_这个库厉害了,自动补全Python代码,节省50%敲码时间
  8. java重载方法math_Java语言程序设计(十二)Math数学类,方法重载及变量作用域...
  9. java怎样访问servlet_如何访问URL并从java servlet获取响应?
  10. 中tr不能显示字符_垃圾文本识别中基本操作指南和错误总结,第三部分