国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

快速排序遞歸方法和非遞歸方法詳解

這篇具有很好參考價值的文章主要介紹了快速排序遞歸方法和非遞歸方法詳解。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。


快速排序遞歸方法和非遞歸方法詳解,排序算法,C/C++,排序算法,算法,數(shù)據(jù)結構

快速排序遞歸方法和非遞歸方法詳解

1、快速排序(遞歸)

1.1、快速排序思想(遞歸)

  • 快速排序(遞歸)是對數(shù)組進行拆解,也就是基于分治的思想。這里討論將數(shù)組升序的情況:每次選出一個樞軸,在這部分數(shù)據(jù)中將小于該樞軸的元素放到樞軸的左邊,大于樞軸的元素放到樞軸的右邊,然后基于此次樞軸的位置,分成左右兩個區(qū)間。再繼續(xù)對此左右區(qū)間繼續(xù)進行上述操作。每趟選出一個樞軸,每趟排序都可以將這次排序的樞軸放到最終位置。(看下面圖解)

    • 樞軸即可以將序列分成兩半的元素,樞軸大于左邊序列,樞軸小于右邊序列。
      快速排序遞歸方法和非遞歸方法詳解,排序算法,C/C++,排序算法,算法,數(shù)據(jù)結構

1.2、排序過程(遞歸)圖解

  • 每次選出一個樞軸(這里每次選的的最此次需要排序的左邊元素),把這趟要排序的元素中小于樞軸的元素放到樞軸的左邊,大于樞軸的元素放到樞軸的右邊。

    • 也就是right先走(最后left所指元素一定會比keyi所指元素?。瑥挠彝笞?,找到比樞軸小的元素,就停下來。然后再left走,從左往右找,找到比樞軸大的元素,就停下來。然后交此時leftright對應的元素的值。leftright繼續(xù)走,直到left == right。此時left所指元素一定小于keyi所指元素,將left所指元素余keyi所指元素互換,以確定此趟排序一個元素的最終位置pos。

    快速排序遞歸方法和非遞歸方法詳解,排序算法,C/C++,排序算法,算法,數(shù)據(jù)結構

  • 確定好了樞軸的最終位置后,遞歸調用,把此次要排序的元素分成兩組,左邊一組(小于樞軸),右邊一組(大于樞軸),對此左右兩組繼續(xù)采用上述方法。

    快速排序遞歸方法和非遞歸方法詳解,排序算法,C/C++,排序算法,算法,數(shù)據(jù)結構

    快速排序遞歸方法和非遞歸方法詳解,排序算法,C/C++,排序算法,算法,數(shù)據(jù)結構

    快速排序遞歸方法和非遞歸方法詳解,排序算法,C/C++,排序算法,算法,數(shù)據(jù)結構

    • 這里僅畫了部分圖解,其他的過程自己考慮(和上述過程類似)。

1.3、快速排序(遞歸)代碼

1.3.1、原始方法
  • 先寫出一趟排序的代碼,即先選出此趟排序的樞軸,右邊指針right先走,從右往左找到第一個小于樞軸的元素,就停下來。然后左邊指針left再走,從左往右找到第一個大于樞軸的元素,就停下來。交換此時左右指針所指向的元素,然后繼續(xù)上述過程(right先走),直到left == right。排序完將此樞軸放到整個數(shù)組的最終位置上。一趟排序的代碼如下:

    //一趟排序  ---  原始方法
    int PartSort1(int *arr, int left, int right) {
        int midi = GetMid(arr, left, right);
        Swap(&arr[left], &arr[midi]);
        int keyi = left;
        while (left < right) {
            while (left < right && arr[right] >= arr[keyi]) {
                --right;
            }
            while (left < right && arr[left] <= arr[keyi]) {
                ++left;
            }
            Swap(&arr[left], &arr[right]);
        }
        //由于是右邊先走,所以left處為最終樞軸元素的最終位置
        Swap(&arr[keyi], &arr[left]);
        return left;
    }
    
    • 這里我們注意到,這里有個GetMid函數(shù),這個函數(shù)是干嘛的呢?其實,這個函數(shù)是用來優(yōu)化遞歸版的快速排序的。這個函數(shù)可以用來對每趟排序的樞軸的取值更合理,即每趟取此趟排序的最前元素、最后元素、中間元素的中位數(shù)。這樣可以非常有效防止對于一個基本有序或者已經(jīng)有序的序列進行快速排序的時候遞歸深度太深GetMid函數(shù)代碼如下:

      //取第一個、中間一個、最后一個元素中的中位數(shù)
      int GetMid(int *arr, int left, int right) {
          int mid = (left + right) / 2;
          if (arr[mid] >= arr[left]) {
              if (arr[mid] >= arr[right]) {//mid處元素值最大
                  if (arr[left] <= arr[right]) {//right處元素值第二大
                      return right;
                  } else {
                      return left;//left處元素值第二大
                  }
              } else {//right處元素值最大
                  return mid;//mid處元素值第二大
              }
          } else {
              if (arr[left] >= arr[right]) {//left處元素值最大
                  if (arr[mid] >= arr[right]) {
                      return mid;//mid處元素值第二大
                  } else {
                      return right;//right處元素值第二大
                  }
              }
          }
      }
      

      下面再介紹兩種上述一趟排序的算法,分別是挖坑法左右指針法。

1.3.2、挖坑法
  • 挖坑法:首先在最左邊樞軸元素處挖坑,用hole標記洞的位置,并且記錄此元素值key,然后right先走,從右往左走,找到比key小的元素將其填補到左邊的,并將此元素位置作為新的坑,然后再left走,找到比key大的元素將其填補到右邊的,并將此元素位置作為新的坑。重復上述過程,直到left == right,再將已記錄的樞軸填補到left位置上。最后洞的位置就是這趟排序樞軸的最終位置。

    • 挖坑法部分圖解:

      快速排序遞歸方法和非遞歸方法詳解,排序算法,C/C++,排序算法,算法,數(shù)據(jù)結構

      快速排序遞歸方法和非遞歸方法詳解,排序算法,C/C++,排序算法,算法,數(shù)據(jù)結構

    • 挖坑法代碼:

      //一趟排序  ---  挖坑法
      int PartSort2(int *arr, int left, int right) {
          int midi = GetMid(arr, left, right);
          Swap(&arr[left], &arr[midi]);
          int key = arr[left];
          int hole = left;//左邊第一個坑位
          while (left < right) {
              //右邊先走,找小,填到左邊的坑,在右邊形成新的坑位
              while (left < right && arr[right] >= key) {
                  --right;
              }
              //右邊坑位
              arr[hole] = arr[right];
              hole = right;
              //左邊先走,找大,填到右邊的坑,在左邊形成新的坑位
              while (left < right && arr[left] <= key) {
                  ++left;
              }
              //左邊坑位
              arr[hole] = arr[left];
              hole = left;
          }
          arr[hole] = key;//最后一次坑填當前樞軸元素
          return hole;
      }
      
1.3.3、左右指針法
  • 左右指針法:取樞軸為最左邊元素(keyi所指元素),定義兩個指針precur,pre用來記錄大于樞軸的元素的前驅,cur用來從左往右找小于樞軸的元素,找到了就++pre,然后precur所指元素交換(cur指向右邊小于樞軸的元素,pre指向左邊大于樞軸的元素),然后++cur,否則只需要++cur,pre不動。直到cur == right,就交換prekeyi所指向的元素,即可確定此趟排序的最終樞軸位置pre。

    • 左右指針法部分圖解:

      快速排序遞歸方法和非遞歸方法詳解,排序算法,C/C++,排序算法,算法,數(shù)據(jù)結構

    • 左右指針法代碼:

      //一趟排序  ---  左右指針法
      int PartSort3(int *a, int left, int right) {
          int pre = left;
          int cur = left + 1;
          int keyi = left;
          while (cur <= right) {
              //cur找小于樞軸的元素,找到就++pre和cur元素交換,否則cur后移繼續(xù)找
              if (a[cur] < a[keyi] && ++pre != cur) { //++pre != cur 防止自己和自己交換 這里pre已經(jīng)++了
                  Swap(&a[pre], &a[cur]);
              }
              ++cur;
          }
          Swap(&a[pre], &a[keyi]);
          return pre;
      }
      
  • 再來寫整體的排序代碼,即每趟選出一個樞軸元素后,就確定了它的最終位置,然后對此樞軸元素的左邊序列排序,再對對此樞軸元素的右邊序列排序。遞歸結束條件是只剩下一個元素(begin == end),或者begin > end,因為到這一步不需要排序了。

    //快速排序 -- 遞歸方法
    void QuickSort(int *arr, int begin, int end) {
        if (begin >= end)//只剩下一個元素,或者begin > end直接返回
            return;
        //先找到排序一趟后的樞軸元素的最終位置
        int pos = PartSort3(arr, begin, end);
        QuickSort(arr, begin, pos - 1);//對左邊排序
        QuickSort(arr, pos + 1, end);//對右邊排序
    }
    
    • 我們來想想,對于這個遞歸版的快速排序還能不能再優(yōu)化?答案是肯定的。我們考慮一個問題,當排序到當前序列長度就為10左右,此序列是不是基本有序了,此時遞歸深度可能已經(jīng)很深了。那基本有序了是不是可以考慮一下其他排序算法,不用遞歸的算法,可以降低遞歸深度,這里插入排序是最優(yōu)選擇。那么我們看下優(yōu)化后的代碼。其中判斷此趟序列長度可以用end - begin。

      //快速排序  -- 遞歸方法優(yōu)化
      void QuickSort(int *arr, int begin, int end) {
          if (begin >= end)//只剩下一個元素,或者begin > end直接返回
              return;
          if (end - begin <= 10) {
              //區(qū)間短短直接用插入排序,因為短區(qū)間此時基本有序了
              InsertSort(arr + begin, end - begin + 1);
          } else {
              //先找到排序一趟后的樞軸元素的最終位置
              int pos = PartSort3(arr, begin, end);
              QuickSort(arr, begin, pos - 1);//對左邊排序
              QuickSort(arr, pos + 1, end);//對右邊排序
          }
      }
      

2、快速排序(非遞歸)

2.1、快速排序(非遞歸)思想

  • 快速排序(非遞歸)和快速排序(遞歸)的思想類似,但是遞歸版的使用的系統(tǒng)棧,非遞歸版則是采用人工棧來模擬遞歸的系統(tǒng)棧。即還是采用分治法,每趟排序完把這趟的序列分成兩部分。首先創(chuàng)建一個棧Stack,然后先把這整個要排序的序列的左右邊界的下標壓入棧中,這里采用先壓左邊邊界下標left(也可以先壓右邊邊界下標,出棧也得跟著變),然后壓右邊邊界下標right。接下來就是對這個整個序列入棧出棧的操作了(排序):取這次要排序的序列區(qū)間(取棧頂元素(左邊界),再棧頂元素出棧,再取棧頂元素(右邊界),再棧頂元素出棧),然后對這個區(qū)間的序列進行劃分(確定這個序列的樞軸的最終位置pos)。然后判段這次樞軸的位置對于其左右區(qū)間是否還需要繼續(xù)劃分(posleft、right比較,pos -1 > left,pos + 1 < right則繼續(xù)劃分),一直循環(huán)下去,直到???/strong>。

2.2、排序過程(非遞歸)圖解

  • 首先創(chuàng)建一個棧用來存每趟要排序的序列的左右邊界

    快速排序遞歸方法和非遞歸方法詳解,排序算法,C/C++,排序算法,算法,數(shù)據(jù)結構

  • 接下來就是出棧入棧了,可以得到一個區(qū)間,然后對該區(qū)間的序列進行劃分來確定樞軸最終位置。

    • 這里是部分圖解,其他的過程自行思考(和這里圖解過程類似)。

    快速排序遞歸方法和非遞歸方法詳解,排序算法,C/C++,排序算法,算法,數(shù)據(jù)結構

    快速排序遞歸方法和非遞歸方法詳解,排序算法,C/C++,排序算法,算法,數(shù)據(jù)結構

    快速排序遞歸方法和非遞歸方法詳解,排序算法,C/C++,排序算法,算法,數(shù)據(jù)結構

2.3、快速排序(非遞歸)代碼

  • 主要就是用人工棧模擬系統(tǒng)棧的壓棧流程,為什么要使用非遞歸的方法?答案是因為系統(tǒng)棧遞歸調用可能導致棧的深度太深,而且系統(tǒng)棧遞歸調用保存的信息很多。快速排序(非遞歸)代碼如下:

    //快速排序 -- 非遞歸方法
    void QuickSortNonR(int *arr, int begin, int end) {
        Stack st;
        STInit(&st);
        STPush(&st, end);
        STPush(&st, begin);//將此次排序的左右邊界下標入棧
        while (!STEmpty(&st)) {
            int left = STTop(&st);
            STPop(&st);
            int right = STTop(&st);
            STPop(&st);
            int pos = PartSort2(arr, left, right);
            if (right > pos + 1) {
                STPush(&st, right);//右邊排序
                STPush(&st, pos + 1);
            }
            if (left < pos - 1) {//有一個以上元素
                STPush(&st, pos - 1);//左邊排序
                STPush(&st, left);
            }
        }
        STDestroy(&st);
    }
    
    • 這里每趟排序劃分的代碼和遞歸版的代碼一樣,就不再贅述
    • 時間復雜度:最壞情況:數(shù)組完全逆序,這時候需要遞歸n次(或者棧深度為n),共n個元素,所以最壞時間復雜度為O(n^2)。但是,極大多數(shù)情況是由于采用的是二分法,所以每趟排序的時間復雜度為O(logn),又因為有n個元素,所以總的時間復雜度為O(nlogn)
    • 空間復雜度:由于需要遞歸或者使用棧,那么遞歸深度或者棧深度就是相當于需要的額外空間,最壞空間復雜度是O(n),這是在數(shù)組完全逆序的時候(使用GetMid函數(shù)能解決)。但是極大多數(shù)情況空間復雜度是O(logn) <-- 采用二分法,相當于是n個結點的二叉樹的高度。
    • 算法穩(wěn)定性不穩(wěn)定,考慮序列(2,2,1),排序后序列為(1,2,2),我們發(fā)現(xiàn)2的相對位置發(fā)生了變化,所以是不穩(wěn)定的排序算法。

那么好,快速排序算法的遞歸版和非遞歸版的介紹就到這里。其他排序算法可以看看我另一篇博客。下面是我的github主頁,里面記錄了我的學習代碼和leetcode的一些題的題解,有興趣的可以看看。

Xpccccc的github主頁文章來源地址http://www.zghlxwxcb.cn/news/detail-717707.html

到了這里,關于快速排序遞歸方法和非遞歸方法詳解的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。如若轉載,請注明出處: 如若內容造成侵權/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領支付寶紅包贊助服務器費用

相關文章

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領取紅包,優(yōu)惠每天領

二維碼1

領取紅包

二維碼2

領紅包