议题:快速排序实现之五(非递归实现,短序列优先处理,减少递归栈大小)

分析:

  • 算法原理:此算法实现适用于系统栈空间不足够快速排序递归调用的需求,从而使用非递归实现快速排序算法;使用显示下推栈存储快速排序中的每一次划分结果 (将left和right都压入堆栈),并且首先处理划分序列较短的子序列(也就是在得到一次划分的左右部分时,首先将长序列入栈,然后让段序列入栈), 这样可以保证当快速排序退化的线性效率的时候,栈大小仍旧在㏒N范围内。算法策略类似于最小子树优先遍历规则;

  • 弱势:当序列已经就绪,每次划分元素选取为最左边或者最右边的值,一次递归划分仅去除一个元素,既是划分元素本身,而算法也演变为插入排序;

  • 优势:栈大小的最大值与㏒N成比例,但退化情况下增长到N成比例;此实现可以防止因为系统栈空间不足够线性耗用的时候,可以利用显示的堆栈替代;

  • 性质:算法不稳定,任何相等的元素有可能在交换的过程中被重排成不同的序列。快速排序中关键点是划分元素的选取。这个实现方式与上一个实现最大的差距就在于对等于划分元素值的处理上;

  • 时间:由于每一次都将较小子序列优先放入栈中,所以保证每一项都小于它下面一项的1/2,当排序N个元素时,可保证栈大小的最大值为㏒N;

样例:

 struct MyStack {
void push(int n);
int pop();
bool isEmpty();
}; void partition_5(int *array, int l, int r); void quickSort_5(int *array, int l, int r) {
/**
* 使用显示下推推展替代系统递归栈
* */
MyStack* stack=new MyStack();
/**
* 初始化堆栈
* */
stack->push(l);stack->push(r); int left, right, pivot;
while(!stack->isEmpty()) {
/**
* 从堆栈中获取序列段的端点
* */
right=stack->pop();left=stack->pop(); pivot=partition_5(array, left, right);
/**
* 如果当前序列段交叉,则跳过
* */
if(left>=right) continue;
/**
* 为了防止快速排序退化成线性处理,堆栈空间耗用
* 也为线性,此处首先处理较小的序列,也就是将较
* 长的子序列首先入栈,较小的子序列延后入栈;这样
* 可以优先处理最小子序列,从而防止退化成线性空间
* */
if((pivot-left)<(right-pivot)) {
stack->push(pivot+);stack->push(right);
stack->push(left);stack->push(pivot-);
} else {
stack->push(left);stack->push(pivot-);
stack->push(pivot+);stack->push(right);
}
}
} int main() {
int array[]={,,,,,};
quickSort_5(array,,);
for(int i=;i<;i++)
printf("%d,",array[i]);
return ;
}

议题:选择指定排序位置的元素(利用快速排序递归划分,求数值集合中第K小的元素)

分析:

  • 算法原理:为了获取序列中第K小的元素,一种办法是首先对序列进行排序,然后直接找到第k个元素,但是对于获取第K小的元素而言,全序列排序是不需要的; 所以通过使用快速划分来获得某一个划分值,有K-1个元素位于它的左边,则称这个划分元素为这个序列中第K小的值,这样的策略可以在接近线性的运算时间中 完成。与排序相关但又不需要完全进行排序的一个重要应用就是求一系列数值中排在第几位的值(第K小的值);

  • 基于快速排序的选择总体上为线性运行时间O(N)。使用Selection解决第K小问题,需要的时间与Nk成比例(也就是先寻找第一小,然后第二,直到第K小);

样例:

 int quickSelect_1(int *array, int l, int r, int k) {
/**
* 如果l和r交叉,则说明r-l+1小于k,没有第K小的元素
* 返回-1表示失败;
* */
if(l>r) return -;
/**
* 获取划分元素
* */
int pivot=partition(array, l, r);
/**
* 注意k的大小是针对序列的排序值
* 而l,r和pivot的大小是在array中的索引值,
* 所以k需要经过转化才能与pivot进行比较
* */
if(pivot>k+l-)
return quickSelect_1(array, l, pivot-, k);
else if(pivot<k+l-)
return quickSelect_1(array, pivot+, r, k-(pivot-l+));
else
return array[pivot];
} int quickSelect_2(int *array, int l, int r, int k) {
int ltemp=l, rtemp=r;
int pivot;
while(ltemp<rtemp) {
pivot=partition(array, ltemp, rtemp);
if(pivot>k+l-) {
rtemp=pivot-;
} else if(pivot<k+l-) {
ltemp=pivot+;
k=k-(pivot-l+);
} else
return array[pivot];
}
return -;
} int main() {
int array[]={,,,,,}; printf("%d\n",quickSelect_1(array,,,));
return ;
}

对快速排序算法的性能总结

  • 可以使用下面几种策略提升性能:随机选取划分元素,三元素中值确定划分元素,小子文件非递归排序;

  • 快速排序算法的性能取决于递归部分(㏒N)和划分部分(N),尽管快速排序中前面的操作会给后续的操作提供一定的排序信息从而加速后续的操作速度,但是性能仍然极大地取决于原序列的已排序程度;

  • 如果原始序列已经为正序序列,并且划分元素为数组中的最左值或最右值(实际情况中极有可能选择最右边的元素作为划分元素),这样的划分会使得极端不平等的划分,并且导致最终的性能接近于插入排序(N2);

  • 所以应该保证划分的均衡性,使用随机数组(可以将原始序列进行随机化)和随机选择划分元素(就像是上面几种实现那样)就能够在整体上保证划分的随机性,从而保证均衡划分,性能接近二分排序(N㏒N);

