IT公司招人,无论笔试还是面试,快排基本上都会问到。
通常情况下,快排实现有两个函数:
- void quickSort(int arr[], int left, int right);
- int partition(int arr[], int left, int right)
快排采用divide-and-conquer策略:
- 选一个pivot value
- Partition
- Sort both parts(最后不需要去合)
根据自己的学习,今天采用两种思路来实现,要想看一下没中方式在实现排序的过程,把最后c++实现
拷贝运行下就好了。
以下Lomuto partition scheme and Hoare partition scheme来自wiki quicksort,然后我将每种实现了一下。
在wiki上,Lomuto partition scheme是以尾元素为基准进行的排序;Hoare partition scheme是以首元素为基准进行的排序。最重要的是理解其中的思想。
在Lomuto partition scheme中,我以首/尾元素来实现了一下;在Hoare partition scheme中,我以中/首元素实现了一下。其实,在理解了两种快速排序的方式以后,在实现时注意的就是当选取不同不同位置元素为基准时,partition返回值以及quicksort边界问题。
用随机生成的数组来测试一下算法有没有问题,测试结果一并附上。
- 以后分析一下partition and time问题
Lomuto partition scheme
This scheme is attributed to Nico Lomuto and popularized by Bentley in his book Programming Pearls[13] and Cormen et al. in their book Introduction to Algorithms.[14] This scheme chooses a pivot which is typically the last element in the array. The algorithm maintains the index to put the pivot in variable i and each time it finds an element less than or equal to pivot, this index is incremented and that element would be placed before the pivot. As this scheme is more compact and easy to understand, it is frequently used in introductory material, although it is less efficient than Hoare’s original scheme.[15] This scheme degrades to O(n2) when the array is already sorted as well as when the array has all equal elements.[9] There have been various variants proposed to boost performance including various ways to select pivot, deal with equal elements, use other sorting algorithms such as Insertion sort for small arrays and so on. In pseudocode, a quicksort that sorts elements lo through hi (inclusive) of an array A. – wiki
尾元素为基准
这个算法,在《编程珠玑》和《算法导论》中都有介绍。
//lo = 0; hi = A.size();
void quicksort1(vector<int> &A, int lo, int hi) {
hi -= 1;
if (lo < hi) {
int p = partition1(A, lo, hi);
quicksort1(A, lo, p);
quicksort1(A, p+1, hi+1);
}
}
int partition1(vector<int> &A, int lo, int hi) {
int pivot = A[hi]; //以尾元素为基准
int i = lo-1;
for (int j = lo; j < hi; ++j) {
if (A[j] <= pivot) {
swap(A[++i], A[j]);
}
}
swap(A[++i], A[hi]);
return i;
}
以首元素为基准
我自己改了一下,改成首元素为基准。
//lo = 0; hi = A.size();
void quicksort2(vector<int> &A, int lo, int hi) {
hi -= 1;
if (lo < hi) {
int p = partition2(A, lo, hi);
quicksort2(A, lo, p);
quicksort2(A, p+1, hi+1);
}
}
int partition2(vector<int> &A, int lo, int hi) {
int pivot = A[lo]; //以首元素为基准
int i = lo;
for (int j = lo+1; j <= hi; ++j) {
if (A[j] <= pivot) {
swap(A[++i], A[j]);
}
}
swap(A[lo], A[i]);
return i;
}
Hoare partition scheme
The original partition scheme described by C.A.R. Hoare uses two indices that start at the ends of the array being partitioned, then move toward each other, until they detect an inversion: a pair of elements, one greater than the pivot, one smaller, that are in the wrong order relative to each other. The inverted elements are then swapped.[16] When the indices meet, the algorithm stops and returns the final index. There are many variants of this algorithm, for example, selecting pivot from A[hi] instead of A[lo]. Hoare’s scheme is more efficient than Lomuto’s partition scheme because it does three times fewer swaps on average, and it creates efficient partitions even when all values are equal.[9][self-published source?] Like Lomuto’s partition scheme, Hoare partitioning also causes Quicksort to degrade to O(n2) when the input array is already sorted; it also doesn’t produce a stable sort. Note that in this scheme, the pivo