快速排序遞歸與非遞歸C++實現

快速排序基本思路

對一個給定的數組, 選擇一個元素, 對數組進行整理,使得位於該元素的左邊的元素都比選定元素小, 右邊的部分都比選定元素大或相等; 這個整理過程叫做partition; 把partition過程遞歸的應用到選定元素的左側和右側的子數組,直到子數組少於2個元素,遞歸結束; 整個數組變爲有序的;

所以問題轉變成partition的實現, 給定一個數組,選一個數組中的元素, 如何整理使得該元素的左邊都小於它,右邊都大於等於它?

一個最直觀的思路是, 選定一個元素, 例如pivot = A[len/2], 再聲明一個相同大小的數組,對原給定數組A遍歷, 把小於的在頭部開始排列,把大的在尾部開始排列,最後放置pivot;
但這個思路, 問題在於多使用了內存空間, 移動元素次數也較多;

Denis Richie的這個版本quicksort的實現和清華版數據結構的版本實現不同, 思路如下:

// 函數聲明:
void quicksort(int v[], int left, int right);
  • 準備: 選取中間值爲pivot, 把pivot和left的第一個元素交換, 將last指向left
  • 循環執行: 從left+1的位置開始遍歷至right位置, 對於第i位置小於pivot值的元素, 將元素與++last位置的元素交換,直到right, last位置之前的元素,都是小於pivot的值的元素;
  • 循環結束後,將left位置的pivot與last位置元素交換,放到正確的位置上; last的位置就是選定元素的正確位置;

這個實現的優勢是思路簡單,一次遍歷遍歷次數少, 原地操作節省內存;

// 簡單實現

void swap(int v[], int i, int j) {
  int temp;
  temp = v[i];
  v[i] = v[j];
  v[j] = temp;
}


void qsort(int v[], int left, int right) {
  int i, last;
  extern void swap(int v[], int i, int j);

  if (left >= right) {
    return;  // 如果array的元素小於2個,遞歸返回;
  }

  // 選取位置中間的元素,當pivot
  swap(v, left, (left + right)/2); 
  last = left;
  for (i = left + 1; i <= right; i++) {
    // v[left] is pivot
    if (v[i] < v[left]) {
      swap(v, ++last, i);
    }
  }
  // 在遍歷完成後,將pivot(v[left])和v[last]交換,
  // 即滿足了最終的放置元素到正確位置;
  swap(v, left, last);
  qsort(v, left, last-1);
  qsort(v, last+1, right);
}

non-recursive 的快速排序實現

求解問題的思路是包括2點:

  • 瞭解周遊2叉樹的方法作爲前提;
  • 觀察遞歸解法的特點;

可以發現, 遞歸快排實現實際上是二叉樹先序周遊的實現。我們可以用棧作爲輔助實現快排非遞歸算法;

文字僞碼描述參見附錄(todo)。完整源碼如下:

//  quicksort2.h

#ifndef QUICKSORT2_H_
#define QUICKSORT2_H_

#include <vector>

enum NODE_STATE { kInit, kEnterLeftTree, kEnterRightTree, kDone };

struct Node {
  int last;
  int low;
  int high;
  NODE_STATE state;
};

template <typename T>
class Quick_sort {
 private:
  static bool Default_compare_(const T& a, const T& pivot) {
    // for ascending order:
    return a < pivot;
  }

  void swap(int i, int j) {
    T temp = v_[i];
    v_[i] = v_[j];
    v_[j] = temp;
  }

  int partition_(int left, int right) {
    swap(left, (left + right) / 2);
    int last = left;
    for (int i = left + 1; i <= right; i++) {
      if (compare_(v_[i], v_[left])) {
        swap(++last, i);
      }
    }
    swap(left, last);
    return last;
  }

  void partition_(Node &cnode) {
    int last = partition_(cnode.low, cnode.high);
    cnode.last = last;
  }

 public:
  Quick_sort(T v[], decltype(Default_compare_)* comp = Default_compare_) :
    compare_(comp),
    v_(v) { }

  void Quicksort_Recursive(int left, int right) {
    if (left >= right)
      return;

    int last = partition_(left, right);
    Quicksort_Recursive(left, last - 1);
    Quicksort_Recursive(last + 1, right);
  }

  void Quicksort_NonRecursive(int left, int right) {
    Node root = { -1, left, right, kInit };
    std::vector<Node> stack;

    partition_(root);
    stack.push_back(root);

    while (stack.size()) {
      Node &current = stack.front();
      
      // 存在左子樹, 且沒有處理左子樹
      if (current.state == kInit && current.last - 1 > current.low) {
        current.state = kEnterLeftTree;
        Node left_node = { -1, current.low, current.last - 1, kInit };
        partition_(left_node);
        stack.push_back(left_node);
        continue;
      } else {
        // 存在右子樹, 且狀態是還沒有處理右子樹
        if (current.high > current.last - 1 && (current.state == kInit ||
          current.state == kEnterLeftTree)) {
          current.state = kEnterRightTree;
          Node right_node = { -1, current.last + 1, current.high, kInit };
          partition_(right_node);
          stack.push_back(right_node);
          continue;
        } else {
          current.state = kDone;
          stack.pop_back();
        }
      }
    }
  }

 private:
  T *v_;
  decltype(Default_compare_)* compare_;
};

#endif

測試:

// testing

#include <iostream>
#include "quicksort2.h"

struct MyData {
  int val;

  MyData(int value): val(value) {}

  bool operator<(const MyData &rhs) const {
    return this->val < rhs.val;
  }
};


int main() {
  
  ////////////////////////////////////////////////////////////////////////////////
  // sort MyData

  MyData source_array[7] = { 10, 9, 8, 5, 3, 1, 2 };
  Quick_sort<MyData> qs(source_array);
  size_t int_len = sizeof(source_array) / sizeof(source_array[0]);

  // qs.Quicksort_Recursive(0, int_len - 1);
  //
  qs.Quicksort_NonRecursive(0, int_len - 1);

  for (int i = 0; i < int_len; i++) 
    std::cout << " " << source_array[i].val;
  std::cout << std::endl;

  ////////////////////////////////////////////////////////////////////////////////
  // sort string

  std::string str_array[5] = { "hello", "world", "C++", "Javascript", "Dart" };
  Quick_sort<std::string> qs2(str_array);
  size_t str_len = sizeof(str_array) / sizeof(str_array[0]);
  // std::cout << "str_len=" << str_len;

  // qs2.Quicksort_Recursive(0, str_len - 1);
  qs2.Quicksort_NonRecursive(0, str_len - 1);

  for (int j = 0; j < str_len; j++ ) {
    std::cout << " " << str_array[j];
  }
  std::cout << std::endl;
}

結果:
result

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章