快速排序是非常常用的排序方法, 采用分治法的策略将数组分成两个子数组, 基本 思路是:
递归的最底部情形,是数列的大小是零或一,也就是永远都已经被排序好了。虽然一直递归 下去,但是这个算法总会退出,因为在每次的迭代(iteration)中,它至少会把一个元素 摆到它最后的位置去。
排序过程如下图所示:
图片来自维基百科
快速排序最大的优点速度快, 通常能够达到 O(NlogN)
的速度, 原地排序, 不需要额
外的空间, 是非常优秀的算法, 在不考虑稳定性的情况下, 通常会考虑使用快速排序。
不过, 快速排序的缺点也是很明显的:
O(N^2)
。通常在快排实现中, 会对数组进行一次随机排序, 防止最坏的情况出现。
public static class Quick<T> where T : IComparable {
public static void Sort(T[] a) {
StdRandom.Shuffle(a);
Sort(a, 0, a.Length - 1);
}
public static void Sort(T[] a, IComparer c) {
StdRandom.Shuffle(a);
Sort(a, 0, a.Length - 1, c);
}
private static void Sort(T[] a, int lo, int hi) {
if (lo >= hi) {
return;
}
int j = Partition(a, lo, hi);
Sort(a, lo, j - 1);
Sort(a, j + 1, hi);
}
private static void Sort(T[] a, int lo, int hi, IComparer c) {
if (lo >= hi) {
return;
}
int j = Partition(a, lo, hi, c);
Sort(a, lo, j - 1, c);
Sort(a, j + 1, hi, c);
}
private static int Partition(T[] a, int lo, int hi) {
int i = lo, j = hi + 1;
while (true) {
while (Less(a[++i], a[lo])) {
if (i == hi) {
break;
}
}
while (Less(a[lo], a[--j])) {
if (j == lo) {
break;
}
}
if (i >= j) {
break;
}
Exch(a, i, j);
}
Exch(a, lo, j);
return j;
}
private static int Partition(T[] a, int lo, int hi, IComparer c) {
int i = lo, j = hi + 1;
while (true) {
while (Less(a[++i], a[lo], c)) {
if (i == hi) {
break;
}
}
while (Less(a[lo], a[--j], c)) {
if (j == lo) {
break;
}
}
if (i >= j) {
break;
}
Exch(a, i, j);
}
Exch(a, lo, j);
return j;
}
public static T Select(T[] a, int k) {
StdRandom.Shuffle(a);
int lo = 0, hi = a.Length - 1;
while (lo < hi) {
int j = Partition(a, lo, hi);
if (j < k) {
lo = j + 1;
}
else if (j > k) {
hi = j - 1;
}
else {
return a[k];
}
}
return a[k];
}
public static T Select(T[] a, int k, IComparer c) {
StdRandom.Shuffle(a);
int lo = 0, hi = a.Length - 1;
while (lo < hi) {
int j = Partition(a, lo, hi, c);
if (j < k) {
lo = j + 1;
}
else if (j > k) {
hi = j - 1;
}
else {
return a[k];
}
}
return a[k];
}
private static bool Less(IComparable v, IComparable w) {
return v.CompareTo(w) < 0;
}
private static bool Less(object v, object w, IComparer c) {
return c.Compare(v, w) < 0;
}
private static void Exch(T[] a, int i, int j) {
var tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
}
三路快速排序是快速排序的的一个优化版本, 将数组分成三段, 即小于基准元素、 等于 基准元素和大于基准元素, 这样可以比较高效的处理数组中存在相同元素的情况, 其它特 征与快速排序基本相同。
C# 代码实现如下:
public static class Quick3<T> where T : IComparable {
public static void Sort(T[] a) {
Sort(a, 0, a.Length - 1);
}
public static void Sort(T[] a, IComparer c) {
Sort(a, 0, a.Length - 1, c);
}
private static void Sort(T[] a, int lo, int hi) {
if (hi <= lo) {
return;
}
int lt = lo, gt = hi;
var v = a[lo];
int i = lo;
while (i <= gt) {
int c = a[i].CompareTo(v);
if (c < 0) {
Exch(a, lt++, i++);
}
else if (c > 0) {
Exch(a, i, gt--);
}
else {
i++;
}
}
Sort(a, lo, lt - 1);
Sort(a, gt + 1, hi);
}
private static void Sort(T[] a, int lo, int hi, IComparer comp) {
if (hi <= lo) {
return;
}
int lt = lo, gt = hi;
var v = a[lo];
int i = lo;
while (i <= gt) {
int c = comp.Compare(a[i], v);
if (c < 0) {
Exch(a, lt++, i++);
}
else if (c > 0) {
Exch(a, i, gt--);
}
else {
i++;
}
}
Sort(a, lo, lt - 1, comp);
Sort(a, gt + 1, hi, comp);
}
private static void Exch(T[] a, int i, int j) {
var tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
}
考虑使用快速排序算法时, 通常应优先考虑三路快速排序。