快速排序的非遞歸實現

首先說明一下快速排序是對冒泡排序的改進。為什么這么說呢?想一下冒泡排序,它把序列分成了兩部分,前半部分無序,后半部分升序排列,并且后半部分的數都大于前半部的數。

由此可得到快速排序和冒泡排序的一些共同點:

  1. 都要經歷n趟排序
  2. 每趟排序要經歷O(n)次比較
  3. 都是后半部分元素比前半部大

而不同之處就在于冒泡排序的交換操作發生相鄰的元素之間,即一趟排序可以要經過多次交換操作;
快速排序的交換操作發生在間隔比較遠的兩個元素之間,一趟排序要經過交換操作次數會少一些。

下面給出快速排序的遞歸和非遞歸實現代碼:

#include<iostream>
#include<vector>
#include<stack>
#include<cstdlib>
#include<algorithm>
using namespace std;
 
/**把數組分為兩部分,軸pivot左邊的部分都小于軸右邊的部分**/
template <typename Comparable>
int partition(vector<Comparable> &vec,int low,int high){
    Comparable pivot=vec[low];  //任選元素作為軸,這里選首元素
    while(low<high){
        while(low<high && vec[high]>=pivot)
            high--;
        vec[low]=vec[high];
        while(low<high && vec[low]<=pivot)
            low++;
        vec[high]=vec[low];
    }
    //此時low==high
    vec[low]=pivot;
    return low;
}
 
/**使用遞歸快速排序**/
template<typename Comparable>
void quicksort1(vector<Comparable> &vec,int low,int high){
    if(low<high){
        int mid=partition(vec,low,high);
        quicksort1(vec,low,mid-1);
        quicksort1(vec,mid+1,high);
    }
}
 
/**使用棧的非遞歸快速排序**/
template<typename Comparable>
void quicksort2(vector<Comparable> &vec,int low,int high){
    stack<int> st;
    if(low<high){
        int mid=partition(vec,low,high);
        if(low<mid-1){
            st.push(low);
            st.push(mid-1);
        }
        if(mid+1<high){
            st.push(mid+1);
            st.push(high);
        }
        //其實就是用棧保存每一個待排序子串的首尾元素下標,下一次while循環時取出這個范圍,對這段子序列進行partition操作
        while(!st.empty()){
            int q=st.top();
            st.pop();
            int p=st.top();
            st.pop();
            mid=partition(vec,p,q);
            if(p<mid-1){
                st.push(p);
                st.push(mid-1);
            }
            if(mid+1<q){
                st.push(mid+1);
                st.push(q);
            }       
        }
    }
}
 
int main(){
    int len=1000000;
    vector<int> vec;
    for(int i=0;i<len;i++)
        vec.push_back(rand()); 
    clock_t t1=clock();
    quicksort1(vec,0,len-1);
    clock_t t2=clock();
    cout<<"recurcive  "<<1.0*(t2-t1)/CLOCKS_PER_SEC<<endl;
     
    //重新打亂順序
    random_shuffle(vec.begin(),vec.end());
         
    t1=clock();
    quicksort2(vec,0,len-1);
    t2=clock();
    cout<<"none recurcive  "<<1.0*(t2-t1)/CLOCKS_PER_SEC<<endl;
     
    return 0;
}
orisun@zcypc:~g++quicksort.cpp?oqsorisun@zcypc:  ./qs
recurcive 0.38
none recurcive 0.47

可以看到非遞歸的算法比遞歸實現還要慢。下面解釋為什么會這樣。

遞歸算法使用的棧由程序自動產生,棧中包含:函數調用時的參數和函數中的局部變量。如果局部變量很多或者函數內部又調用了其他函數,則棧會很大。每次遞歸調用都要操作很大的棧,效率自然會下降。

而對于非遞歸算法,每次循環使用自己預先創建的棧,因此不管程序復雜度如何,都不會影響程序效率。

對于上面的快速排序,由于局部變量只有一個mid,棧很小,所以效率并不比非遞歸實現的低。

原文來自:博客園(華夏35度)
http://www.cnblogs.com/zhangchaoyang
作者:Orisun

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容