【快排算法,源自算法导论】
PARTITION(A,p,r) x = A[r] i = p-1 for j = p to r-1 if A[j] <= x i = i+1 exchange (A[i],A[j]) exchange(A[i+1],A[r]) return i+1 QUICKSORT(A,p,r) if p < r q = PARTITION(A,p,r) QUICKSORT(A,p,q-1) QUICKSORT(A,q+1,r)
【随机化版本】
就能降低遇到worst_case的概率了(而不是降低worst_case的时间复杂度)
PARTITION(A,p,r) x = A[r] i = p-1 for j = p to r-1 if A[j] <= x i = i+1 exchange (A[i],A[j]) exchange(A[i+1],A[r]) return i+1 RANDOM_PARTITION(A,p,r) i = RANDOM(p,r) exchange(A[i],A[r]) return PARTITION(A,p,r) QUICKSORT(A,p,r) if p < r q = RANDOM_PARTITION(A,p,r) QUICKSORT(A,p,q-1) QUICKSORT(A,q+1,r)
【理解】
(1)这是个不稳定的排序,因为两个同样的值,可能排完序后顺序变了。
比如说1,2,3,2,5 ,为了讲解方便,我们令第一个2为2[1],第二个2为2[2],快排Partition()中如果我把2[1]当成x,让其与A[r]交换
程序往下走,然后遇到2[2],因为2[2] <= x ,那么 i++ , exchange(A[i],A[j] ),那么2[2]就跑到前面去了,循环结束后exchange(A[i+1],A[r])
把2[1]放到A[i+1]的位置,这个位置在2[2]后,你看,排完序,两个相同值可能顺序就变了。
(2)这是个最好只有O(nlogn)的排序,最坏的情况就是排完序的情况,为O(n^2)
它把一个序列划分两段,一共有logn次划分。
二分能够降低时间复杂度,O(n) 变成O(logn) ,O(n^2) 变成O(nlogn)
如果排完序呢,就没办法二分了,或者说二分效果不大,就没办法把其中一个n给降到logn,所以是n^2
【C++ 代码一】
这个是2013/4/5写的
#include <iostream> #include <stdio.h> #include <cstring> #include <math.h> #include <time.h> #include <cstdlib> using namespace std; const int N = 1000; int Arr[N]; void exchange(int &a,int &b) { if(a != b) { a=a+b; b=a-b; a=a-b; } } int Partition(int *A,int p,int r) { try { int i=p-1; int x = A[r]; for(int j=p;j<=r-1;j++) if(A[j] <= x) exchange(A[++i],A[j]); exchange(A[++i],A[r]); return i; } catch(...) { cout<<"extend the array index..."<<endl; return -1; } } int random_partition(int *A,int p,int r) { int i=rand()%(r-p+1)+p; exchange(A[i],A[r]); return Partition(A,p,r); } void QuickSort(int *A,int p,int r) { if(p<r) { int x = random_partition(A,p,r); QuickSort(A,p,x-1); QuickSort(A,x+1,r); } } int main() { int num=5; srand((unsigned)time(NULL)); for(int i=1;i<=num;i++) Arr[i] = rand()%100; cout<<"before quick sort:"<<endl; for(int i=1;i<=num;i++) cout<<Arr[i]<<" "; cout<<endl; QuickSort(Arr,1,num); cout<<"after quick sort:"<<endl; for(int i=1;i<=num;i++) cout<<Arr[i]<<" "; cout<<endl; return 0; } /* 实验结果: before quick sort: 9 37 5 11 93 after quick sort: 5 9 11 37 93 */
【C++代码二】
这个是2011写的,废话有点多。。
C++代码 //随机化版本会降低遇到worst_case的概率,而不是降低worst_case的时间复杂度 //code for testing QuickSort by ouyang #include <iostream> #include <time.h> //time()函数需要 #include <stdlib.h> //srand()函数需要 using namespace std; /*传址交换两个int型整数*/ void Exchange(int *a,int *b) { int temp=*a; *a=*b; *b=temp; } /*产生随机数random_number*/ int Random(int p,int r) { int random_number; srand((time(0))); if(p<r) random_number=rand()%(r-p+1)+p; return random_number; } /*对从p到r的p-r+1个数排序*/ int Partition(int *a,int p,int r) { //这个函数是快排的精华所在,对p到r这r-p+1个数排序 //它会划分三个区域: //1.小于x的(从头至i所指向处) //2.正在排的(j) //3.大于x的(从i+1至j-1所指向处) //如果j小于等于阈值x(也就是被选定的a[r]),那么a[j]和a[++i]交换后: //1.从头到i(注意i已经加1了)所指处,依旧保持小于x //2.从i+1到j-1(注意j在循环中已经加1了)所指处,依旧保持大于x //3.j依旧是正在排的 //如果j大于阈值x,那么: //1.从头到i(i没有自加1)所指处依旧是小于x //2.从i+1到j-1(注意j在循环中已经加1了)依旧是大于x //3.j依旧是正在排的 int i=p-1; int x=a[r];//阈值x for(int j=p; j<=r-1; j++) //因为阈值就是a[r],所以j==r-1即可 { if(a[j]<=x) { i++; Exchange(&a[j],&a[i]); } } //和阈值交换之后,依旧保持着 //1.从头到i处小于x //2.(i+1)+1到r-1都是大于x //3.i+1处就是阈值x Exchange(&a[i+1],&a[r]); return i+1; } /*Partition的随机化版本*/ int Randomized_Partition(int *A,int p,int r) { int i=Random(p,r); Exchange(&A[r],&A[i]); return Partition(A,p,r); } /*快速排序*/ void QuickSort(int *a,int p,int r) { if(p<r) { int q=Randomized_Partition(a,p,r); //q用来将序列分为三部分:(p,q),q,(q+1,r) QuickSort(a,p,q-1); QuickSort(a,q+1,r); } } int main() { int a[10]= {NULL,11,22,33,44,15,226,7,8,9}; QuickSort(a,1,9); for(int i=1; i<=9; i++) cout<<a[i]<<" "; cout<<endl; return 0; }
【非递归版本】
2013/4/5,修改
非递归效率可能更高些。不过个人还是比较递归版本。
用stack保存每一段的首尾号low && high
然后取出一对并处理
#include <iostream> #include <stdio.h> #include <cstring> #include <math.h> #include <time.h> #include <cstdlib> #include <stack> using namespace std; const int N = 1000; int Arr[N]; int Partition(int *A,int low,int high) { int x=A[low]; while(low < high) { while(low < high && A[high]>=x) high--; A[low]=A[high]; while(low < high && A[low]<x) low++; A[high]=A[low]; } A[low]=x; return low; } void QuickSort_NonRecurrence(int* A,int low,int high) { stack<int> s; if(low < high) { s.push(low); s.push(high); //实际上就是保存low high 这一对序列号 //别把low high 弄错顺序了,先压人low,则先取出high while(!s.empty()) { int q = s.top(); s.pop();//high int p = s.top(); s.pop();//low int pqmid = Partition(A,p,q); if(p < pqmid-1) { s.push(p); s.push(pqmid-1); } if(pqmid+1 <q) { s.push(pqmid+1); s.push(q); } } } } int main() { int num=5; srand((unsigned)time(NULL)); for(int i=1;i<=num;i++) Arr[i] = rand()%100; cout<<"before quick sort:"<<endl; for(int i=1;i<=num;i++) cout<<Arr[i]<<" "; cout<<endl; QuickSort_NonRecurrence(Arr,1,num); cout<<"after quick sort:"<<endl; for(int i=1;i<=num;i++) cout<<Arr[i]<<" "; cout<<endl; return 0; } /* 实验结果: before quick sort: 76 15 99 30 43 after quick sort: 15 30 43 76 99 */