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

分析:

  • 算法原理:主要由两部分组成,一部分是递归部分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. Jmeter压测Thrift服务接口

    此文已由作者夏鹏授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. Apache Jmeter是基于Java开发的性能测试工具,支持多种协议的测试,包括:Web(HTTP/HTT ...

  2. Kerberos原理和基础小结

    此篇文章仅做Kerberos的基本原理和基本使用做说明,本人对Kerberos了解有限,也是通过大量英文文档中翻译过来, 加上自己对Kerberos的理解所写,本人英文太菜,看文档看的头昏眼花若有写的 ...

  3. MoveTo和LineTo函数的意思

    这是个画线函数, moveto是移动到某个坐标,lineto是从当前坐标, 移动的某个坐标连接早当前坐标.这两个函数加起来就是画一条直线.

  4. VK Cup 2018 - Round 1 A. Primal Sport

    A. Primal Sport time limit per test 1.5 seconds memory limit per test 256 megabytes input standard i ...

  5. (二)python高级特性

    一.切片 >>> L = ['Michael', 'Sarah', 'Tracy', 'Bob', 'Jack'] 对这种经常取指定索引范围的操作,用循环十分繁琐,因此,Python ...

  6. *关于TCP长连接,NAT超时,心跳包

    参考: http://www.jianshu.com/p/584707554ed7 1.TCP长连接 TCP连接建立后只要不明确关闭,逻辑上连接一直存在. TCP是有保活定时器的,可以打开保活定时器来 ...

  7. hdu3433A Task Process( 二分dp)

    链接 二分时间,在时间内dp[i][j]表示截止到第i个人已经做了j个A最多还能做多少个B #include <iostream> #include<cstdio> #incl ...

  8. css靠左,靠右

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  9. swiper4实现的拥有header和footer的全屏滚动demo/swiper fullpage footer

    用swiper4实现的拥有header和footer的全屏滚动demo, <!DOCTYPE html> <html lang="en"> <head ...

  10. Android一句代码给Activity定制标题栏

    在此之前,使用过几种方法设置标题栏: 1.常规法:这个方法是最常用的了,哪个activity需要什么样的标题栏,就在对应的xml布局设计.缺点:此方法维护起来困难,没有将标题栏的共性抽取出来, 如果要 ...