快速排序实现以及时间复杂度分析
2017-09-30 22:55
465 查看
原文:http://www.cnblogs.com/fengty90/p/3768827.html
之前只知道快速排序的平均时间复杂度为O(n×log(n)),最糟糕时复杂度为O(n^2),但却不知道具体原因,今天好好证明一下,最后部分摘自《算法导论》。
首先再介绍一遍快排的思想:
通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序的目的。
在最优情况下,Partition每次都划分得很均匀,如果排序n个关键字,其递归树的深度就为 [log2n]+1( [x] 表示不大于 x 的最大整数),即仅需递归 log2n 次,需要时间为T(n)的话,第一次Partiation应该是需要对整个数组扫描一遍,做n次比较。然后,获得的枢轴将数组一分为二,那么各自还需要T(n/2)的时间(注意是最好情况,所以平分两半)。于是不断地划分下去,就有了下面的不等式推断:
n/4 n/8都是等于1,所以式子转化为nT(1)
这说明,在最优的情况下,快速排序算法的时间复杂度为O(nlogn)。
然后再来看最糟糕情况下的快排,当待排序的序列为正序或逆序排列时,且每次划分只得到一个比上一次划分少一个记录的子序列,注意另一个为空。如果递归树画出来,它就是一棵斜树。此时需要执行n‐1次递归调用,且第i次划分需要经过n‐i次关键字的比较才能找到第i个记录,也就是枢轴的位置,因此比较次数为 ,最终其时间复杂度为O(n^2)。
最后来看一下一般情况,平均的情况,设枢轴的关键字应该在第k的位置(1≤k≤n),那么:
本来想按这种思路推导出来,结果发现半天推不出结果,最后去翻阅《算法导论》7.4节,发现证明过程还是蛮复杂的,我就偷懒贴一下好了~
下面参考: http://m.blog.csdn.net/yuanba_xs/article/details/61932665
快速排序思想:
1.选择数组左边第一个元素为枢轴pivot,把数组所有元素比pivot大的放在数组右边,比pivot小的放在左边
(复杂度为O(n)欧(n))
2.对pivot左右两边的序列分别进行快速排序。
平均时间复杂度分析:
T(1) = 1;(1个时间单位(比如计算一个数排序需要的时间))
T(n) = 2*T(n/2) + a*n;(a为常数,每次合并时,复杂度为O(n))
= 2*(2*T(n/4)+a*n/2) + a*n
= 4*T(n/4) + 2*a*n
= 4*(2*T(n/8)+a*n/4) + 2*a*n
= 8*T(n/8) + 3*a*n
=......
= 2^k*T(1) + k*a*n (其中n==2^k,即k=log2(n))
= n + a*n*log2(n);
所以时间复杂度为O(nlogn) 欧(nlogn)
注意:对左右分别快排时,可能出现一遍元素个数为0,这是最坏情况,此时时间复杂度为O(n^2)
之前只知道快速排序的平均时间复杂度为O(n×log(n)),最糟糕时复杂度为O(n^2),但却不知道具体原因,今天好好证明一下,最后部分摘自《算法导论》。
首先再介绍一遍快排的思想:
通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序的目的。
1、最优情况
在最优情况下,Partition每次都划分得很均匀,如果排序n个关键字,其递归树的深度就为 [log2n]+1( [x] 表示不大于 x 的最大整数),即仅需递归 log2n 次,需要时间为T(n)的话,第一次Partiation应该是需要对整个数组扫描一遍,做n次比较。然后,获得的枢轴将数组一分为二,那么各自还需要T(n/2)的时间(注意是最好情况,所以平分两半)。于是不断地划分下去,就有了下面的不等式推断:n/4 n/8都是等于1,所以式子转化为nT(1)
这说明,在最优的情况下,快速排序算法的时间复杂度为O(nlogn)。
2、最糟糕情况
然后再来看最糟糕情况下的快排,当待排序的序列为正序或逆序排列时,且每次划分只得到一个比上一次划分少一个记录的子序列,注意另一个为空。如果递归树画出来,它就是一棵斜树。此时需要执行n‐1次递归调用,且第i次划分需要经过n‐i次关键字的比较才能找到第i个记录,也就是枢轴的位置,因此比较次数为 ,最终其时间复杂度为O(n^2)。
3、一般情况
最后来看一下一般情况,平均的情况,设枢轴的关键字应该在第k的位置(1≤k≤n),那么:本来想按这种思路推导出来,结果发现半天推不出结果,最后去翻阅《算法导论》7.4节,发现证明过程还是蛮复杂的,我就偷懒贴一下好了~
下面参考: http://m.blog.csdn.net/yuanba_xs/article/details/61932665
快速排序思想:
1.选择数组左边第一个元素为枢轴pivot,把数组所有元素比pivot大的放在数组右边,比pivot小的放在左边
(复杂度为O(n)欧(n))
2.对pivot左右两边的序列分别进行快速排序。
平均时间复杂度分析:
T(1) = 1;(1个时间单位(比如计算一个数排序需要的时间))
T(n) = 2*T(n/2) + a*n;(a为常数,每次合并时,复杂度为O(n))
= 2*(2*T(n/4)+a*n/2) + a*n
= 4*T(n/4) + 2*a*n
= 4*(2*T(n/8)+a*n/4) + 2*a*n
= 8*T(n/8) + 3*a*n
=......
= 2^k*T(1) + k*a*n (其中n==2^k,即k=log2(n))
= n + a*n*log2(n);
所以时间复杂度为O(nlogn) 欧(nlogn)
注意:对左右分别快排时,可能出现一遍元素个数为0,这是最坏情况,此时时间复杂度为O(n^2)
#include <iostream> #include <cstdio> #include <algorithm> #define N 10005 using namespace std; int a ; void Qsort(int a[], int s, int e) { if (s >= e)//待排元素个数<1 return; int pivot = a[s], i = s, j = e; while (i < j) { while (i < j && a[j] >= pivot) j--; swap(a[i], a[j]); while (i < j && a[i] <= pivot) i++; swap(a[i], a[j]); } Qsort(a, s, i - 1); Qsort(a, i + 1, e); } int main() { int n; scanf("%d", &n); for (int i = 0; i < n; i++) scanf("%d", &a[i]); Qsort(a, 0, n - 1);//待排数组,以及序列下标始末位置。 for (int i = 0; i < n; i++) printf("%d ", a[i]); printf("\n"); return 0; }
相关文章推荐
- 排序算法——快速排序的图解、代码实现以及时间复杂度分析
- java版排序算法简介及冒泡排序以及优化,选择排序,直接插入排序,希尔排序,堆排序,快速排序及其优化前言 2 分类 2 稳定性 3 时间复杂度 4 Java实现版本 5 1、冒泡排序 6 2、选择排序
- 排序算法——希尔排序的图解、代码实现以及时间复杂度分析
- 排序算法的C语言实现以及各个算法的时间复杂度和空间复杂度分析(冒泡排序)
- 排序算法——插入排序的图解、代码实现以及时间复杂度分析
- c语言实现线性表的建立,初始化,插入,删除,查找,遍历以及时间复杂度分析
- 快速排序算法原理,实现,以及时间复杂度分析
- 插入排序和归并排序实现以及时间复杂度分析
- Java快速排序的实现和时间空间复杂度分析
- 借鉴快速排序的思想,实现算法将整型数组a[0...n]分成两块,使得第一块元素均大于等于0,第二块的元素均小于0,要求算法原地工作且时间复杂度为O(n)
- 找出数组中第k大的数(时间复杂度分析、C++代码实现). TopK in array. ( leetcode - 215 )
- 浅谈直接插入排序算法思想以及时间复杂度分析
- 快速排序和冒泡排序的时间复杂度分析(C++算法实现对比)
- 堆排序算法原理,实现及时间复杂度分析
- 归并排序实现及时间复杂度分析
- 快速排序及时间复杂度分析
- MIT:算法导论——4.2快速排序 以及 排序算法时间复杂度分析
- 算法 插入排序 的 JS实现及时间复杂度分析
- 斐波那契数列算法的三种C#实现及时间复杂度分析
- 约瑟夫问题的数学角度分析 C 数组实现 循环链表实现 递归实现时间复杂度O(logN)