1 算法描述与分析
(1)问题的理解与描述
序列的划分问题描述如下:
输入:序列A[p...r]
输出:下标q(p≤q≤r),原序列A[p...r]的一个重排:使得A[p...q]中的元素值不超过A[q](=原A[r]的值),A[q+1...r]中的元素值均大于A[q]。
解决此问题的算法对序列A[p...r]进行原地重组。算法选择元素x=A[r]作为主元素,以它为分界点对序列A[p...r]进行划分,目标是将其分成前后两段A[p...q]和A[q+1...r],使得A[p...q]中的元素值不超过x,而A[q+1...r]中的元素值大于x。 算法维护两个下标值i和j,初始值分别为p-1和p。让j在[p...r]中扫描,若A[j]≤A[r],则将A[j]与A[i+1]交换,然后i增加1。这样,在此过程中,A[p...i]中的元素均不超过A[r],而A[i+1...j]中的元素大于A[r]。随着j的增加,A[p...i]和A[i+1...j]也随之增长,最后j达到r-1,并将A[r+1]和A[r]交换,返回i+1即为所求的q。
例如对下图中所示的序列进行的操作:
图:对一个样本序列的划分操作。浅蓝色数组元素都在第一部分A[p...i]中,其值都不大于x。深蓝色数组元素都在第二部分A[i+1,j]中,其值都大于x。无阴影元素是尚未进入上述两个部分的元素,黑色元素是基准元素。(a)初始的序列和变量设置。没有任何元素进入两部分。(b)~(h)表示第3~6行的For循环的每一次重复。黑色双向箭头表示第6行的元素交换操作。(i)表示上述循环终止后第7行执行的元素交换操作。
(2)算法的伪代码描述
PARTITION(A, p, r)
1 x ← A[r]
2 i ← p - 1
3 for j ← p to r -1
4 do if A[j] ≤ x
5 then i ← i + 1
6 exchange A[i] ←→ A[j]
7 exchange A[i+1] ←→ A[r]
8 return i + 1
算法:解决划分问题的PARTITION算法
(3)算法的正确性
(4)算法的运行时间
2 程序实现
/**
* @MethodName:partition
* @Description: 该方法用于序列的划分
* @param a 用于存储序列的数组
* @param p 序列划分的开始位置
* @param r 序列划分的结束位置
* @return 序列划分的分界点
*/
public static int partition(int []a, int p, int r){
int x = a[r];//设定基准元素为a[r]
int i = p - 1;//用于记录序列前一部分的最后一个元素位置,初始为p-1
int j = p;//用于循环扫描序列,范围为p~r-1
//循环扫描序列
for(j = p;j < r; j++ ){
//如果在p~r-1中找到某一个元素比基准元素小,则将其交换至第一部分尾部,
//对应元素交换至j处
if(a[j]<=x){
//由于第一部分即将新添加一个元素a[j],所以i加1(为a[j]要交换至的位置)
i++;
//交换j和i处元素
swap(a, j, i);
}
}
//最后交换基准元素a[r]至第一部分尾部
swap(a, i+1, r);
//返回序列划分的分界点位置i+1
return i+1;
}
/**
* @MethodName:swap
* @Description: 用于交换序列中两个位置的元素
* @param a 用于存储序列的数组
* @param p 要交换的第一个元素的位置
* @param r 要交换的第二个元素的位置
*/
private static void swap(int []a, int p, int r){
int t = a[p];
a[p] = a[r];
a[r] = t;
}