最坏情况下的线性时间的选择算法

在上一篇文章中,我们提到了期望时间为线性的选择算法,而我们这次提到的最坏情况下的线性时间的选择算法。它的成功在于,它不是随机的选择关键数,它选择他们的中数作为关键数,这样提高了算法的速度。

算法的步骤:找出第i个最小的元素

1.将输入数组的n个元素划为【n/5】组,每一组5个元素,且至多只有最后一组由剩下的n mod 5个元素组成;

 

2.寻找每一组的中位数:对每一组元素进行插入排序,然后确定每组有序元素的中位数;

 

3.把每组找出的中位数重组为一个新的数组,找出其中位数x;

 

4.利用PARTITION,按找出的中位数对其进行划分,其中位数对应的下标为k;

 

5.如果i=k;则返回x,如果i<k,则递归的在低分区找第i个最小的元素,如果i>k,则递归的在高分区找第i-k个最小的元素。

 

#include <iostream>
#include <string.h>
using namespace std;


//对每个小组进行插入排序 
void InsertionSort(int *a, int p, int r, int &mid)
{
	int i;
	for (int j = p + 1; j <= r; j++)
	{
		i = j - 1;
		int key = a[j];
		while (i >= p && a[i]>key)
		{
			a[i + 1] = a[i];
			i--;
		}
		a[i + 1] = key;
	}
	int d = r - p + 1;
	if (d % 2 == 0)
	{
		d = d / 2 - 1;
	}
	else
	{
		d = d / 2;
	}
	//cout<<d;
	mid = a[d + p];
}

//寻找数组a的中位数,放到x中
void Select(int *a, int p, int r, int &x)
{
	if (p == r)
	{
		x = a[p];
		return;
	}
	int n = r - p + 1;
	int m = 0;//组数
	if (n % 5 == 0)
	{
		m = n / 5;
	}
	else
	{
		m = n / 5 + 1;
	}

	//将数组划分为五个一组五个一组...,每一组进行插入排序,并在排序后取出每一组的中位数,放入数组b中
	int b[10] = {0};//记录每一组的中数
	int i = p;
	for (int j = 0; j<m; j++)
	{
		int mid;//每一行的中数
		if (i + 4 <= r)//前面的几个整的行
		{
			InsertionSort(a, i, i + 4, mid);//用插入法对其排序
			b[j] = mid;
			i += 5;
		}
		else//最后可能不到5个数的行
		{
			InsertionSort(a, i, r, mid);
			b[j] = mid;
		}
		//cout<<"i="<<i<<" mid="<<mid<<" j="<<j<<" m="<<m<<" r="<<r;system("pause");
	}

	//对包含各分组中位数的数组b,递归调用Select,找出中位数x
	//Select(b, 0, m - 1, x);
	InsertionSort(b, 0, m-1, x);
}

//根据中位数x,对数组a进行划分 
int Partition(int *a, int p, int r, int x)
{
	int index = -1;
	for (int i = p; i <= r; i++)
	{
		if (a[i] == x)
		{
			index = i;//cout<<i;system("pause");
			break;
		}
	}
	if (index == -1)
	{
		cout << "error" << endl;
		exit(1);
	}
	int temp = a[index];
	a[index] = a[r];
	a[r] = temp;

	int i = p - 1;
	for (int j = p; j<r; j++)
	{
		if (a[j] <= x)
		{
			i++;
			int tmp = a[i];
			a[i] = a[j];
			a[j] = tmp;
		}
	}
	int pivot = a[r];
	a[r] = a[i + 1];
	a[i + 1] = pivot;

	return i + 1;
}

//寻找第i小的数
void find(int *a, int p, int r, int i, int &result)
{
	int x;
	Select(a, p, r, x);

	//以x为主元,对数组a进行划分
	int q = Partition(a, p, r, x);
	int k = q - p + 1;
	if (k == i)
	{
		result = a[q];
	}
	else if (k>i)
	{
		find(a, p, q - 1, i, result);
	}
	else
	{
		find(a, q + 1, r, i - k, result);
	}
}

void Output(int *a, int n)
{
	for (int i = 0; i<n; i++)
	{
		printf("%d ", a[i]);
	}
	cout << endl;
}

int main()
{
	int n = 11;
	int a[11];
	for (int i = 0; i<n; i++)
	{
		a[i] = rand() % n;
	}
	Output(a, n);
	int result = -1;
	int i;
	while (1)
	{
		cout << "输入你要查找的第i小:i=";
		cin >> i;
		find(a, 0, n - 1, i, result);
		cout << "i=" << i << " result=" << result << endl;
	}
	//Output(a,n);
	system("pause");
	return 0;
}

 

 

代码我用的是这个师兄的博客点击打开链接

 


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值