(1)先从数列中取出一个数作为基准数
(2)分区过程:
将比基准数大于的数全放到它的右边,
小于或等于它的数全放到它的左边
(3)对区间再进行排序
void quickSort( int A[], int p, int r) { if (p < r) { int q = Partition( A, p, r); //步骤(2),其实这样也就确定了基准数的位置, quickSort(A, p, q-1); //所以对步骤(3)的两个区间,就不应该包括基准数 quickSort(A, q+1, r); } }
实现步骤(2)的方法有多种,现在介绍两种:
第一种:从左到右进行查找,用 border 来指定界限( 它的左边都小于或者等于 基准数, 右边大于基准数),
初始值为 p -1
然后用 j 从左到右循环, 当A[j] 小于或者等于基准数, { border++; 交换 A[border] 和 A[ j ] }
void Partirion( int A[], int p, int r) { int x = A[r]; int border = p-1; for (int j = p; j < r; j++) { if( A[j] <= x ) { border++; exchange( A[border] , A[j]); } } exchange( A[border+1], A[r]); return border+1 ; }
例子:
border指向p-1
3 |
8 |
9 |
4 |
6 |
7 |
5 |
p指向3, r指向5
j指向3 , 3 < 5 ,所以
border指向3
3 |
8 |
9 |
4 |
6 |
7 |
5 |
j指向8 , 8 > 5 , 所以
border不会自加,仍然指向3
3 |
8 |
9 |
4 |
6 |
7 |
5 |
j指向9, 9> 5 ,所以
border不会自加,仍然指向3
3 |
8 |
9 |
4 |
6 |
7 |
5 |
j指向4, 4 <5 , 所以
border自加,指向8,
3 | 8 |
9 |
4 |
6 |
7 |
5 |
并且交换4和8的位置
3 | 4 |
9 |
8 |
6 |
7 |
5 |
j指向6, 6 > 5, 所以
border不会自加,仍然指向4
3 |
4 |
9 |
8 |
6 |
7 |
5 |
指向7, 7> 5, 所以
border不会自加,仍然指向4
3 |
4 |
9 |
8 |
6 |
7 |
5 |
然后 j 退出循环, 交换 9 和 5 的位置, 即 border+1 和 r 的位置
3 |
4 |
5 |
8 |
6 |
7 |
9 |
这样基准数5的位置就确定了,然后返回基准数的位置(border + 1)
牢记: border是作为界限,它所指的位置和其所指位置的左边,都小于或者等于基准数
第二种方法:从两边一起向中间查找, 左边找到大于基准数的位置, 右边找到小于或者等于基准数的位置,
然后交换两者的位置。
fore 从左到右查找,back从右到左查找
void Partition( int A[], int p ,int r) { int x = A[r]; int fore = p; int back = r - 1; while( fore < back) { while( A[fore] <= x && fore < back) fore++; while( A[back] > x && fore < back) back--; exchange( A[fore], A[back]); } //退出时一定时fore == back //因为fore和back一个一个的加的 //而且可以肯定fore-1(包括自身) 都小于等于x, // back+1(包括自身) 都大于x //只有fore和back指向的元素不知道与x的大小 if( A[fore] > x) { exchange( A[fore], A[r]); return fore; } else { exchange(A[fore+1],A[r]); return fore+1; } }