MIT:算法导论——6.中位数和顺序统计量:随机快排应用==>随机选择

在一个由n个元素组成的集合中,第i个顺序统计量(order statistic)是该集合中第i小的元素。
例如:最小值,是第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];
	}
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值