使用二分法从数组中找出最小的k个数

来源:互联网 发布:网络推广和seo 编辑:程序博客网 时间:2024/05/20 03:43

问题:从n个元素的无数组中,找出最小的k个元素.


分析:可借助快速排序算法的思想,将数组分而治之——随机选取数组中某个元素的值为中值,并以此中值为分水岭,按大小将所有元素分为两组。之后将偏小一组的元素数(设为t)与k进行比较。

如图所示:


若t = k,则结束操作,偏小数组内元素即为我们想要寻找的k个数.

若t < k,则需要在偏大数组中寻找(k - t)个最小的数,可对偏大数组进行递归操作.

若t > k,则需要在偏小数组中寻找k个最小的数,可对偏小数组进行递归操作.


如此一来,需要解决的问题就是怎样将数组元素正确分组了。此处可借鉴快速排序算法的一部分,用两个迭代器分别从数组的两端向中间遍历,若碰到一对‘错位’的元素就相互置换,直到两迭代器相遇。


插一又不会怀孕】其实TopK问题,还有一个比较常用的解法:先取集合的前k个元素放入容器,然后遍历所有后续元素——若该元素比当前容器内的至少一个元素更小,则用该元素将容器内最大元素替换掉(可先将容器内元素排序,提高效率)。这个方法相对二分法的优势在于:在元素没有索引输入数据个数未知时,仍然能工作。OK,到此为止


二分法具体实现时,特别需要注意的是两迭代器相遇前后的处理。稍有不慎,代码里面就有可能引入一些狡猾的错误。此处我采用的是迭代器每移位一次则判断是否相遇的办法——只在‘配对’成功时,才进行数据交换。


代码如下:

/**************** * TopK.cpp ***************/#include <iostream>#include <time.h>using namespace std;//将Top K个元素置于数组起点端.void GetTopK(int* addr, int size, int k);//交换两个元素的值.void Swap(int* a, int* b);//打印数组.void PrintArray(int* addr, int size);//初始化数组.void InitArray(int* addr, int size);//测试函数.void TestTopK(int* addr, int size, int k);void GetTopK(int* addr, int size, int k){if((NULL == addr)|| (size <= 1) || (k <= 0) || (size <= k)){return;}//两个迭代器,分别从两端往中间移动.int i = 0;int j = size - 1;//记录偏小一组的元素个数.int nBound = 0;//随机取一个元素的值作为中值.srand((unsigned)time(0));int pivot = rand() % size;int pivotValue = addr[pivot];while(i<j){//迭代器i后移.while((i < j) && (addr[i] <= pivotValue)){i++;}//检查是否相遇.if(i == j){//记下分界位置,停止移动.nBound = (addr[i] <= pivotValue) ? (i + 1) : i;break;}//迭代器j前移.while((i < j) && (addr[j] > pivotValue)){j--;}//检查是否相遇.if(i == j){//记下分界位置,停止移动.nBound = (addr[i] <= pivotValue) ? (i + 1) : i;break;}else{//找到配对,交互数据.Swap((int*)addr+i, (int*)addr+j);}// 打印.//PrintArray(addr, len);}if(nBound == k){return;}else if( nBound < k){//在偏大一组中继续处理.GetTopK((int*)addr + nBound, size - nBound, k - nBound);}else{//在偏小一组中继续处理.GetTopK((int*)addr, nBound, k);}}void Swap(int* a, int* b){int temp = *a;*a = *b;*b = temp;}void PrintArray(int* addr, int size){for(int i=0; i<size; i++){cout<<addr[i]<<" ";}cout<<endl;}void InitArray(int* addr, int size){if(size <= 0){return;}const int MAX_NUM = 1000;srand((unsigned)time(0));for(int i=0; i<size; i++){addr[i] = rand() % MAX_NUM;}}void TestTopK(int* addr, int size, int k){InitArray(addr, size);PrintArray(addr, size);GetTopK(addr, size, k);printf("【最小的%d个数】: ", k);PrintArray(addr,k);printf("\n");}int main(){const int SIZE = 50;int testArray[SIZE] = {0};TestTopK(testArray, SIZE, 1);TestTopK(testArray, SIZE, 2);TestTopK(testArray, SIZE, 5);TestTopK(testArray, SIZE, 20);return 0;}


运行结果:



=======================End===========================





原创粉丝点击