快速排序算法分析和实现
快速排序的思想:
选择一个基准元素,比基准元素小的放基准元素的前面,比基准元素大的放基准元素的后面,这种动作叫分区,每次分区都把一个数列分成了两部分,每次分区都使得一个数字有序,然后将基准元素前面部分和后面部分继续分区,一直分区直到分区的区间中只有一个元素的时候,一个元素的序列肯定是有序的嘛,所以最后一个升序的序列就完成啦
但是关于基准元素的选择有两种
1.第一种是每次都选序列的子序列的第一个元素(有缺陷)
2.第二种是每次都选择子序列中的第x个元素,x随机
普通快速排序
代码如下:
#include<bits/stdc++.h> using namespace std; #define n 5 int a[n]; void swap_t(int a[],int i,int j) { int t=a[i]; a[i]=a[j]; a[j]=t; } int par(int a[],int p,int q) { int i=p;//p是轴 int x=a[p];//基准元素 for(int j=p+1;j<=q;j++) { if(a[j]<=x)//升序 { i++; swap_t(a,i,j); } } swap_t(a,p,i); return i;//轴位置 } void QuickSort(int a[],int p,int q) { if(p<q)//序列中元素个数大于1 { int r=par(a,p,q); QuickSort(a,p,r-1); QuickSort(a,r+1,q); } } int main() { int i; for(i=0;i<n;i++) { scanf("%d",&a[i]); } QuickSort(a,0,n-1); for(i=0;i<n;i++) { printf("%d\n",a[i]); } return 0; }
算法分析:
1.最好情况:(每次分区时子序列中元素个数相同)
T(n)=2T(n/2)+O(n)=O(n*logn) log的底是2
2.最坏情况:序列是由大到小排序的
比如:7 6 5 4 3 2 1 -----> ( 进行依次分区,基准元素是7) 1 6 5 4 3 2 7,元素7的后面是没有元素的,同理 再将序列1 6 5 4 3 2分区,变成了1 2 5 4 3 6,分区6的后面也是没有元素的(7不属于这个分区),所以最坏情况的快速排序相当于冒泡排序了。。。。。。。有没有一种操蛋的感觉
T(n)=T(1)+T(n-1)+O(n)
=T(1)+T(1)+T(n-2)+T(n-1)+O(n-1)+O(n)
继续变形下去
T(常数)可以去掉
T(n)=O(1)+O(2)+.......+O(n)//等差数列求和
T(n)=O((1+n)*n/2)=n*n //常数可以省略
所以T(n)=n*n
3.一般情况:
T(n)=T(n/5)+T(4*n/5)+O(n) //假设前面分成1份后面分成4份这种情况
=T(n/25)+T(4*n/25)+O(n/5)+T(4*n/25)+T(16*n/25)+O(4*n/5)
我们假设T(n/5) 和T(4*n/5)展开成一颗树的形状,经过一系列比较复杂的推导之后(不好描述。。。。。)得到树的最大深度是log 5/4 n(底数的5/4),然后log 5/4 n 经过数学等价变形为log 2 n(底数为2)
然后又经过一系列复杂推导后(。。。。。。。。。。),树的最大宽度为:n
所以T(n)=n*log 2 n(底数为2)
总结:普通快排存在缺陷,缺陷在于每次基准元素的选择
二,改进之后的快速排序:随机化快速排序(每次随机在序列中选择基准元素),因为快排的性能取决于划分的对称性
附加知识:生成p,q之间的随机数:
方法:先生成随机数n,int i=n%(q-p+1)+p ,i就是p,q之间的随机数
代码如下:
#include<bits/stdc++.h> using namespace std; #define n 5 int a[n]; void swap_t(int a[],int i,int j) { int t=a[i]; a[i]=a[j]; a[j]=t; } int par(int a[],int p,int q) { int i=p;//p是轴 int x=a[p]; for(int j=p+1;j<=q;j++) { if(a[j]<=x) { i++; swap_t(a,i,j); } } swap_t(a,p,i); return i;//轴位置 } int Random(int p,int q) { return rand()%(q-p+1)+p; } int Randomizedpar(int a[],int p,int q) { int i=Random(p,q); swap_t(a,p,i);//第一个和第i个交换,相当于有了一个随机基准元素 return par(a,p,q); } void RandomizedQuickSort(int a[],int p,int q) { if(p<q) { int r=Randomizedpar(a,p,q); printf("%d到%d之间的随机数:%d\n",p,q,r); RandomizedQuickSort(a,p,r-1); RandomizedQuickSort(a,r+1,q); } } int main() { int i; for(i=0;i<n;i++) { scanf("%d",&a[i]); } RandomizedQuickSort(a,0,n-1); for(i=0;i<n;i++) { printf("%d\n",a[i]); } return 0; }
总结:快排虽然不稳定,但是高效(关于不稳定的概念请百度。。。)
有错误的地方麻烦大佬们指正!!!