编程之美 之 找最大的K个数 堆和快速选择 实现

多种方法 http://blog.csdn.net/v_july_v/article/details/6370650

在增加一种:败者树http://blog.csdn.net/fisher_jiang/article/details/2473698

下面是自己实现的两种:

// 编程之美-寻找最大的K个数.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <iostream>
using namespace std;
#define N 3900
#define K 39
double Arr[N];// 可以利用原来数组中的第1个到 第K个 作为堆  但是会改变原来的数组
/*
double heap[K+1];//堆数组 K个元素的堆(o(N*log(K))) 比 建立 N个元素的堆o(N+K*log(N))的时间复杂度高,但是用 K个 空间复杂度较好
void HeapJust(double *arr, int r,int m)//小跟堆
{
	for(int j=2*r;j<=m;j*=2)
	{
		if(j+1<=m&&arr[j]>arr[j+1])j++;
		if(arr[r]<arr[j])break;
			double temp=arr[j];
			arr[j]=arr[r];
			arr[r]=temp;
			r=j;
	}
}
int _tmain(int argc, _TCHAR* argv[])
{
	for(int i=0;i<N;i++)
		Arr[i]=rand()%100000;
	for(int i=0;i<K;i++)
		heap[i+1]=Arr[i];
	for(int i=K/2;i>=1;i--)// construct the heap canbe  o(n)
		HeapJust(heap,i,K);

	for(int i=K;i<N;i++)//遍历数组 中剩余的所有元素,找出最大的K个
	{
		if(Arr[i]>heap[1])
		{
			heap[1]=Arr[i];//堆中最小的元素被替代
			HeapJust(heap,1,K);
		}
	}

	for(int i=1;i<=K;i++)
		cout<<heap[i]<<" ";
		cout<<endl;
	 for(int i=0;i<N;i++)
		if(Arr[i]>32756)cout<<Arr[i]<<" ";
	//cout<<"result is "<<Arr[0]<<endl;
	system("pause");
	return 0;
}*/

