例如:最小值,是第1个顺序统计量(i = 1);
最大值,是第n个顺序统计量(i = n);
中位数,当n为奇数时唯一,i = (n+1)/2;当n为偶数时有两个。可取其下中位数。
(1)最大最小值
记录最大最小值。将一对输入元素相互进行比较,然后把较小的与当前最小值比较;
把较大的与当前最大值比较。这样每两个元素需要比较3次。
时间复杂度为3[n/2]。
(2)期望为线性时间的选择算法
RANDOMIZED-SELECT( A, p, r, i )
if p == r
return A[p]
q = RANDOMIZED-PARTITION( A, p, r )// A[p...q...r]
k = q - p + 1
if i == k
return A[q]
else if i < k
return RANDOMIZED-SELECT( A, p, q - 1, i )
else
return RANDOMIZED-SELECT( A, q + 1, r, i - k )
最坏情况运行时间O(n^2),平均O(n)。
给个例子说明此过程,假设现有集合A={32,23,12,67,45,78,10,39,9,58},要求其第5小的元素,假设在划分过程中以总是以最后一个元素为主元素进行划分。执行过程如下图所示:
#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;
// 顺序统计量:找第i个量
template<typename T>// i[1...n]
T randomized_select( T a[], int p, int r, int i );
int main( void )
{
int a[] = { 100, 0, 20, 18, 20, 12, 5, 22, 15, 4, 7 };// 11
int len = sizeof( a ) / sizeof( int );
for( int i = 1; i <= len; ++i ){// 注意结束为len-1
int data = randomized_select<int>( a, 0, len - 1, i );
cout << data << "\t" << endl;
}
return 0;
}
void swap( int* a, int* b )
{
int tmp = *a;
*a = *b;
*b = tmp;
}
// 固定分割算法
template<typename T>
int partion( T a[], int p, int r )
{
int i, j;
i = p - 1;
for( j = p; j < r; ++j ){
if( a[j] < a[r] ){
swap( &a[j], &a[++i] );
}
}
swap( &a[r], &a[++i] );
return i;
}
// 随机分割算法
template<typename T>
int randomized_partion( T a[], int p, int r )
{
if( p > r )
return -1;
static int first_time = true;
if( first_time ){
first_time = false;
srand( ( unsigned int)time( NULL ) );
}
int loc = rand() % ( r - p + 1) + p;
swap( a[r], a[loc] );// 选定最后一个位置为pivot
return partion( a, p, r );
}
// 顺序统计量:找第i个量
template<typename T>
T randomized_select( T a[], int p, int r, int i )
{// i 从1开始
if( p == r )
return a[p];
int q = randomized_partion( a, p, r );
int k = q - p + 1;
if( i == k )
return a[q];//下标曾经写错了a[k],2014-6-11 11:02:09
else if( i < k )
return randomized_select( a, p, q - 1, i );
else
return randomized_select( a, q + 1, r, i - k );
}
(3)最坏情况为线性时间的选择算法
SELECT算法的思想是要保证对数组的划分是个好的划分,对PARTITION过程进行了修改。现在通过SELECT算法来确定n个元素的输入数组中的第i小的元素,具体操作步骤如下:
(1)将输入数组的n个元素划分为n/5(上取整)组,每组5个元素,且至多只有一个组有剩下的n%5个元素组成。(为何是5,而不是其他数,有点不明白。)
(2)寻找每个组织中中位数。首先对每组中的元素(至多为5个)进行插入排序,然后从排序后的序列中选择出中位数。
(3)对第2步中找出的n/5(上取整)个中位数,递归调用SELECT以找出其中位数x。(如果是偶数去下中位数)
(4)调用PARTITION过程,按照中位数x对输入数组进行划分。确定中位数x的位置k。
(5)如果i=k,则返回x。否则,如果i<k,则在地区间递归调用SELECT以找出第i小的元素,若干i>k,则在高区找第(i-k)个最小元素。
SELECT算法通过中位数进行划分,可以保证每次划分是对称的,这样就能保证最坏情况下运行时间为θ(n)。举个例子说明此过程,求集合A={32,23,12,67,45,78,10,39,9,58,125,84}的第5小的元素,操作过程如下图所示:
===================================================
最大值最小值代码实现——
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <stack>
#include <queue>
#include <map>
#include <set>
using namespace std;
template<typename T>
void minmum_m( T a[], int n, T& max, T& min );
int main( void )
{
int a[] = { 100, 0, 20, 18, 20, 12, 5, 22, 15, 4, 7 };
int max, min;
minmum_m( a, 10, max, min );
cout << max << "\t" << min << endl;
return 0;
}
template<typename T>
void minmum_m( T a[], int n, T& max, T& min )
{
if( a == NULL || n < 1 )
return;// 可以设置一个全局变量
max = min = a[0];
int bIdx, sIdx, i;
for( i = 1; i + 1 < n; i += 2 ){
if( a[i] > a[i+1] ){
bIdx = i;
sIdx = i + 1;
}else{
bIdx = i + 1;
sIdx = i;
}
if( min > a[sIdx] )
min = a[sIdx];
if( max < a[bIdx] )
max = a[bIdx];
}
if( i == n - 1 ){
if( min > a[i] )
min = a[i];
if( max < a[i] )
max = a[i];
}
}