快速排序递归与非递归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

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