《算法导论》——最坏时间为线性时间的选择算法

《算法导论》——最坏时间为线性时间的选择算法

该算法与期望时间为线性时间的选择算法的最大区别就是该算法的partition中不是随机的以某一个数作为基准,而是将中位数的中位数传入作为基准,返回中位数的中位数在序列中的位置。
步骤如下

1: 将输入数组的n个元素划分为 n/5 组,每组5个元素,且至多只有一组由剩下的 n%5 个元素组成。

2: 寻找 n/5 组中每一组的中位数:首先对每组元素(至多为5个)进行插入排序,然后确定每组有序元素的中位数。

3: 对第2步中找出的 n/5 个中位数,递归调用 select 以找出其中位数 num(如果有偶数个中位数,为了方便,约定 num 是较小的中位数)

4: 利用修改过的partition版本,按中位数的中位数 num 对输入数组进行划分。让 count 比划分的低区中的元素数目多1,因此 num 是第 count 小的元素,并且有 n - count 个元素在划分的高区。

5: 如果 k == count,则返回 num。如果 k < count,则在低区递归调用 select 以找出第 k 小的元素。如果 k > count,则在高区递归查找第 k - count 小的元素。
在这里插入图片描述

#include "iostream"
#include "algorithm"
using namespace std;

void insert_sort(int* a, int l, int h)//插入排序
{
	
	for (int i = l + 1; i <= h; i++)
	{
		int key = a[i];
		int j = i - 1;
		while (j >= l && a[j] > key)
		{
			a[j + 1] = a[j];
			j--;
		}
		a[j + 1] = key;
	}
}

int partition(int* a, int l, int h, int q)//q为中位数的中位数
{
	int j = 0;
	for (int i = l; i <= h; i++)//找到中位数的中位数在数组中的位置
	{
		if (a[i] == q)
		{
			j = i;
			break;
		}	
	}
	swap(a[j], a[h]);//放大索引最大处

	int n = l - 1;
	for (int m = l; m < h; m++)
	{
		if (a[m] < a[h])
		{
			n++;
			swap(a[m], a[n]);
		}
	}
	swap(a[n + 1], a[h]);
	return n + 1; //返回中位数的中位数的基准(左边小于它,右边大于它)
}
int select(int* a, int l, int h, int k)//l,h为数组的边界,k为输入
{
	if (h - l+1 <= 5 )
	{
		insert_sort(a, l, h);
		return a[l + k - 1];
	}
	int group = (h - l + 5) / 5; //每5个数为一组,group为组数
	for (int i = 0; i < group; i++)
	{
		int left = l + 5 * i;
		int right = l + 5 * i + 4 > h ? h : l + 5 * i + 4;//如果边界大于了数组上边界,则用数组上边界作为边界
		insert_sort(a, left, right);
		int mid = (left+right) / 2;
		swap(a[mid], a[l + i]);//把中位数放在前面,方便后面的计算中位数的中位数		

	}
	int q = select(a, l, l + group - 1, (1 + group)/2);//递归找到中位数的中位数
	int m = partition(a, l, h, q);//返回中位数的中位数在当前划分数组里的位置
	int s = m - l + 1;//中位数的中位数是第几大数
	if (s == k)
	{
		//return a[l+k-1];//一定记得要加上L(在执行select(a, m + 1, h, k - s);时,传的k值是减过的)
		return a[m];
	}
	if (s > k)
	{
		select(a, l, m - 1, k);
	}
	else
	{
		select(a, m + 1, h, k - s);
	}
}
  int main(void)
{
	
	int a[10] = { 25,31,89,12,67,53,44,59,71,19 };
	for(int i =1;i<11;i++)
		cout<<"第"<<i<<"大数为:"<< select(a, 0, 9, i)<<endl;
		system("pause");
 }

猜你喜欢

转载自blog.csdn.net/weixin_42809993/article/details/84719354