笔试算法题(54):快速排序实现之单向扫描、双向扫描(single-direction scanning, bidirectional scanning of Quick Sort)
议题:快速排序实现之一(单向遍历)
分析:
算法原理:主要由两部分组成,一部分是递归部分QuickSort,它将调用partition进行划分,并取得划分元素P,然后分别对P之前的部分和P 之后的部分递归调用QuickSort;另一部分是partition,选取划分元素P(随机选取数组中的一个元素,交换到数组末尾位置),定义两个标记 值left和right,随着划分的进行,这两个标记值将数组分成三部分,left之左的部分是小于划分元素P的值,left和right之间的部分是大 于等于划分元素P的值(等于p的值没有必要进行交换),right之右的部分是未划分的部分。运行中right自左向右遍历,left指向最左的一个不小 于P的值,当right遇见小于P的元素就与left当前索引的值交换,right和left同时前进,否则right直接前进,直到数组末尾,最后将P 与left当前指向的值交换,并且返回i的值;

弱势:对于已经排序的序列,运行效率相当于插入排序,因为此时的划分极其不平衡。算法受输入序列的顺序影响较大,不能保证某个元素能放到最终位置;
优势:内循环仅仅是比较数组元素和固定值,这种简单性正是快速排序如此高效的原因。处理划分元素恰好为序列中最大值,或者最小值;
性质:算法不稳定(尚未发现使基于数组的快速排序变得稳定的简单办法),任何相等的元素有可能在左右交换的过程中被重排成不同的序列。快速排序中关键点是划分元素的选取;
时间:运行时间为N㏒N;
样例:
int partition_1(int *array, int l, int r) {
int temp;
/**
* 利用rand函数随机获取l和r之间的一个元素作为划分值
* 并将其与array[r]进行交换
* */
srand((int)time());
int pivot=rand()%(l+(r-l));
printf("%d\n",pivot);
temp=array[pivot];
array[pivot]=array[r];
array[pivot]=temp;
/**
* 单向扫描:
* right向右遍历array,当遇到小于pivot的元素,则与
* left当前指向的元素进行交换,否则直接跳过,一直到
* 达array的最右边
* right为主动遍历,left为被动遍历
* */
int left=l, right=l;
while(right<r) {
if(array[right]<array[r]) {
/**
* 如果array[r]是array中最大的元素,则right
* 遇到的所有元素都要与left指向的元素进行交换
* 如果left与right相等,则交换是不必要的
* */
if(left!=right) {
temp=array[left];
array[left]=array[right];
array[right]=temp;
}
left++;right++;
} else {
/**
* 如果array[r]是array中最小的元素,则left会一直
* 停留在l处
* */
right++;
}
}
/**
* 最终需要将pivot元素换回其排序最终位置,也就是left当前的位置
* */
temp=array[left];
array[left]=array[r];
array[r]=temp;
return left;
}
void quickSort_1(int *array, int l, int r) {
/**
* 递归终止条件
* */
if(l>=r) return;
/**
* 利用partition方法获取划分元素
* */
int pivot=partition_1(array, l, r);
/**
* 划分元素已经到达最终位置,所以不用参与进一步处理
* 分别递归处理左右部分的元素
* */
quickSort_1(array, l, pivot-);
quickSort_1(array, pivot+, r);
}
int main() {
int array[]={,,,,,};
quickSort_1(array,,);
for(int i=;i<;i++)
printf("%d,",array[i]);
return ;
}
议题:快速排序实现之二(双向遍历)
分析:
算法原理:思想与上一种实现相同,只是使用不同的划分策略。使用left和right将数组划分成三部分,left之前的部分为小于等于划分元素P的 值,right之后的部分为大于划分元素P的值,left和right之间的部分是没有进行划分的区域。外循环使得left自左向右遍历,同时right 自右向左遍历,在这个过程中当left遇见大于P的值则停止,等待right遇见小于等于P的值又停止之后,交换他们的值,这个循环在left和 right相遇或者交叉之后停止。最后交换a[r]和left的值,并返回left;

