数据结构-快速排序的三种实现方式及其优化
来源:互联网 发布:e.abchina.com js 编辑:程序博客网 时间:2024/05/29 04:44
快速排序初步了解:
快速排序是由东尼·霍尔所发展的一种排序算法。在平均状况下,排序 n 个项目要Ο(n log n)次比较。在最坏状况下则需要Ο(n2)次比较,但这种状况并不常见。后面将提供一种快排的优化方式可以尽量避免出现Ο(n2)的复杂度。
基本思路
1.先从数据当中找出一个数据作为参照数
2.然后开始分区操作,大于参照数的就放到参照数右边,小于参照数的就放到参照数的左边.
3.然后对左右分区继续进行该操作,知道区间里面只有一个数据的是时候,快排完成
排序效果:
一、左右指针法
<1>思路:给定一个数组,再给定左右区间的下标值left,right(此处我给的是左右闭区间),给定一个基准值为arr[right],接下来就是循环和递归的过程。单趟过程中,先让begin记录该区间的第一个下标(就像一个指针指向头一样),end记录最后一个值的下标,基准值为该区间的最后一个值。循环过程中,begin记录比基准值大的值,end记录比基准值小的值,判断如果begin和end还未相遇,则交换对应的值,再进行循环直至begin和end相遇。相遇后此时单趟排序完成,现在begin和end指向一个元素,该元素左侧都是比基准值小的值,右侧都是比基准值大的,将begin对应的值与基准值进行交换,开始递归左半区间和右半区间,重复上述过程。
<2>图示单趟过程:
整体过程如下:
1 5 6 2 7 4
1 2 6 5 7 4
1 2 4 5 7 6
1 2 4 5 6 7
<3>实现代码
//左右指针法void LRPoint(int* arr, int left, int right){ if (left >= right) return; assert(arr); int begin = left; int end = right; int key = arr[end]; while (begin < end) { while (begin < end && arr[begin] <= key) begin++; while (begin < end && arr[end] >= key) end--; if (begin < end) swap(arr[begin], arr[end]); } //begin end指向一起,与key交换 swap(arr[begin], arr[right]); LRPoint(arr, left,begin - 1); LRPoint(arr, begin+1,right);}
二、挖坑法
<1>思路:大体上如同左右指针法,但是左右指针法是在begin和end符合条件的值后,将begin和end对应的值进行交换;而挖坑法则是边找边替换,在最后再将key填入坑中。图中橙色元素由于此处的值赋值到了别处,可以形象的看做是一个“坑”。
<2>图示单趟过程:
整体过程如下:
1 5 6 2 7 4
1 5 6 2 7 5
1 2 6 2 7 5
1 2 6 6 7 5
1 2 4 6 7 5
1 2 4 6 7 6
1 2 4 5 7 6
1 2 4 5 7 7
1 2 4 5 6 7
<3>实现代码
void PitPoint(int* arr, int left, int right){ if (left >= right) return; int begin = left; int end = right; int key = arr[right]; while (begin < end) { while (begin < end && arr[begin] <= key) begin++; arr[end] = arr[begin]; while (begin < end && arr[end] >= key) end--; arr[begin] = arr[end]; } arr[end] = key; PitPoint(arr, left, begin - 1); PitPoint(arr, begin + 1, right);}
三、前后指针法
<1>思路:一开始使用cur记录left位置,prev记录cur的前一个位置,利用cur来寻找比基准值小的值,需要注意的是只有当arr[cur] < arr[right]和++prev != cur两个条件都满足才进行交换,此处还涉及如果arr[cur] < arr[right]不满足则不进行prev++,当条件都满足时进行交换,出了循环后将基准值放在prev++的位置。看着代码自己缕一遍就会发现规律了。
<2>图示单趟过程:
整体过程如下:
1 5 6 2 7 4
1 2 6 5 7 4
1 2 4 5 7 6
1 2 4 5 6 7
<3>实现代码
void FBPoint(int* arr, int left, int right){ if (left >= right) return; int cur = left; int prev = left - 1; while (cur < right) { if (arr[cur] < arr[right] && ++prev != cur) swap(arr[cur], arr[prev]); ++cur; } swap(arr[++prev], arr[right]); FBPoint(arr, left, prev - 1); FBPoint(arr, prev + 1, right);}
四、非递归实现
<1>思路:
用栈来模拟实现递归操作,每次从栈中取出对应的左右边界进行操作;在push边界的顺序与取栈顶数据作为左右边界的顺序要对应。
<2>实现代码:
void NRQuickSort(int* arr, int sz){ assert(arr); stack<int> s; int left = 0; int right = sz; s.push(right); s.push(left); while (!s.empty()) { left = s.top(); s.pop(); right = s.top(); s.pop(); if (left < right) { int begin = left; int end = right; int key = arr[right]; while (begin < end) { //1,5,6,2,7,4 while (begin < end && arr[begin] <= key) begin++; while (begin < end && arr[end] >= key) end--; if (begin < end) swap(arr[begin], arr[end]); } swap(arr[begin], arr[right]); s.push(right); s.push(begin + 1); s.push(begin - 1); s.push(left); } }}
五、优化
<1>通过三数取中法选取key的下标:
当按照上面的代码如果每次取key的时候取到了待排序列中最大的或者最小的. 也就是序列有序的时候,快速排序的时间复杂度为O(N^2),我们可以通过选择合适的key值来进行优化:也就是所谓的三数取中法(给定左右区间的下标,计算出中间下标,并返回这三个值中不大不小的那个),将该下标所对应的值和区间最右侧的值进行交换即可。拿左右指针法优化举例:
int GetMid(int* a, int left, int right){ int mid = left + ((right - left) >> 1); // left mid if (a[left] < a[mid]) { if (a[right] < a[left]) return left; else if (a[right] > a[mid]) return mid; else return right; } else { //mid left if (a[right] < a[mid]) return mid; else if (a[right] < a[left]) return left; else return right; }}void LRPoint(int* arr, int left, int right){ if (left >= right) return; assert(arr); int begin = left; int end = right; swap(arr[end], arr[GetMid(arr, left, right)]); int key = arr[end]; while (begin < end) { while (begin < end && arr[begin] <= key) begin++; while (begin < end && arr[end] >= key) end--; if (begin < end) swap(arr[begin], arr[end]); } //begin end指向一起,与key交换 swap(arr[begin], arr[right]); LRPoint(arr, left,begin - 1); LRPoint(arr, begin+1,right);}
<2>小区间优化:
思路:由于递归需要建立栈帧消耗十分大,我们可以判断如果该区间内的元素值小于n(可以取5~15,视情况而定)的时候直接采取插入排序来处理该区间内的数据,就不必再多建立那么多栈帧,从而提高效率。具体实现就不给出了,只需要添加一个判断条件再引入插入排序即可。
阅读全文
1 0
- 数据结构-快速排序的三种实现方式及其优化
- 快速排序的三种不同的实现方式。
- 快速排序的三种实现及两种优化
- 三种快速排序的方法及其快排的优化
- 三种快速排序以及快速排序的优化
- 三种快速排序以及快速排序的优化
- 三种快速排序以及快速排序的优化
- 三种快速排序算法以及快速排序的优化
- 三种快速排序算法以及快速排序的优化
- 三种快速排序以及快速排序的优化
- 三种快速排序算法以及快速排序的优化
- 三种快速排序以及快速排序的优化
- 三种快速排序以及快速排序的优化
- 三种快速排序以及快速排序的优化
- 三种快速排序算法以及快速排序的优化
- 三种快速排序以及快速排序的优化
- 三种快速排序算法以及快速排序的优化
- 三种快速排序以及快速排序的优化
- JDK5至JDK8的新特性
- 利用阴影可以做背景的渐变色
- (3)树莓派3b命令行版网易云音乐
- Git远程操作详解
- 常见的排序算法总结
- 数据结构-快速排序的三种实现方式及其优化
- 高级语言多态对比
- 【AGC018F】Two Trees 构造 黑白染色
- spring-mvc-mybatis web子目录xml文件
- linux安装SSH+允许root用户远程登录
- Tensorflow爬过的坑
- 通俗易懂的_A*
- 编写优质嵌入式C程序(转)
- XML约束