快速排序的复杂度分析以及使用插入排序优化的快速排序
2017-08-11 23:13
681 查看
首先,对快速排序分析可知,在一个包含n个元素的数组上运行快速排序时,记总共的两个元素之间的比较次数为X,则快速排序的运行时间为O(n+X)(算法导论第三版7.4.2节引理7.1)。因此,必须了解算法在什么时候对数组中的两个元素进行比较,什么时候不进行比较。
将数组A中的各个元素重新命名为z1,z2,z3,...,zn,其中zi是数组中第i小的元素。定义Zij=zi,zi+1,zi+2,...,zj为zi和zj之间(含i和j)的元素集合。定义指示器随机变量:
Xij=I{zi与zj进行比较}
则算法的总共的比较次数为:
X=∑n−1i=1∑nj=i+1Xij
对上述式子两边取期望可得:
E[X]=E[∑n−1i=1∑nj=i+1Xij]=∑n−1i=1∑nj=i+1E[Xij]=∑n−1i=1∑nj=i+1Pr{zi与zj进行比较}
通常,假设每个元素的值是互异的,因此,一旦一个满足zi<x<zj的主元被选择后,zi和zj就再也不可能被比较了,因为zi和zj已经被划分到两个不同的划分中。然而,若是zi在Zij中的其他所有元素之前被选为主元,那么zi将和Zij中除了它自身之外的所有元素进行比较。因此,zi和zj当且仅当Zij中第一个被选为主元的元素是zi或者zj。
接着计算这一事件发生的概率。在任意一个Zij内的元素被选择为主元之前,整个集合均在一个划分的同一个分区之中。因此,每个Zij中的元素被选为主元的概率相等。因为集合中有j−i+1个元素,并且主元的选择是独立且随机的,所以任何元素首先被选为主元的概率为1j−i+1。于是:
Pr{zi于zj相比较}=Pr{zi或者zj是集合中首先被选择作为主元的元素}=2j−i+1
所以有:
E[X]=∑n−1i=1∑nj=i+12j−i+1
所以,可以有:
E[X]=∑n−1i=1∑nj=i+12j−i+1=∑n−1i=1∑n−ik=12k+1<∑n−1i=1∑n−ik=12k=∑n−1i=1O(lgn)=O(nlgn)
由此可得,在输入元素均不相同的情况下,随机化的快速排序算法的期望运行时间是O(nlgn)。
对于上式,又有:
E[X]=∑n−1i=1∑nj=i+12j−i+1=∑n−1i=1∑n−ik=12k+1≥∑n−1i=1∑n−ik=122k≥∑n−1i=1Ω(lgn)=Ω(nlgn)
所以,又有,随机化的快速排序算法的期望运行时间为Ω(nlgn)。由上可知,随机化的快速排序算法的期望运行时间为Θ(nlgn)。
当输入数据已经几乎有序时,插入排序的速度很快。在实际应用中,可以利用这一特点来提高快速排序的速度。当对一个长度小于k的子数组调用快速排序时,可以让它不做任何处理就返回。当上层快速排序调用返回后,对整个数组进行插入排序来完成排序过程。该算法的期望时间复杂度为O(nk+nlg(n/k))。该算法的时间复杂度分析如下:
对于快速排序结束后的插入排序,假设每一小结的插入排序时间复杂度为O(k^2),则n/k个小结的时间复杂度为nkO(k2)=O(nk)。
对于随机的快速排序过程,时间复杂度是O(nlg(n/k)),网上很多解答说这个利用指示器随机变量,然后计算j与i的距离大于k的均值即可,但是笔者认为,在划分过程中,j于i之间的距离小于k时的比较也可能出现,所以不可以单单这样计算,然而想不出正确解法,故留待之后解决。
C语言实现优化的快速排序如下:
将数组A中的各个元素重新命名为z1,z2,z3,...,zn,其中zi是数组中第i小的元素。定义Zij=zi,zi+1,zi+2,...,zj为zi和zj之间(含i和j)的元素集合。定义指示器随机变量:
Xij=I{zi与zj进行比较}
则算法的总共的比较次数为:
X=∑n−1i=1∑nj=i+1Xij
对上述式子两边取期望可得:
E[X]=E[∑n−1i=1∑nj=i+1Xij]=∑n−1i=1∑nj=i+1E[Xij]=∑n−1i=1∑nj=i+1Pr{zi与zj进行比较}
通常,假设每个元素的值是互异的,因此,一旦一个满足zi<x<zj的主元被选择后,zi和zj就再也不可能被比较了,因为zi和zj已经被划分到两个不同的划分中。然而,若是zi在Zij中的其他所有元素之前被选为主元,那么zi将和Zij中除了它自身之外的所有元素进行比较。因此,zi和zj当且仅当Zij中第一个被选为主元的元素是zi或者zj。
接着计算这一事件发生的概率。在任意一个Zij内的元素被选择为主元之前,整个集合均在一个划分的同一个分区之中。因此,每个Zij中的元素被选为主元的概率相等。因为集合中有j−i+1个元素,并且主元的选择是独立且随机的,所以任何元素首先被选为主元的概率为1j−i+1。于是:
Pr{zi于zj相比较}=Pr{zi或者zj是集合中首先被选择作为主元的元素}=2j−i+1
所以有:
E[X]=∑n−1i=1∑nj=i+12j−i+1
所以,可以有:
E[X]=∑n−1i=1∑nj=i+12j−i+1=∑n−1i=1∑n−ik=12k+1<∑n−1i=1∑n−ik=12k=∑n−1i=1O(lgn)=O(nlgn)
由此可得,在输入元素均不相同的情况下,随机化的快速排序算法的期望运行时间是O(nlgn)。
对于上式,又有:
E[X]=∑n−1i=1∑nj=i+12j−i+1=∑n−1i=1∑n−ik=12k+1≥∑n−1i=1∑n−ik=122k≥∑n−1i=1Ω(lgn)=Ω(nlgn)
所以,又有,随机化的快速排序算法的期望运行时间为Ω(nlgn)。由上可知,随机化的快速排序算法的期望运行时间为Θ(nlgn)。
当输入数据已经几乎有序时,插入排序的速度很快。在实际应用中,可以利用这一特点来提高快速排序的速度。当对一个长度小于k的子数组调用快速排序时,可以让它不做任何处理就返回。当上层快速排序调用返回后,对整个数组进行插入排序来完成排序过程。该算法的期望时间复杂度为O(nk+nlg(n/k))。该算法的时间复杂度分析如下:
对于快速排序结束后的插入排序,假设每一小结的插入排序时间复杂度为O(k^2),则n/k个小结的时间复杂度为nkO(k2)=O(nk)。
对于随机的快速排序过程,时间复杂度是O(nlg(n/k)),网上很多解答说这个利用指示器随机变量,然后计算j与i的距离大于k的均值即可,但是笔者认为,在划分过程中,j于i之间的距离小于k时的比较也可能出现,所以不可以单单这样计算,然而想不出正确解法,故留待之后解决。
C语言实现优化的快速排序如下:
void insertion_sort(int *source, int head, int tail) { if (head > tail) { fprintf(stderr, "Error head and tail\n"); return; } int i = head + 1; int j = head; int temp; for (i = head + 1; i <= tail; i++) { temp = source[i]; for (j = i - 1; j >= head && source[j] > temp; j--) { source[j + 1] = source[j]; } source[j + 1] = temp; } } int randomized_partition(int *source, int head, int tail) { swap(source + rand() % (tail - head + 1) + head, source + tail); int x = source[tail]; int i = head - 1, j = head; for (j = head; j < tail; j++) { if (source[j] <= x) { i++; swap(source + j, source + i); } } swap(source + tail, source + i + 1); return i + 1; } void limited_quick_sort(int *source, int head, int tail, int k) { if (tail - head >= k) return; int mid = randomized_partition(source, head, tail); limited_quick_sort(source, head, mid - 1, k); limited_quick_sort(source, mid + 1, tail, k); } void insertion_optimized_quick_sort(int *source, int head, int tail, int k) { limited_quick_sort(source, head, tail, k); insertion_sort(source, head, tail); }
相关文章推荐
- 排序-快速排序-优化-使用插入排序
- 高级排序-快速排序-使用插入排序来处理小于10个数据项的子数组,使快速排序性能发挥到极致。
- 元素排序几种常用的排序算法的分析及java实现(希尔排序,堆排序,归并排序,快速排序,选择排序,插入排序,冒泡排序)
- 经典排序算法设计与分析(插入排序、冒泡排序、选择排序、shell排序、快速排序、堆排序、分配排序、基数排序、桶排序、归并排序)
- 几种常用的排序算法的分析及java实现(希尔排序,堆排序,归并排序,快速排序,选择排序,插入排序,冒泡排序)
- MIT:算法导论——4.2快速排序 以及 排序算法时间复杂度分析
- 快速排序(二) jdk源码中如何优化快速排序
- 排序系列--快速排序(实现+复杂度分析)
- 快速排序原理、复杂度分析及C语言实现
- java版排序算法简介及冒泡排序以及优化,选择排序,直接插入排序,希尔排序,堆排序,快速排序及其优化前言 2 分类 2 稳定性 3 时间复杂度 4 Java实现版本 5 1、冒泡排序 6 2、选择排序
- 排序算法的C++实现与性能分析(插入排序、归并排序、快速排序、STOOGE排序、堆排序)
- leetcode:Kth Largest Element in an Array 使用快速选择算法,以及对其复杂度的分析
- 快速排序 优化 详细分析
- 经典排序算法设计与分析(插入排序、冒泡排序、选择排序、shell排序、快速排序、堆排序、分配排序、基数排序、桶排序、归并排序)
- 快速排序的分析与优化
- 【编程珠玑】第十一章 排序 (插入排序和快速排序的深度优化)
- 浅谈C++之冒泡排序、希尔排序、快速排序、插入排序、堆排序、基数排序性能对比分析(好戏在后面,有图有真相)
- 快速排序的分析与优化
- 几种常用的排序算法的分析及java实现(希尔排序,堆排序,归并排序,快速排序,选择排序,插入排序,冒泡排序)
- 浅谈C++之冒泡排序、希尔排序、快速排序、插入排序、堆排序、基数排序性能对比分析(好戏在后面,有图有真相)