//http://blog.csdn.net/v_JULY_v/article/details/6403777
//快速排序 采用 中位数的中位数 即是在最坏的情况下 可达到 线性时间复杂度(常数比较大)  一定是要改变原来的数组了,否则开辟 N 大空间 
//5个数而言 6次比较 可找中间元,7次比较可 排序
// 进过测试 堆排序方法效率远比 快速选择方法 高  当N为五位数时,选择排序执行困难,虽然算法本身的时间复杂度为O(n),
// 但算法其他的额外开销占用大量时间,而且采用递归,递归每次又要开辟一定的空间 来存储临时的中位数。会改善吗?待续。。。
void Swap(double *p,double *q)
{
	double temp = *p;
	*p=*q;
	*q=temp;
}
double middle5(double *Arr, int i)//以i为起始点的5个元素
{
	if(Arr[i]>Arr[i+1]) Swap(&Arr[i],&Arr[i+1]);
	if(Arr[i+2]>Arr[i+3]) Swap(&Arr[i+2],&Arr[i+3]);
	if(Arr[i+1]>Arr[i+3]) 
	{
		Swap(&Arr[i+1],&Arr[i+3]);
		Swap(&Arr[i],&Arr[i+2]);
	}// Arr[i]  Arr[i+3] canbe excluded
	if(Arr[i+2]>Arr[i+4])Swap(&Arr[i+2],&Arr[i+4]);
	if(Arr[i+1]>Arr[i+4])
	{
		Swap(&Arr[i+1],&Arr[i+4]);//Arr[i+4]canbe excluded
		Swap(&Arr[i],&Arr[i+2]);
	}
	if(Arr[i+1]>Arr[i+2]) return Arr[i+1];
	else return Arr[i+2];
}
double middle4(double *Arr, int i)//以i为起始点的4个元素 返回第二大的
{
	if(Arr[i]>Arr[i+1]) Swap(&Arr[i],&Arr[i+1]);
	if(Arr[i+2]>Arr[i+3]) Swap(&Arr[i+2],&Arr[i+3]);
	if(Arr[i+2]>Arr[i+1]) 
	return Arr[i+1];
	else 
	{
		if(Arr[i+2]>Arr[i]) return Arr[i+2];
		else return Arr[i];
	}
}
double middle3(double *Arr, int i)//以i为起始点的3个元素
{
	if(Arr[i]>Arr[i+1]) Swap(&Arr[i],&Arr[i+1]);
	if(Arr[i+2]>Arr[i+1])
	return Arr[i+1];
	else 
	{
		if(Arr[i+2]>Arr[i]) return Arr[i+2];
		else return Arr[i];
	}
}
double middle2(double *Arr, int i)//以i为起始点的2个元素
{
	if(Arr[i]>Arr[i+1]) Swap(&Arr[i],&Arr[i+1]);
		return Arr[i];
}
double middle1(double *Arr, int i)//以i为起始点的1个元素
{
	return Arr[i];
}
double selectpivot(double *Arr,int i,int n)// 寻找起始点为i 长度为n的一段的中位数
{
	int m=n/5,r=n%5,j,k;
	if(r!=0)
	{
		double *pivot=new double[m+1];//存储n个元素的中位数
		for(j=i,k=0;k<m&&j<i+n;j+=5,k++)
		{
			pivot[k]=middle5(Arr,j);
		}
		if(r==4)
			pivot[m]=middle4(Arr,5*m+i);
		else if(r==3)
			pivot[m]=middle3(Arr,5*m+i);
		else if(r==2)
			pivot[m]=middle2(Arr,5*m+i);
		else if(r==1)
			pivot[m]=middle1(Arr,5*m+i);
		if(m+1>5) return selectpivot(pivot,0,m+1);
		else if(m+1==5) return middle5(pivot,0);
		else if(m+1==4) return middle4(pivot,0);
		else if(m+1==3) return middle3(pivot,0);
		else if(m+1==2) return middle2(pivot,0);
		else if(m+1==1) return middle1(pivot,0);
	}
	else
	{
		double *pivot=new double[m];//存储n个元素的中位数
		for(int j=i,k=0;k<m&&j<i+n;j+=5,k++)
		{
			pivot[k]=middle5(Arr,j);
		}
		if(m>5) return selectpivot(pivot,0,m);
		else if(m==5) return middle5(pivot,0);
		else if(m==4) return middle4(pivot,0);
		else if(m==3) return middle3(pivot,0);
		else if(m==2) return middle2(pivot,0);
		else if(m==1) return middle1(pivot,0);
	}
}
double quickSelect(double *Arr, int i, int n,int k)//form i on  n is  the number k 表示 第几小的
{
	double pivot = selectpivot(Arr,i,n);
	//对于所有的数各不相同的情况,要找到pivot的位置,才能保证左面的都比其小,右面的都大,若有相同的数,则可以让左面的不比其大,右边的不比其小,可以不用找pivot的位置
	int pos=0;
	for(int j=i;j<i+n;j++)
		if(Arr[j]==pivot)
		{ pos=j; break;}
	int low=i,high=i+n-1;
	Arr[pos]=Arr[high];
	//double temp=Arr[high];
	while(low<high)
	{
		//?while(low<high&&Arr[low]<pivot)low++;
		while(low<high&&Arr[low]>pivot)low++;
		Arr[high]=Arr[low];
		//?while(low<high&&Arr[high]>pivot)high--;
		while(low<high&&Arr[high]<pivot)high--;
		Arr[low]=Arr[high];
	}
	Arr[low]=pivot;
	if(k==low-i+1) return Arr[low];
	else if(k>low-i+1) return quickSelect(Arr,low+1,i+n-1-low,k-(low-i+1));
	else return quickSelect(Arr,i,low-i,k);

 }
int _tmain(int argc, _TCHAR* argv[])
{
	for(int i=0;i<N;i++)
	Arr[i]=rand()%100000;
	//for(int i=0;i<N;i++)
		//cout<<Arr[i]<<" ";
		//cout<<endl;
	 cout<<"result is "<<quickSelect(Arr,0,N,K)<<endl;
	//for(int i=0;i<N;i++)
	//	cout<<Arr[i]<<" ";

	system("pause");
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值