笔试算法题(56):快速排序实现之非递归实现,最小k值选择(non-recursive version, Minimal Kth Selection of Quick Sort)的更多相关文章

  1. 前端如何应对笔试算法题?(用node编程)

    用nodeJs写算法题 咱们前端使用算法的地方不多,但是为了校招笔试,不得不针对算法题去练习呀! 好不容易下定决心 攻克算法题.发现js并不能像c语言一样自建输入输出流.只能回去学习c语言了吗?其实不 ...

  2. 小小c#算法题 - 6 - 快速排序 (QuickSort)

    快速排序是排序算法中效率比较高的一种,也是面试常被问到的问题. 快速排序(Quick Sort)是对冒泡排序的一种改进.它的基本思想是,通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字 ...

  3. 笔试算法题(50):简介 - 广度优先 & 深度优先 & 最小生成树算法

    广度优先搜索&深度优先搜索(Breadth First Search & Depth First Search) BFS优缺点: 同一层的所有节点都会加入队列,所以耗用大量空间: 仅能 ...

  4. 笔试算法题(13):反转链表 & 左旋转字符串

    出题:反转链表(递归和非递归解法): 分析:有递归跟非递归实现,注意对原始链表头节点的处理,因为其他节点都指向下一个节点,其需要指向NULL: 解题: struct Node { int v; Nod ...

  5. 笔试算法题(52):简介 - KMP算法(D.E. Knuth, J.H. Morris, V.R. Pratt Algorithm)

    议题:KMP算法(D.E. Knuth, J.H. Morris, V.R. Pratt Algorithm) 分析: KMP算法用于在一个主串中找出特定的字符或者模式串.现在假设主串为长度n的数组T ...

  6. 笔试算法题(03):最小第K个数 & 判定BST后序序列

    出题:输入N个整数,要求输出其中最小的K个数: 分析: 快速排序和最小堆都可以解决最小(大)K个数的问题(时间复杂度为O(NlogN)):另外可以建立大小为K的最大堆,将前K个数不断插入最大堆,对于之 ...

  7. 笔试算法题(54):快速排序实现之单向扫描、双向扫描(single-direction scanning, bidirectional scanning of Quick Sort)

    议题:快速排序实现之一(单向遍历) 分析: 算法原理:主要由两部分组成,一部分是递归部分QuickSort,它将调用partition进行划分,并取得划分元素P,然后分别对P之前的部分和P 之后的部分 ...

  8. 笔试算法题(57):基于堆的优先级队列实现和性能分析(Priority Queue based on Heap)

    议题:基于堆的优先级队列(最大堆实现) 分析: 堆有序(Heap-Ordered):每个节点的键值大于等于该节点的所有孩子节点中的键值(如果有的话),而堆数据结构的所有节点都按照完全有序二叉树 排.当 ...

  9. php笔试算法题:顺时针打印矩阵坐标-蛇形算法

    这几天参加面试,本来笔试比较简单,但是在面试的时候,技术面试官说让我现场写一个算法,顺时针打印矩阵的坐标,如图所示 顺序为,0,1,2,3,4,9,14,19,24,23,22,21,20,15,10 ...

随机推荐

  1. 2-18 matplotlib模块的使用

    import numpy as np import matplotlib.pyplot as plt x = np.array([1,2,3,4,5,6,7,8]) y = np.array([3,5 ...

  2. CF767C Garland 【树形dp】By cellur925

    一句话题意:给定一个树,树有点权,要求把树的某些边删去,使树变成三个部分,每部分点权值和相等. 我们很容易想到,再读入的时候记录所有点的点权之和,点权除以3是最后权值相等的值.如果不能整除3一定无解, ...

  3. 【CSS】少年,你想拥有写轮眼么?

    最近笔者在公司内部开展了一次CSS讲座,由于授课经验不太足,授课效果自我感觉并不太好,不过课中有一个笔者用CSS写的一个小效果,其中还是包含了蛮多CSS的常见知识点的,正好也有部分同学很感兴趣如何实现 ...

  4. tuple元组创建单元素

    创建tuple单元素,一定要在结尾时添加一个逗号(,)解:如果不加逗号,哪怕是使用tuple()正确的创建元组,也会有歧义,它会把创建tuple元组的单元素,当成一个普通的输出语句结果列:如下,错误的 ...

  5. 工作记录 SQL prompt .net平台版本安装

    昨天泡脚了,虽然依然睡的很晚,但是身体很舒服,尽量坚持把 上午尝试用一根网线,链接服务器和笔记本,但是设置好了,Ping不通. 下午安装SQL插件SQL prompt https://www.cnbl ...

  6. AJPFX总结FileWriter类的write方法

    FileWriter从类OutputStreamWriter继承的 1.public void write(int c)          throws IOException写入单个字符.     ...

  7. AJPFX: Java基础多线程(一)

    多线程是Java学习的非常重要的方面,是每个Java程序员必须掌握的基本技能.本文只是多线程细节.本质的总结,并无代码例子入门,不适合初学者理解.初学者学习多线程,建议一边看书.看博文,以便写代码尝试 ...

  8. GIT配置及用法

    ssh配置 TortoiseGit配置 用法: 下面是我整理的常用 Git 命令清单.几个专用名词的译名如下. Workspace:工作区 Index / Stage:暂存区 Repository:仓 ...

  9. 【JavaScript】之函数的this

    提起函数中的this是很多初学者较为苦恼的,也是很多工作一段时间的人也存在误解的,你问this指向的是谁,大多数人会随口一答当然是指向调用这个函数的元素,当然这也没什么错,可是函数的调用方法不同thi ...

  10. 坑爹的鲁大师,VMware Workstation 报错(AsyncSocket error)一例解决

    今天准备把电脑上安装的VMware Play换成VMware Workstation,毕竟 Workstation 的快照功能还是很有必要的. 结果,VMware Workstation 安装成功后, ...