转自算法导论中文版第9.2节
线性时间期望的选择算法
randomized-select的期望运行时间为Θ(n)。该算饭是一个随机算法,只处理划分的一边。
伪代码
RANDOMIZED-SELECT(A, p, r, i)
1 if p = r
2 then return A[p]
3 q = RANDOMIZED-PARTATION(A, p, r)
4 k = q - p +1
5 if i = k
6 then return A[q]
7 elseif i < k
8 then return RANDOMIZED-SELECT(A, p, q-1 i)
9 else return RANDOMIZED-SELECT(A, q+1, r, i-k)
在算法第3行的RANDOMIZED-PARTATION执行之后,数组A[p...r]被划分成两个(可能是空的)子数组A[p...q-1], A[q+1...r],使得A[p...q-1]中的每一个元素都小于或等于A[q],A[q+1...r]中的每一个元素都大于A[q]。与快速排序中一样,称A[q]为主元素(povit element)。RANDOMIZED-SELECT的第4行计算子数组A[p...q]内的元素个数k,即处于划分低区的元素的个数机上一个主元元素。然后第5行检查A[q]是不是第i小的元素。如果是,就返回A[q]。否则,算法要确定第i小的元素落在两个子数组A[p...q-1]和 A[q+1...r]中的哪一个之中。如果i< k,则要找的元素落在划分的低区中,于是第8行就在低区的子数组中进一步递归选择。如果i>k,则要找的元素落在划分的高区子数组中。因为我们已经知道有k个值(即A[p...q]中的所有元素)小于A[p...r]中的第i小元素,故所求原色必是A[q+1...r]中的第(i-k)元素,它在第9行中被递归地选取。
RANDOMIZED-SELECT的最坏情况运行时间为Θ(n^2),即使是要选择最小元素也是如此,因为在每次划分时可能极不走运,总是按余下的元素中最大的进行划分,而划分操作需要Θ(n)的时间。该算法的平均情况性能较好,又因为它是随机化的,故没有哪一种特别的输入会导致其最坏情况发生。
#include <stdio.h>
#include <stdlib.h>
void swp(int *a, int *b)
{
int tmp = *a;
*a = *b;
*b = tmp;
}
int randomize(int p, int q)
{
int size = q - p +1;
return p + rand() % size;
}
int partation(int a[], int p, int r)
{
int x = a[r];
int i = p -1;
int j;
for (j = p; j < r; j++) {
if (a[j] <= x) {
i++;
swp(&a[j], &a[i]);
}
}
swp(&a[i+1], &a[r]);
return i+1;
}
int random_partation(int a[], int p, int r)
{
int i;
i = randomize(p, r);
swp(&a[i], &a[r]);
return partation(a, p, r);
}
/*
* int random_select(int a[], int p, int r, int i)
* {
* int q, k;
*
* if (p == r) {
* return a[p];
* }
*
* q = random_partation(a, p, r);
* k = q - p + 1;
*
* if (i == k) {
* return a[q];
* } else if (i < k) {
* return random_select(a, p, q -1, i);
* } else {
* return random_select(a, q +1, r, i -k);
* }
* }
*/
int random_select(int a[], int start, int end, int i)
{
int p = start, r = end;
int q, k;
if (p == r) {
return a[p];
}
while (p < r) {
q = random_partation(a, p, r);
k = q - start + 1;
if (i == k) {
return a[q];
} else if (i < k) {
r = q - 1;
} else {
p = q + 1;
}
}
}
int main(void)
{
int k;
int a[] = {
0, 1, 2, 3, 4, 5, 9, 7, 8, 6, 10,
};
k = random_select(a, 1, 10, 9);
printf("the 9th is %d", k);
return 0;
}