快速排序基本思路
对一个给定的数组, 选择一个元素, 对数组进行整理,使得位于该元素的左边的元素都比选定元素小, 右边的部分都比选定元素大或相等; 这个整理过程叫做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;
}
结果: