笔试算法题(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):每个节点的键值大于等于该节点的所有孩子节点中的键值(如果有的话),而堆数据结构的所有节点都按照完全有序二叉树 排.当 ...
随机推荐
- poj 2987 Firing【最大权闭合子图+玄学计数 || BFS】
玄学计数 LYY Orz 第一次见这种神奇的计数方式,乍一看非常不靠谱但是仔细想想还卡不掉 就是把在建图的时候把正权变成w*10000-1,负权变成w*10000+1,跑最大权闭合子图.后面的1作用是 ...
- bzoj 1602: [Usaco2008 Oct]牧场行走【瞎搞】
本来想爆手速写个树剖,然而快下课了就手残写了了个n方的短小-- 暴力把查询的两个点中深的一个跳上来,加上边权,然后一起跳加边权就行了 #include<iostream> #include ...
- 洛谷 P1251 餐巾计划问题【最小费用最大流】
建图细节比较多,对于每个点i,拆成i和i',i表示用的餐巾,i'表示脏餐巾,连接: (s,i,r[i],p)表示在这一天买新餐巾 (i,t,r[i],0)表示这一天用了r[i]的餐巾 (s,i+n,r ...
- maven-将依赖的 jar包一起打包到项目 jar 包中
前言: 有时候在项目开发中,需要很多依赖的 jar 包,其中依赖的 jar 包也会依赖其他的 jar 包,导致jar 包的管理很容易不全,以下有两种方法可以规避这个问题. 一.在pom.xml 文件中 ...
- JavaScript--DOM创建元素节点createElement
创建元素节点createElement createElement()方法可创建元素节点.此方法可返回一个 Element 对象. 语法: document.createElement(tagName ...
- Poj 3264 Balanced Lineup RMQ模板
题目链接: Poj 3264 Balanced Lineup 题目描述: 给出一个n个数的序列,有q个查询,每次查询区间[l, r]内的最大值与最小值的绝对值. 解题思路: 很模板的RMQ模板题,在这 ...
- ACM_逆序数(归并排序)
帮挂科 Time Limit: 2000/1000ms (Java/Others) 64bit IO Format: %lld & %llu Problem Description: 冬瓜发现 ...
- Latex排版工具的使用(二) 分类: Latex 2014-06-14 23:01 389人阅读 评论(0) 收藏
Latex可以支持中文排版,如何实现中文支持可以到网上查找教程. 下面编写一段对中文排版的Latex源文档: 新建文件second.tex: \documentclass{article} \usep ...
- 【转】JAVA的静态变量、静态方法、静态类
转自:http://blog.csdn.net/zhandoushi1982/article/details/8453522/ 静态变量和静态方法都属于静态对象,它与非静态对象的差别需要做个说明. ( ...
- mybatis的mapper.xml文件细节