理解:
- 对某一数组A,每次选择一个值作为pivot支点,将所有小于pivot的值放在pivot左边,将所有大于pivot的值放在pivot右边,如此将数组分成了左右两部分SubAL,SubAR。
- 对每一部分数组Sub,重复进行以上过程,最终实现完整排序。
#include<iostream>
using namespace std;
template <class T>
void Swap(T&a, T&b){
T tmp = a;
a = b;
b = tmp;
}
template <class T>
T indexOfMax(T a[], int n){
int i,index;
int max = 0;
for (i = 0; i < n; i++){
if (max < a[i]){
max = a[i];
index = i;
}
}
return index;
}
template <class T>
void quicksort(T a[], int n){
//驱动函数
if (n <= 1)return;
int max = indexOfMax(a, n);
Swap(a[n - 1], a[max]);
//将最大的函数移到最右侧,以免leftCursor连续自增,导致数组越界
quickSort(a, 0, n - 2);
}
template <class T>
void quickSort(T a[], int leftEnd, int rightEnd){
if (leftEnd >= rightEnd) return;
int leftCursor = leftEnd;
int rightCursor = rightEnd + 1;
//为了后面的do-while语句作出的协调
T pivot = a[leftEnd];
//将最左端的元素当作支点pivot
while (true){
do{ // 直接do-while,跳过最左端的pivot
leftCursor++;
} while (a[leftCursor] < pivot);
//找到大于等于支点的元素下标
do{
rightCursor--;
} while (a[rightCursor] > pivot);
//找到小于等于支点的元素下标
if (leftCursor >= rightCursor) break;
//如果左游标大于等于右游标,说明遍历已经重合或擦肩而过,放弃交换,该轮结束。
//ps:注意,这里只有两种情况:rightCursor == leftCursor 或 rightCursor == leftCursor-1.
//因为只要经历了前面的第一个do-while循环,我们可以肯定:在leftCursor前面的值都已经小于pivot,
//因此,这些值必然符合第二个do-while的终止条件
Swap(a[leftCursor], a[rightCursor]);
//否则就说明可以交换
}
a[leftEnd] = a[rightCursor];
//将右游标此时停留的位置的值赋给最左端(支点pivot)的值
a[rightCursor] = pivot;
//将支点值赋给右游标停下的位置。
//相当于交换操作。————为什么?如何理解与rightCursor交换的操作而不是与leftCursor交换?
//假设从上述while中break跳出,分析此时leftCursor和rightCursor的位置关系 与 元素大小关系。
//如果leftCursor位置上的元素大于pivot,则rightCursor必然为leftCursor-1;pivot应为中值,故与必然比pivot小的a[rightCursor]交换,如此使得左端的值比pivot小。如果交换了a[leftCursor]就会出现pivot左边出现比pivot大的值。这是错误的。
//如果leftCursor位置上的元素等于pivot,则rightCursor必然等于leftCursor。同理,既然相等则直接交换即可。
//只有以上两种情况的可能性。
quickSort(a, leftEnd, rightCursor - 1);
quickSort(a, rightCursor + 1, rightEnd);
//递归调用该函数,这里可以看出以rightCursor位置的值作为分界点,就是之前的pivot
}
int main(){
const int n = 1000;
int A[n];
int i;
for (i = 0; i < n; i++){
A[i] = ceil(rand());
}
quicksort(A,n);
for (i = 0; i < n; i++){
cout << A[i] << " ";
}
system("pause");
return 0;
}
更为简洁的写法——挖坑理解:
void qSort(vector<int> &a, int left, int rightEnd){
int l = left; // 左游标
int r = rightEnd; //右游标
if(l >= r) return;
int p = a[left]; //取基准数,此时a[left]位置挖出了一个坑待填。
while(l < r){
while(l < r && a[r] >= p) r--;
if(l < r) a[l] = a[r]; // 如果l<r,那么只可能是出现了小于p的数,填入坑。
// 填坑后,a[r]又空出来了,又挖了一个坑。
while(l < r && a[l] < p) l++;
if(l < r) a[r] = a[l]; // 此时出现了大于等于p的数,填入坑
}
// 此时一定有l == r,因为while中无时无刻不在判定l和r的大小
a[l] = p;
qSort(a, left, l - 1);
qSort(a, l + 1, rightEnd);
}