议题:快速排序实现之一(单向遍历)

分析:

  • 算法原理:主要由两部分组成,一部分是递归部分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)的更多相关文章

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

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

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

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

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

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

  4. 笔试算法题(17):奇偶数分置数组前后段 & 反序访问链表

    出题:输入一个数组,要求通过交换操作将奇数索引的元素调整到数组前半部分,偶数索引的元素调整到数组后半部分: 分析: 当然如果没有额外要求的话很容易实现,最好使用In-Place的实现策略:考虑插入排序 ...

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

    议题:快速排序实现之五(非递归实现,短序列优先处理,减少递归栈大小) 分析: 算法原理:此算法实现适用于系统栈空间不足够快速排序递归调用的需求,从而使用非递归实现快速排序算法:使用显示下推栈存储快速排 ...

  6. 笔试算法题(27):判断单向链表是否有环并找出环入口节点 & 判断两棵二元树是否相等

    出题:判断一个单向链表是否有环,如果有环则找到环入口节点: 分析: 第一个问题:使用快慢指针(fast指针一次走两步,slow指针一次走一步,并判断是否到达NULL,如果fast==slow成立,则说 ...

  7. 笔试算法题(19):判断两条单向链表的公共节点 & 字符集删除函数

    出题:给定两个单向链表的头结点,判断其是否有公共节点并确定第一个公共节点的索引: 分析: 由于是单向链表,所以每个节点有且仅有一个后续节点,所以只可能是Y型交叉(每条链表中的某个节点同时指向一个公共节 ...

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

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

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

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

随机推荐

  1. idea设置控制台不打印日志

    这样做的好处是当想打印数据到控制台查看就特别方便,这个在大数据spark sql使用的多.当然如果代码报错也会打印,这个不必担心. 方案Ⅰ 方法是将这个log日志文件放到idea的资源目录里即可 lo ...

  2. python 面向对象九 定制类

    一.定制类,实质就是我们自己重写特殊函数 看到类似__slots__这种形如__xxx__的变量或者函数名就要注意,这些在Python中是有特殊用途的. __slots__我们已经知道怎么用了,__l ...

  3. bzoj 1613: [Usaco2008 Jan]Running贝茜的晨练计划【dp】

    设f[i][j]为第i分钟疲劳j,从三种情况转移,记得休息的时候判断从i开始休息到n能不能恢复到疲劳0 #include<iostream> #include<cstdio> ...

  4. (8)css表格

    用css设置表格样式 *<table></table> 标签定义 HTML 表格. * tr 元素定义表格的行:th 元素定义表格的表头:td 元素定义表格中的单元格:capt ...

  5. Akka源码分析-Akka Typed

    对不起,akka typed 我是不准备进行源码分析的,首先这个库的API还没有release,所以会may change,也就意味着其概念和设计包括API都会修改,基本就没有再深入分析源码的意义了. ...

  6. SQL Server OPENQUERY使用

    以下以创建好的ORAC链接服务器为例: A. 执行 SELECT 传递查询 SELECT * FROM OPENQUERY(ORAC, 'SELECT ID,NAME FROM SCOTT.RYB') ...

  7. python 关闭redis的连接

    在python语言中使用redis时,没有找到对应的关闭的方法 try: self.redisconn = StrictRedisCluster(startup_nodes=self.redisNod ...

  8. BZOJ2333 棘手的操作

    Description ​ 有N个节点,标号从1到N,这N个节点一开始相互不连通.第i个节点的初始权值为a[i],接下来有如下一些操作: U x y: 加一条边,连接第x个节点和第y个节点 A1 x ...

  9. git介绍及安装

    git介绍 git是一个开源的分布式版本控制系统,用于敏捷高效的处理任何或大或小的项目.git是linus Torvalds为了帮助管理Linux内核开发的一个开放源码的版本控制软件. Git 与常用 ...

  10. Codeforces Round #138 (Div. 1)

    A 记得以前做过 当时好像没做对 就是找个子串 满足括号的匹配 []最多的 开两个栈模拟 标记下就行 #include <iostream> #include<cstring> ...