【數據結構】大量數據(20萬)的快速排序的遞歸與非遞歸算法、三數取中思想

快速排序的挖坑法與prev、cur法,咱們在上一篇博客的第6個排序中講的很是詳細,http://10740184.blog.51cto.com/10730184/1774508【數據結構】經常使用排序算法(包括:選擇排序,堆排序,冒泡排序,選擇排序,快速排序,歸併排序)    
算法

有興趣的話,相信聰明的你,一看就會秒懂快速排序的思想。數據結構


下面,咱們將快速排序優化:ide

一、三數取中來優化快速排序函數


優化緣由:優化


快速排序的擦差很少每次將序列一分爲二,時間複雜度是O(n*lgn).ui

wKiom1c-ku6jIeVzAAAhXifwo6A279.png

咱們思考,快速排序的時間複雜度是O(n*lgn),在序列亂序時,它的效率很高。可是,當序列有序或者接近有序時,效率就沒有那麼高了。spa

若是一個序列是這樣的:blog

{10,5,1,4,5,9,6,1}    
排序

咱們針對上述序列,若是要排成升序的話,咱們要找一個數知足挖坑法裏面的挪數據條件,或者說prev、cur法中的交換數據條件時,可能一直將序列從頭遍歷,找到結束或者快要結束才找到或者尚未找到,這時候至關於效率就變成了o(n^2)了。遞歸


優化方法:

所以,咱們想到了三數取中的思想。即序列的三個位置最左邊left,最右邊right,中間mid,三個數取出中間大小的數,用這個數作key.


三數取中的代碼以下:


int mid(int* a, int left, int right)
{
    int mid = left - (left - right) / 2;
    if (a[left] < a[right])
    {
        if (a[left] > a[mid])
        {
            return a[left];
        }
        else  
        {
            if (a[right] < a[mid])
            {
                return a[right];
            }
            else
            {
                return a[mid];
            }
        }
    }
    else
    {
        if (a[right] > a[mid])
        {
            return a[right];
        }
        else
        {
            if (a[left] < a[mid])
            {
                return a[left];
            }
            else
            {
                return a[mid];
            }
        }
    }
}



2.非遞歸的實現


優化緣由:

當一個序列較小時,每次將序列再成兩半,遞歸處理兩半的序列。

可是,當要給20萬、30萬這樣的序列排序時,每次遞歸的話,無疑每次調用函數創建棧幀會很很大的系統開銷,甚至會耗盡系統的空間。


優化方法:用棧stack模擬實現棧幀,每次壓棧出棧---》即遞歸寫法。


遞歸代碼以下:


int PartSort(int* a, int left, int right)
{
    assert(a);
    int cur = left;
    int prev = cur - 1;
    int key = mid(a,left,right);
    swap(key, a[right]);

    while (cur < right)
    {
        if (a[cur] <= a[right])
        {
            swap(a[++prev], a[cur]);
        }
        cur++;
    }
    swap(a[++prev], a[right]);
    
    return prev;
}


void QuickSort(int* a, int left, int right)
{
    int prev = PartSort(a,left, right);

    if (prev - 1 > left)
    {
        QuickSort(a, left, prev - 1);
    }

    if (prev + 1 < right)
    {
        QuickSort(a, prev + 1, right);
    }

}



非遞歸代碼以下(推薦):


//prev、cur法,也能夠採用挖坑法等其餘辦法
int PartSort(int* a, int left, int right)
{
    assert(a);
    int cur = left;
    int prev = cur - 1;
    int key = mid(a,left,right);
    swap(key, a[right]);//將三數取中獲得的數據與a[right]處交換。

    while (cur < right)
    {
        if (a[cur] <= a[right])
        {
            swap(a[++prev], a[cur]);
        }
        cur++;
    }
    swap(a[++prev], a[right]);
    
    return prev;
}


void QuickSort_NonR(int* a, int left, int right)
{
    stack<int> s;
    //左右區間壓入棧中,或者此時也能夠定義一個結構體,裏面有左右區間,一次把左右區間都壓進去
    s.push(left);
    s.push(right);
    while (!s.empty())
    {
        //[left,right]
        int curRight = s.top();//壓棧先壓的是左區間,先進後出,取數據先取右區間
        s.pop();
        int curLeft = s.top();
        s.pop();
        int prev = PartSort(a, curLeft, curRight);//將這個區間進行一次快速排序
        if (prev - 1 > curLeft)
        {
            s.push(curLeft);//壓入新分的左端序列
            s.push(prev - 1);
        }
        if (prev + 1 < curRight)
        {
            s.push(prev + 1);//壓入新分的右段區間
            s.push(curRight);
        }
    }
}
相關文章
相關標籤/搜索