1. 程式人生 > >快速排序的兩種實現思路和非遞迴實現--C++實現

快速排序的兩種實現思路和非遞迴實現--C++實現

思路一:
       第一種是根據演算法導論上的思想:取陣列的最後一個元素為主元,i初始化為最低位元素的前一個位置,j指向遍歷陣列中待排序的元素,當j所指元素比主元小的時候i= i + 1,然後交換i和j所指的元素,j不斷遍歷,遇到小於主元的就進行交換,這樣就能一直維持這樣一個序列,i之前的元素(包括i所指元素本身)都是比主元小的元素,i到j之間都是比主元大的元素,j(包括j所指元素)之後都是待排序的元素,最後交換主元和第一個比主元大的元素就可以完成劃分。
       演算法導論具體介紹如下:
           快速排序是基於分治模式處理的,對一個典型子陣列A[p…r]排序的分治過程為三個步驟:

這裡寫圖片描述

           具體實現快速排序的虛擬碼:

這裡寫圖片描述

           具體例子分析詳情:

這裡寫圖片描述
這裡寫圖片描述

           C++程式碼實現如下:

//1、進行區域的劃分
int getPartition(vector<int> &nums, int low, int height)
{
    int keyVal = nums[height];
    int i = low - 1;
    for (int j = low; j < height; j++)
    {

        if (nums[j] <= keyVal)
        {
            i = i + 1
; swap(nums[i], nums[j]); } } swap(nums[i + 1], nums[height]); return i+1; } //2、遞迴呼叫劃分區域函式,進行快速排序 void quickSort(vector<int> &nums,int low,int height) { if (low<height) { int mid = getPartition(nums, low, height); quickSort(nums, low, mid-1
); quickSort(nums, mid + 1, height); } }

思路二:
       第二種是嚴蔚敏的資料結構(C語言版)上的思想:取陣列的第一個元素為主元,左(left)、右(height)兩指標進行遍歷,先右邊開始邊查詢比主元小的(比主元大時height直接減減),找到就直接覆蓋左邊所指的元素,然後從左邊開始查詢比主元大的元素,找到就直接覆蓋右邊所指的元素,依次迴圈進行。最後low不小於height時把所取的主元付給low所指的位置,完成劃分。
       資料結構(C語言版)具體介紹如下:

這裡寫圖片描述
這裡寫圖片描述

           具體實現快速排序的虛擬碼:

這裡寫圖片描述
這裡寫圖片描述

           具體例子分析詳情:

這裡寫圖片描述
這裡寫圖片描述

           C++程式碼實現如下:

//1、進行區域的劃分
int getPartition(vector<int> &nums, int low, int height)
{
    int keyVal = nums[low];
    while (low<height)
    {
        while (low < height&&nums[height] >= keyVal)
            height--;
        nums[low] = nums[height];
        while (low < height&&nums[low] <= keyVal)
            low++;
        nums[height] = nums[low];
    }
    nums[low] = keyVal;
    return low;
}

//2、遞迴呼叫劃分區域函式,進行快速排序
void quickSort(vector<int> &nums,int low,int height) {

    if (low<height)
    {
        int mid = getPartition(nums, low, height);
        quickSort(nums, low, mid-1);
        quickSort(nums, mid + 1, height);
    }
}

最後呈上快速排序的非遞迴實現:

void quickSortNonRecursive(vector<int> &nums, int low, int height)
{
    stack<int> s;
    if (low<height)
    {
        int mid = getPartition(nums, low, height);
        if (mid-1>low)
        {
            s.push(low);
            s.push(mid - 1);
        }
        if (mid+1<height)
        {
            s.push(mid + 1);
            s.push(height);
        }

        while (!s.empty())
        {
            int qHeight = s.top();
            s.pop();
            int pLow = s.top();
            s.pop();
            int pqMid = getPartition(nums, pLow, qHeight);
            if (pqMid - 1 > pLow)
            {
                s.push(pLow);
                s.push(pqMid - 1);
            }
            if (pqMid + 1 < qHeight)
            {
                s.push(pqMid + 1);
                s.push(qHeight);
            }
        }

    }
}

      其實經過測試非遞迴的演算法比遞迴實現還要慢。 因為遞迴演算法使用的棧由程式自動產生,棧中包含:函式呼叫時的引數和函式中的區域性變數。如果區域性變數很多或者函式內部又呼叫了其他函式,則棧會很大。每次遞迴呼叫都要操作很大的棧,效率自然會下降。而對於非遞迴演算法,每次迴圈使用自己預先建立的棧,因此不管程式複雜度如何,都不會影響程式效率。但是對於上面的快速排序,由於區域性變數只有一個mid,棧很小,所以效率並不比非遞迴實現的低。

      具體的關於快速排序的優化,提高快排效率,詳見文章快排優化