快速排序基本思路
對一個給定的數組, 選擇一個元素, 對數組進行整理,使得位於該元素的左邊的元素都比選定元素小, 右邊的部分都比選定元素大或相等; 這個整理過程叫做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 ¤t = 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;
}
結果: