求topK问题

        最近关注一下数据算法面试的时候常考的题目,求topK问题。

本文从两种方法实现,并给出实际数据实验结果以及相关的分析。

以下是堆排序

// Heap sort;
void MaxHeapify(int * src, int pos,int count)
{
	int l = (pos<<1)+1;
	int r = l+1;
	int maxpos = pos;
	if (l<count && src[l]>src[pos])
	{
		maxpos = l;
	}
	if (r<count && src[r]>src[maxpos])
	{
		maxpos = r;
	}
	if (maxpos != pos)
	{
		int temp = src[maxpos];
		src[maxpos] = src[pos];
		src[pos] = temp;
		MaxHeapify(src,maxpos,count);
	}
}

void CreateMaxHeap(int * src,int count)
{
	for (int t = count>>1;t>=0;--t)
	{
		MaxHeapify(src,t,count);
	}
	return;
}

void HeapSort(int * src,int count)
{
	CreateMaxHeap(src,count);
	while (count--)
	{
		int t = src[0];
		src[0] = src[count];
		src[count] = t;
		MaxHeapify(src,0,count);	
	}
}

int _tmain(int argc, _TCHAR* argv[])
{
	int intarray[1024] = {0};
	for(int i = 0; i< 1024; ++i)
	{
		intarray[i] = i;
	}
	HeapSort(intarray,1024);
	return 0;
}

      只要把上面的HeapSort函数稍改一下,我们就可以得到一个求从小到大排序排在第k位置的数值。

int HeapSortGetMinK(int *src,int begin,int end,int k)
{
	CreateMaxHeap(src,k);
	int i = k;
	while (i<=end)
	{
		if (src[i]<src[0])
		{
			int t = src[0];
			src[0] = src[i];
			src[i] = t;
			MaxHeapify(src,0,k);	
		}
		++i;
	}
	return src[0];
}
      其中的参数名字都很好理解。从时间复杂度上算,建一个k大小的堆,o(k);小于n-k次堆的调整,o((n-k)lgk)近似为nlgk/2。整体时间复杂度数量级为nlgn;
      另一种方法是类似快速排序的分法,每次使用partition将数值分为两边,根据k值与当前分值位置的对比,决定下一个partition区间;也就是说比快排少了一边递归;以下是算法的实现:

int partition(int *A,int begin,int end)
{
	int i = begin;
	int j = end;
	int mid = A[begin];
	while (i<j)
	{
		while (A[j]>=mid && i<j)
		{
			--j;
		}
		if (i<j)
		{
			A[i] = A[j];
		}

		while (A[i]<=mid && i<j)
		{
			++i;
		}
		if (i<j)
		{
			A[j] = A[i];
		}
	}
	A[j] = mid;
	return j;
}
// 求一个长为m的数组中第k(k<m)大的数值
// 输入: 数组指针A,数组长度len,k值
// 返回: 第k+1小的数
int PartitionSortGetMinK(int *A,int begin,int end,int k)
{
	int mid = partition(A,begin,end);
	if ( k == mid )
	{
		return A[mid];
	}
	else if (k<mid)
	{
		return PartitionSortGetMinK(A,begin,mid-1,k);
	}
	return PartitionSortGetMinK(A,mid+1,end,k);
}
注意这个partition比算法导论上面那个标准算法要来的快,来的巧妙。从时间复杂度上算,partition算法只需要一次遍历,就能把范围内的数值分两边,复杂度为lgn;而在递归的时候,每次partition的范围从平均上说都减半,算法导论上证明了以上方法的时间复杂度在平均情况下是o(n)。

        那么,从实际数据检验的结果来看会怎么样呢?我们以下就给实验过程:

int getmilliseconds(SYSTEMTIME begin,SYSTEMTIME end)
{
	// 请勿在23:59到00:01之间做此计算
	int hour = end.wHour -begin.wHour;
	int min = end.wMinute - begin.wMinute;
	int seconds = end.wSecond - begin.wSecond;
	int millisecond = end.wMilliseconds - begin.wMilliseconds;
	return ((hour*60+min)*60+seconds)*1000+millisecond;
}

//产生数组的方法,可以使用多种不同的算法产生数组
void GetArray(int *intarray,int count,int seed)
{
	// 以种子产生的随机数
	srand(seed);
	for(int i = 0; i< count; ++i)
	{
		intarray[i] = rand();
	}
}
void test(int *intarray,int len,int k,int seed,int &heapsortresult,int &partitionresult)
{
	int kvalue = 0;
	int inter = 0;
	GetArray(intarray,len,seed);
	SYSTEMTIME begin1,begin2,end1,end2;
	GetSystemTime(&begin1);
	kvalue = HeapSortGetMinK(intarray,0,len-1,k);
	GetSystemTime(&end1);
	inter = getmilliseconds(begin1,end1);
	heapsortresult += inter;
	//printf("\t %d \t",inter);

	GetArray(intarray,len,seed);
	GetSystemTime(&begin2);
	kvalue = PartitionSortGetMinK(intarray,0,len-1,k-1);
	GetSystemTime(&end2);
	inter = getmilliseconds(begin2,end2);
	partitionresult +=inter;
	//printf("%d\n",inter);
	

}
int _tmain(int argc, _TCHAR* argv[])
{
	int maxlen = 1<<24;
	int *intarray = (int*)malloc(maxlen*sizeof(int));
	// 整体数组最长值,每次增长四倍
	for (int len = 1<<2;len<=maxlen;len<<=2)
	{
		printf("++++++length %d \n",len);
		// k值每次增长2倍
		for (int k = 1;k<len;k<<=1)
		{
			printf("\tk %15d",k);
			int partitionresult=0;
			int heapsortresult = 0;
			// 四个种子数方法,计算时间
			for (int i = 1;i<5;i++)
			{
				int seed = i;
				//printf("\t\tseed %d ",i);
				test(intarray,len,k,seed,heapsortresult,partitionresult);
			}
			// 对四次堆方法的时间和取平均值
			heapsortresult>>=2;
			// 对四次划分方法的时间和取平均值
			partitionresult>>=2;
			printf("\t%d\t%d\n",heapsortresult,partitionresult);
		}
		printf("\n");
	}
	free(intarray);
	return 0;
}

实验结果:

++++++length 4 
	k               1	0	0
	k               2	0	0

++++++length 16 
	k               1	0	0
	k               2	0	0
	k               4	0	0
	k               8	0	0

++++++length 64 
	k               1	0	0
	k               2	0	0
	k               4	0	0
	k               8	0	0
	k              16	0	0
	k              32	0	0

++++++length 256 
	k               1	0	0
	k               2	0	0
	k               4	0	0
	k               8	0	0
	k              16	0	0
	k              32	0	0
	k              64	0	0
	k             128	0	0

++++++length 1024 
	k               1	0	0
	k               2	0	0
	k               4	0	0
	k               8	0	0
	k              16	0	0
	k              32	0	0
	k              64	0	0
	k             128	0	0
	k             256	0	0
	k             512	0	0

++++++length 4096 
	k               1	0	0
	k               2	0	0
	k               4	0	0
	k               8	0	0
	k              16	0	0
	k              32	0	0
	k              64	0	0
	k             128	0	0
	k             256	0	0
	k             512	0	0
	k            1024	0	0
	k            2048	0	0

++++++length 16384 
	k               1	0	0
	k               2	0	0
	k               4	0	0
	k               8	0	0
	k              16	0	0
	k              32	0	0
	k              64	0	0
	k             128	0	0
	k             256	0	0
	k             512	0	1
	k            1024	0	0
	k            2048	0	0
	k            4096	0	0
	k            8192	0	0

++++++length 65536 
	k               1	0	0
	k               2	0	0
	k               4	0	0
	k               8	0	0
	k              16	0	0
	k              32	0	0
	k              64	0	0
	k             128	0	0
	k             256	0	0
	k             512	0	0
	k            1024	0	0
	k            2048	1	1
	k            4096	1	0
	k            8192	2	1
	k           16384	2	1
	k           32768	2	0

++++++length 262144 
	k               1	0	0
	k               2	0	0
	k               4	0	0
	k               8	0	0
	k              16	0	0
	k              32	0	0
	k              64	0	0
	k             128	0	0
	k             256	0	0
	k             512	0	2
	k            1024	0	1
	k            2048	1	2
	k            4096	1	2
	k            8192	3	2
	k           16384	4	2
	k           32768	8	2
	k           65536	10	3
	k          131072	11	3

++++++length 1048576 
	k               1	1	0
	k               2	1	1
	k               4	0	0
	k               8	0	0
	k              16	1	0
	k              32	1	0
	k              64	0	0
	k             128	0	1
	k             256	0	0
	k             512	1	1
	k            1024	1	0
	k            2048	2	7
	k            4096	2	7
	k            8192	4	7
	k           16384	7	7
	k           32768	13	8
	k           65536	22	7
	k          131072	34	8
	k          262144	49	10
	k          524288	55	12

++++++length 4194304 
	k               1	4	4
	k               2	4	4
	k               4	4	4
	k               8	4	4
	k              16	4	3
	k              32	4	4
	k              64	4	3
	k             128	4	3
	k             256	4	4
	k             512	4	4
	k            1024	4	4
	k            2048	5	4
	k            4096	6	4
	k            8192	9	33
	k           16384	12	33
	k           32768	21	35
	k           65536	35	36
	k          131072	59	36
	k          262144	99	35
	k          524288	160	38
	k         1048576	278	46
	k         2097152	378	51

++++++length 16777216 
	k               1	15	15
	k               2	15	15
	k               4	15	15
	k               8	15	15
	k              16	15	15
	k              32	15	15
	k              64	15	15
	k             128	15	15
	k             256	15	15
	k             512	16	15
	k            1024	16	16
	k            2048	16	15
	k            4096	18	16
	k            8192	20	15
	k           16384	26	15
	k           32768	36	141
	k           65536	55	142
	k          131072	92	142
	k          262144	155	144
	k          524288	270	145
	k         1048576	528	177
	k         2097152	1102	176
	k         4194304	1877	171
	k         8388608	2274	187



实验中设计,length值从4开始,以4为步长,一直增长到1<<24;对应 的K值从1开始,以2为步长,一直增长至length的一半值;所使用的数据分别用1,2,3,4 为种子数,生产伪随机序列,四次计算时间后得出当前方法计算的平均时间作为打印结果。

结果中,其中length是数组的总长度,k后面的第一个数字为k值,第三列为使用【最大堆方法】求出第k小数所需要的时间(单位是ms),第四列为【划分法】求第k小数所需要的时间,单位为ms。


可以看出,大部分情况下,划分法的速度是占住优势的。但有一个疑问,在以固定的N值,以最大推方法求的过程中,时间随k值稳步上升;而以划分法时间随k增大在某一个时间点增加很快,其他时间点基本稳定。


我怀疑是产生随机数的算法问题,造成数据有一定的规律,并且影响到划分排序的进行。







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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值