线性时间选择

给定线性序集中n个元素和一个整数k,1≤k≤n,要求找出这n个元素中第k小的元素。

Step1. 从a[]中选取划分基准元素,将a[0:n-1]划分为2部分,得到A[0:q]:
1、a[0:q],q+1个元素,包括a[q]

2、a[q+1, n-1],n-q-1个元素

Step2. 
1、L=q+1>=k, q>=k-1, 则左半段至少有k个不大于x=a[q]的小元素,第k小的元素在左半段,递归搜索a[0:q]

2、L=q+1<k, q<k-1, 左半段比x=a[q]小的元素少于k个,x=a[q]不可能是第k小元素,第k小的元素在右半段,并且为右半段的第k-q小,递归搜索a[q+1:n-1]

为降低最坏复杂性,要求尽可能保证划分后的2个子集大小接近,相差过大将导致不平衡。

解决思路:以n(例如5)的长度将数组分割成若干小段,通过排序取到个小段的中位数,再取各中位数的中位x,以此为基准划分至少与位于左半部分的3n/10-1个元素小于x,保证划分后的子集长度相近

原题:采用线性时间选择算法,根据基站k-dist距离,挑选出
k-dist值最小的基站
k-dist第5小的基站
k-dist值第50小的基站
k-dist值最大的基站

#include<iostream>
#include<string>
#include<fstream>
#include<cstdlib>
#define max 2000
using namespace std;

typedef struct Basestation
{
	int ENODEBID;
	float LONGITUDE;//经度 
	float LATITUDE;//纬度 
	float K_DIST ;
 }Basestation;
 
Basestation a[max];

int Max(int a, int b);
int Partition(Basestation a[], int p, int r, Basestation z);
int RandomizedPartition(Basestation a[], int p, int r);
Basestation Select(Basestation a[], int p, int r, int ak);

int main()
{
	int i = 1;
	int num = 0;
	Basestation temp;
	ifstream file;
	file.open("Data_Of_Basestation.txt", ios::in);
	if(file.bad())
	{
		cout<<"打开文件时发生错误"<<endl;
		return 0;
	}
	while(!file.eof())
	{
		file>>a[i].ENODEBID>>a[i].LONGITUDE>>a[i].LATITUDE>>a[i].K_DIST;
		i++;
		num++;
		//cout<<a[i].ENODEBID<<" "<<a[i].LONGITUDE<<" "<<a[i].LATITUDE<<" "<<a[i].K_DIST<<endl;
		file.get();
		if(file.peek()==EOF)
			break;
	}
	cout<<"==============================================================================="<<endl;
	cout<<"\t\t\t基站编号 "<<"\t"<<" 基站经度 "<<"\t"<<" 基站纬度 "<<"\t"<<" K_DIST"<<endl; 
	cout<<"K_DIST最小的基站是:\t";
	temp = Select(a, 1, num, 1);
	cout<<temp.ENODEBID<<"\t\t"<<temp.LONGITUDE<<"\t\t"<<temp.LATITUDE<<"\t\t"<<temp.K_DIST<<endl;
	cout<<"K_DIST第五小的基站是:\t";
	temp = Select(a, 1, num, 5);
	cout<<temp.ENODEBID<<"\t\t"<<temp.LONGITUDE<<"\t\t"<<temp.LATITUDE<<"\t\t"<<temp.K_DIST<<endl;
	cout<<"K_DIST第五十小的基站是:";
	temp = Select(a, 1, num, 50);
	cout<<temp.ENODEBID<<"\t\t"<<temp.LONGITUDE<<"\t\t"<<temp.LATITUDE<<"\t\t"<<temp.K_DIST<<endl;
	cout<<"K_DIST最大的基站是:\t";
	temp = Select(a, 1, num, num);
	cout<<temp.ENODEBID<<"\t\t"<<temp.LONGITUDE<<"\t\t"<<temp.LATITUDE<<"\t\t"<<temp.K_DIST<<endl;
	cout<<"==============================================================================="<<endl;
	file.close();
	return 0;
 } 

int Max(int a, int b)
{
	return (a>b)?a:b;
 }

int Partition(Basestation a[], int p, int r, Basestation z)//z表示以数组中第z个为基准 
{
	int i = p;
	int j = r+1;
	int k;
	Basestation beitai;
	for(k=p; k<=r; k++)
	{
		if(a[k].K_DIST==z.K_DIST)
			break;
	}
	beitai = a[k];
	a[k] = a[p];
	a[p] = beitai;
	Basestation x = a[p];
	while(i<j)
	{
		while (a[++i].K_DIST <x.K_DIST && i<r);   //扩展左端a[p:i],左→右搜索大元素a[i]>=x
        while (a[--j].K_DIST >x.K_DIST);               //扩展右端a[j:r],左←右搜索小元素a[i]<=x
		if(i >= j)
		{
			break;
		}
		beitai = a[i];
		a[i] = a[j];
		a[j] = beitai;
	}
	a[p] = a[j];
	a[j] = x;
	return j; 
}

Basestation Select(Basestation a[], int p, int r, int k)
{
	Basestation beitai;
	Basestation final;
	if(r-p<20)//如果数组足够小,简单排序,寻找第k小元素 
	{
		for(int i = 0; i<r-p+1; i++)
		{
			for(int j = p+1; j<=r-i; j++)
			{
				if(a[j].K_DIST<a[j-1].K_DIST)
				{
					beitai = a[j];
					a[j] = a[j-1];
					a[j-1] = beitai;
				}
			}
		}
		return a[p+k-1];
	}
	int s;
	int t;
	for ( int i = 0; i<=(r-p-4)/5; i++ ) 
	{ 
        s= p + 5*i;
    	t = s + 4; 
    	for(int k=0; k<3; k++)
    	{
    		for(int j=s+1; j<=t-k; j++)
    			if(a[k].K_DIST<a[k-1].K_DIST)
    			{
    				beitai = a[j];
					a[j] = a[j-1];
					a[j-1] = beitai;
				}
		}
       beitai = a[p+i];
       a[p+i] = a[s+2];
       a[s+2] = beitai;
     }    
	final = Select(a, p, p+(r-p-4)/5, (r-p+6)/10);
	int i = Partition(a, p, r, final);
	int j = i-p+1;
	if(k==j)
		return a[i];
	else if(k<j)
		return Select(a, p, i, k);
	else
		return Select(a, i+1, r, k-j);
}
点赞