一、10大排序算法
二、快速排序原理
快速排序为什么快的一个主要原因就是:通过选主元,进行过子集划分,主元一次性就被放到了正确的位置上了
算法思想与步骤
1.选主元,藏一边
2.进行子集划分,划分过程中会进行交换,保证主元左侧元素比主元小,右侧元素比主元大
3.对子集进行递归调用
三、细节问题
问题1
关于这个问题,我一开始认为最好情况是初始就是有序的,实际上初始有序,主元要是选一端的话时间复杂度会是O(N^2),原因可见下图:(这里的时间复杂度分析是按照递推的方法进行的,其中O(n)指每次选主元的都要遍历下数据,所以为O(n)).也就是说原先是有序的,且每次主元都选在了一端(子集划分时偏向一侧).反而是最坏的情况.
最好的情况:每次选的主元都能将序列大致等分
问题2
为什么呢?
举一个极端的例子,如果一个序列都是相等的,比如十个1,
如果交换,那么坏处是:进行了很多无畏的交换;好处:每次交换完成后,首尾的指针都会继续前进,最后两个子序列基本都是等分的,这样时间复杂度就会是O(nlog(n))
如果不交换,那么好处就是,不用进行无畏的交换了,坏处就是,主元每次都选在了一端,时间复杂度就是O(n^2)
问题3、小规模数据处理问题
快速排序的一个问题是:它使用了递归算法,递归算法层层压栈,小规模数据性能并不好.
四、防止栈溢出的方法
第一种是限制递归深度。一旦递归过深,超过了我们事先设定的阈值,就停止递归。
第二种是通过在堆上模拟实现一个函数调用栈,手动模拟递归压栈、出栈的过程,这样就没有了系统栈大小的限制
五、代码实现
namespace QuickSort{
int median3(int a[],int left,int right){
// Dump(a,10,"Median3 before : ");
int mid=(left+right)/2;
if(a[left]>a[right]){
swap(a[left],a[right]);
}
if(a[mid]<a[left]){
swap(a[mid],a[left]);
}
if(a[mid]>a[right]){
swap(a[mid],a[right]);
}
// cout<<"median: "<<a[mid];//调试代码
swap(a[mid],a[right-1]);//将主元藏在右侧(倒数第二个元素) 目的:方便后面做子集划分
// Dump(a,10," Median3 After : ");//调试代码
return a[right-1];
}
void Qsort(int a[],int left,int right){
//递归退出条件
if(right-left<=1){
return;
}
//1.选主元
int pivot=median3(a,left,right);
//2.子集划分(细节1)
int l=left;//最左侧肯定比主元小了,不用考虑了,所以下面是++l
int r=right-1;//最右侧肯定比主元大,倒数第二个是主元,都不用考虑了,所以下面是--r
//2.1
while(1){
//(细节2 ++l,--r)
while(a[++l]<=pivot && l<r){}
while(a[--r]>=pivot && l<r){}
if(l<r){
swap(a[l],a[r]);
}else{
break;
}
}
//2.2 将主元放在正确的位置(细节3,交换的是a[right-1]小心不能写作pivot)
swap(a[right-1],a[l]);
// cout<<"media: "<<pivot;//调试代码
// Dump(a,10," after put pivot: ");
//3 递归调用左右两侧(细节4,都写成l,)
Qsort(a,left,l);//这里是l,不能是l-1
Qsort(a,l+1,right);//这里这里是l+1,或l都可
}
void Func(int a[],int size){
if(size<=1){
return;
}
Qsort(a,0,size-1);
}
}
namespace Test{
int arr1[]={1,0,4,3,5,9,7,2,6,8,13,12,18,15,20,24,99,33};
int arr2[]={1,0,3};
int arr3[]={1};
int arr4[]={2,2,2,2,0,2,2,2};
int arr5[]={3,3,3,3,3,3,3};
void QuickSortTest(){
cout<<"test1"<<endl;
Dump(arr1,sizeof(arr1)/sizeof(int),"before:\n");
QuickSort::Func(arr1, sizeof(arr1)/sizeof(int));
Dump(arr1,sizeof(arr1)/sizeof(int),"after:\n");
cout<<endl;
cout<<"test2"<<endl;
Dump(arr2,sizeof(arr2)/sizeof(int),"before:\n");
QuickSort::Func(arr2, sizeof(arr2)/sizeof(int));
Dump(arr2,sizeof(arr2)/sizeof(int),"after:\n");
cout<<endl;
cout<<"test3"<<endl;
Dump(arr3,sizeof(arr3)/sizeof(int),"before:\n");
QuickSort::Func(arr3, sizeof(arr3)/sizeof(int));
Dump(arr3,sizeof(arr3)/sizeof(int),"after:\n");
cout<<endl;
cout<<"test4"<<endl;
Dump(arr4,sizeof(arr4)/sizeof(int),"before:\n");
QuickSort::Func(arr4, sizeof(arr4)/sizeof(int));
Dump(arr4,sizeof(arr4)/sizeof(int),"after:\n");
cout<<endl;
cout<<"test5"<<endl;
Dump(arr5,sizeof(arr5)/sizeof(int),"before:\n");
QuickSort::Func(arr5, sizeof(arr5)/sizeof(int));
Dump(arr5,sizeof(arr5)/sizeof(int),"after:\n");
}
}
int main()
{
Test::QuickSortTest();
return 0;
}
参考:
数据结构MOOC课程