弱势:当序列已经就绪,并每次划分元素选取为最左边或者最右边的值,一次递归划分仅去除一个元素,既是划分元素本身,程序将递归调用N次,而算法也演变为插入排序,比较次数达到(N+1)N/2次;
优势:快速排序满足分治递推式:CN=2CN/2 + N,最终化解为CN=NlgN;但此种情况需要划分点在序列的中间位置;
性质:算法不稳定,任何相等的元素有可能在交换的过程中被重排成不同的序列。快速排序中关键点是划分元素的选取。这个实现方式与上一个实现最大的差距就在于对等于划分元素值的处理上,还有就是本实现的遍历方式是两边向中间,而并不是只有一边到另外一边;
时间:当每次划分大约都将序列二分划分,运行时间为N㏒N,平均比较次数为2NlgN;最坏情况下,快速排序使用(N+1)N/2次比较;系统堆栈耗用的大小与logN成比例,退化的情况下雨N成比例;
样例:
int partition_2(int *array, int l, int r) {
int temp;
/**
* 利用rand函数随机获取l和r之间的一个元素作为划分值
* 并将其与array[r]进行交换
* */
srand((int)time());
int pivot=rand()%(l+(r-l));
printf("%d\n",pivot);
temp=array[pivot];
array[pivot]=array[r];
array[pivot]=temp;
/**
* 双向扫描:
* left从array的左边l处开始向右处理,直到r-1
* right从array的右边r-1处开始向左处理,直到l
* left和right都是主动移动
* */
int left=l, right=r-;
while(true) {
/**
* left左边的元素为小于等于array[r]的元素
* 并注意array[r]为最大值的情况,left会一直
* 移动到r
* */
while(array[left]<=array[r] && left<r)
left++;
/**
* right右边的元素为大于array[r]的元素
* 并注意array[r]为最小值的情况,right会一直
* 移动到l-1
* 这里仅使用大于的逻辑关系还可以避免当array
* 都是相同元素的情况时指针交叉的发生
* */
while(array[right]>array[r] && right>=l)
right--;
/**
* 有四种序列情况:
* 1. 一般情况:left和right在序列中间的某个元素交叉
* 2. array[r]是最大值情况:left移动到r,right在r-1
* 3. array[r]是最小值情况:left在l,right移动到l-1
* 4. array所有元素为同一个值:left移动到r,right在r-1
* */
if(left>=right)
break;
/**
* 交换元素
* */
temp=array[left];
array[left]=array[right];
array[right]=temp;
left++;right--;
}
/**
* 最终将array[r]的pivot元素与array[left]进行交换
* 由于此时的array[right]比array[r]小,所以只能交换
* array[left]
* */
temp=array[left];
array[left]=array[r];
array[r]=temp;
return left;
}
void quickSort_2(int *array, int l, int r) {
/**
* 递归终止条件
* */
if(l>=r) return;
/**
* 利用partition方法获取划分元素
* */
int pivot=partition_2(array, l, r);
/**
* 划分元素已经到达最终位置,所以不用参与进一步处理
* 分别递归处理左右部分的元素
* */
quickSort_2(array, l, pivot-);
quickSort_2(array, pivot+, r);
}
int main() {
int array[]={,,,,,};
quickSort_2(array,,);
for(int i=;i<;i++)
printf("%d,",array[i]);
return ;
}
笔试算法题(54):快速排序实现之单向扫描、双向扫描(single-direction scanning, bidirectional scanning of Quick Sort)的更多相关文章
- 前端如何应对笔试算法题?(用node编程)
用nodeJs写算法题 咱们前端使用算法的地方不多,但是为了校招笔试,不得不针对算法题去练习呀! 好不容易下定决心 攻克算法题.发现js并不能像c语言一样自建输入输出流.只能回去学习c语言了吗?其实不 ...
- 小小c#算法题 - 6 - 快速排序 (QuickSort)
快速排序是排序算法中效率比较高的一种,也是面试常被问到的问题. 快速排序(Quick Sort)是对冒泡排序的一种改进.它的基本思想是,通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字 ...
- 笔试算法题(03):最小第K个数 & 判定BST后序序列
出题:输入N个整数,要求输出其中最小的K个数: 分析: 快速排序和最小堆都可以解决最小(大)K个数的问题(时间复杂度为O(NlogN)):另外可以建立大小为K的最大堆,将前K个数不断插入最大堆,对于之 ...
- 笔试算法题(17):奇偶数分置数组前后段 & 反序访问链表
出题:输入一个数组,要求通过交换操作将奇数索引的元素调整到数组前半部分,偶数索引的元素调整到数组后半部分: 分析: 当然如果没有额外要求的话很容易实现,最好使用In-Place的实现策略:考虑插入排序 ...
- 笔试算法题(56):快速排序实现之非递归实现,最小k值选择(non-recursive version, Minimal Kth Selection of Quick Sort)
议题:快速排序实现之五(非递归实现,短序列优先处理,减少递归栈大小) 分析: 算法原理:此算法实现适用于系统栈空间不足够快速排序递归调用的需求,从而使用非递归实现快速排序算法:使用显示下推栈存储快速排 ...
- 笔试算法题(27):判断单向链表是否有环并找出环入口节点 & 判断两棵二元树是否相等
出题:判断一个单向链表是否有环,如果有环则找到环入口节点: 分析: 第一个问题:使用快慢指针(fast指针一次走两步,slow指针一次走一步,并判断是否到达NULL,如果fast==slow成立,则说 ...
- 笔试算法题(19):判断两条单向链表的公共节点 & 字符集删除函数
出题:给定两个单向链表的头结点,判断其是否有公共节点并确定第一个公共节点的索引: 分析: 由于是单向链表,所以每个节点有且仅有一个后续节点,所以只可能是Y型交叉(每条链表中的某个节点同时指向一个公共节 ...
- php笔试算法题:顺时针打印矩阵坐标-蛇形算法
这几天参加面试,本来笔试比较简单,但是在面试的时候,技术面试官说让我现场写一个算法,顺时针打印矩阵的坐标,如图所示 顺序为,0,1,2,3,4,9,14,19,24,23,22,21,20,15,10 ...
- 笔试算法题(57):基于堆的优先级队列实现和性能分析(Priority Queue based on Heap)
议题:基于堆的优先级队列(最大堆实现) 分析: 堆有序(Heap-Ordered):每个节点的键值大于等于该节点的所有孩子节点中的键值(如果有的话),而堆数据结构的所有节点都按照完全有序二叉树 排.当 ...
随机推荐
- ubuntu/linuxmint搜狗输入法无法输入中文或崩溃
执行下列命令: cd ~/.config rm -rf SogouPY SogouPY.users sogou-qimpanel 然后重启电脑.
- 查询及删除重复记录的SQL语句
1.查找表中多余的重复记录,重复记录是根据单个字段(peopleId)来判断 select * from people where peopleId in (select peopleId from ...
- Windows软件推荐
本篇博文主要记录一些实用性的windows软件或者插件,重在积累! 工具类 1.截图软件 https://zh.snipaste.com/ Snipaste 是一个简单但强大的截图工具,也可以让你将截 ...
- Plugging an Unplugged Pluggable Database issue 3
Multitenant Unplug/Plug Best Practices (文档 ID 1935365.1) 1.source 从0419 升级到1019 ,但是datapatch 没有回退041 ...
- 事件模型的介绍与Button的ActionListener
事件监听: 这是个很重要的概念,也是个很重要的模型,vb,vc都是这样用,甚至后面学的web框架也在用. 现在我们可以做很多按钮了吧,但是我们的按钮按它是没反应的,现在我们来看看怎么样才能让它有 ...
- OpenCV2.4.9 + Ubuntu15.04配置
为了run Car-Detection安装了OpenCV. 基本上就照着这个弄下来: ubuntu14.04 + OpenCV2.4.9 配置方法 1. 安装openCV 所需依赖库或软件: s ...
- rhel7安装oracle 11gR2
一.修改操作系统核心参数 在Root用户下执行以下步骤: 1)修改用户的SHELL的限制,修改/etc/security/limits.conf文件 输入命令:vi /etc/security/lim ...
- [转]C#委托Action、Action<T>、Func<T>、Predicate<T>
CLR环境中给我们内置了几个常用委托Action. Action<T>.Func<T>.Predicate<T>,一般我们要用到委托的时候,尽量不要自己再定义一 个 ...
- poj2718 Smallest Difference
思路: 暴力乱搞. 实现: #include <iostream> #include <cstdio> #include <sstream> #include &l ...
- InChatter系统之服务端的Windows服务寄宿方式(三)
为了部署的方便,我们开发Windows服务的服务寄宿程序,这样我们的服务便可以作为系统服务,随着系统的启动和关闭而启动和关闭,而避免了其他的设置,同时在服务的终止时(如系统关闭等)能及时处理服务的关闭 ...