交換排序——快速排序

void QuickSort(int *arr, int begin, int end)
{
     assert(arr);
     int div = PartSort1(arr, begin, end);
     if (div - 1>begin)
          PartSort1(arr, begin, div - 1);
     if (div + 1<end)
          PartSort1(arr, div + 1, end);
}

快速排序是一種劃分交換的方法,它採用分治法進行排序。

快速排序的基本思想:通過一趟排序將要排序的數據分割成獨立的兩部分,其中一部分的所有數據都比另外一部分的所有數據都要小,然後再按此方法對這兩部分數據分別進行快速排序,整個排序過程可以遞歸進行,以此達到整個數據變成有序序列,直到所有元素都排在相應的位置(即序列中只有一個元素或者沒有元素的時候)。

方法一:左右指針法

 一個指針從頭開始,一個指針從尾開始,選取尾的數作爲key值。兩個指針分別開始走,頭指針遇到比key值大的就停下來,尾指針遇到比key值小的就停下來,頭指針停下來的數和尾指針停下來的數進行交換。交換之後頭尾指針分別繼續走,直至頭指針大於尾指針則本次循環結束。此時,頭指針指向的數一定大於key值,頭指針指向的數與key指向的數交換。
 (在進行比較的時候一定要至少一個加上等號否則如果相等頭指針和尾指針都不走了,就會陷入死循環)

這裏寫圖片描述
這裏寫圖片描述
這裏寫圖片描述
這裏寫圖片描述

如上圖,爲第一次排序的過程,第一次排序之後,key所指向的5左側所有的數均比5小,右側所有的數均比5大。

int PartSort1(int* arr, int begin, int end)
{
     int key = arr[end];
     int left = begin;
     int right = end;
     while (left < right)
     {
          while (left < right && arr[left] < key)
          {
              ++left;
          }
          while (left < right && arr[right] >= key)
          {
              --right;
          }
          swap(arr[left], arr[right]);
     }
     swap(arr[left], arr[end]);
     return begin;
}

方法二:挖坑法

挖坑法是先找一個值,將其保存起來,這樣這個值的位置就形成一個坑,可以隨意覆蓋掉了。
這裏寫圖片描述

由於坑在left的位置,所以我們從right開始找比key小的值。將這個值放入key剛纔所在的坑裏,此時形成一個新的坑。
這裏寫圖片描述
此時坑在right的位置,從left找一個比key大的值放入坑內。
這裏寫圖片描述
這裏寫圖片描述
這裏寫圖片描述
這裏寫圖片描述

right不斷減,最終與left相遇,則把key放入坑內。此時第一趟排序結束,key值左側的值均比key小,右側的值均比key大。 再對子序列重複上述操作,直到劃分的子序列中元素的個數只有一個或者沒有的時候才停止。

int PartSort2(int* arr, int begin, int end)
{
     int key = arr[begin];
     int left = begin;
     int right = end;
     while (left < right)
     {
          while (left < right&&arr[right] >= key)
          {
              --right;
          }
          arr[left] = arr[right];
          while (left < right&&arr[left] <= key)
          {
              ++left;
          }
          arr[right] = arr[left];
     }
     arr[left] = key;
     return left;
}

方法三:前後指針法

一個prev指針和一個cur指針。prev指向cur的前一個。
cur向後移動,如果arr[cur]比key值小就停下來,++prev再判斷如果prev!=cur就交換,交換之後cur就繼續走。如果大於key就直接++cur。
等到cur遍歷完整個數組,++prev,然後將arr[prev]和arr[end]交換

這裏寫圖片描述
這裏寫圖片描述
這裏寫圖片描述
這裏寫圖片描述
這裏寫圖片描述

int PartSort3(int* arr, int begin, int end)
{
     int key = arr[end];
     int cur = begin;
     int prev = begin - 1;
     while (cur < end)
     {
          if(arr[cur] < key)
          {
              ++prev;
              if (prev != cur)
              {
                   swap(arr[cur], arr[prev]);
              }
          }
          ++cur;
     }
     ++prev;
     swap(arr[prev], arr[end]);
     return prev;
}

小區間優化法

思想:假如劃分的區間小於13,那麼我們認爲不需要用快排序來實現,因爲遞歸也是需要付出代價的,這樣反而效率低,因此我們選擇直接插入排序來解決。

三數取中法

由於快速排序可能在劃分的時候它的end-begin可能是最大值或者最小值也可能是接近於最大值或最小值,而在這種情況下快速排序它的時間複雜度就可能接近於O(N2)爲了避免這種情因此我們都選擇三個數裏面最中間的那個數。

int GetMiddle(int*arr, int left, int right)
{
     int mid = left + ((right - left) >> 1);
     if (arr[left] < arr[right])
     {
          if (arr[right] < arr[mid])
              return right;
          else
          {
              if (arr[left] < arr[mid])
                   return mid;
              else
                   return left;
          }
     }
     else//left>right
     {
          if (arr[right] > arr[mid])
              return right;
          else
          {
              if (arr[left] > arr[mid])
                   return mid;
              else
                   return left;

          }
     }
}

快速排序非遞歸

藉助棧,通過壓棧出棧操作,實現快速排序的非遞歸。

void QuickSortNR(int *arr, int begin, int end)
{
     assert(arr);
     stack<int> s;

     s.push(begin);
     s.push(end);
     while (!s.empty())
     {
          int left = s.top();
          s.pop();
          int right = s.top();
          s.pop;
          int div = PartSort3(arr, left, right);
          if (div - 1 > left)
          {
              s.push(div - 1);
              s.push(left);
          }
          if (div + 1 < right)
          {
              s.push(right);
              s.push(div + 1);
          }
     }
}
發佈了67 篇原創文章 · 獲贊 6 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章