c语言队列优先级编程,优先级队列解决top-K问题(C语言实现)

对于数组{10, 4, 3, 6, 5, 8, 9, 3},希望求第k (k = 3) 大的数据。

如果用传统的思路解决这个问题:先用快速排序对整个数组排序。然后取第K个元素,这种方式的时间复杂度为O(N * logN)。如果n非常大,对整个数组排序需要使用外部排序 (内存中放不下,需要硬盘辅助排序)。如果使用大小为k的小顶堆,就有可能在内存中完成这个任务。也就是说,这个方法不仅可以降低时间复杂度,还可以降低内存的消耗。

这里用小顶堆的方式进行计算:取前面3个元素,建立一个小顶堆,然后遍历其它元素,如果某个元素比堆顶元素还要小,则丢弃该元素;如果该元素比堆顶元素大,则用它代替堆顶元素,并维护这个堆。最后,这个堆中的元素就是这个数组中最大的三个元素,这个堆顶元素就是第3大的数据。如果希望得到的是最大的3个数据,输出这个堆中的三个元素即可。

这个堆一共k个元素,所以维护一次堆的时间复杂度为O(logK)。遍历(N - k)个元素,并维护堆的时间复杂度为O((N - k) * logK)。

最好的情况,K = 1,不需要维护堆,所以时间复杂度为O(N);最差的情况,K = N,也就是用小顶堆求最小的那个元素,这时几乎所有时间都花费在建立一个大小为N的小顶堆,时间复杂度为O(N * logN)。与进行堆排序后取第K个元素的时间复杂度一样。但是如果是这种情况,用大顶堆计算,时间复杂度又是O(N);平均情况,如果K == N / 2,则时间复杂度为O(N * logN);一般情况下,k的值远小于N,所以时间复杂度为O(N * logK)。

这种求top-K的方法属于部分排序。如果k < logN,可以考虑用选择排序,如果k >= logN,可以使用本方法。

完整的代码如下:

#include

#include

int arr [] = {10, 4, 3, 6, 5, 8, 9, 3};

int sizeOfHeap = 3; //第k (k = 3) 大的数据,维护一个大小为3的小顶堆

int size = 0; //数组的大小

void swap(int i, int j) //交换数组arr中编号为i和j的两个元素

{

int temp = arr[i];

arr[i] = arr[j];

arr[j] = temp;

}

//寻找3个元素中的最小值

int getMin(int father, int leftSon, int rightSon)

{

int minIndex = father; //较小元素的index

int min = arr[father]; //较小元素的值

if(leftSon < sizeOfHeap && arr[leftSon] < min)

{

min = arr[leftSon];

minIndex = leftSon;

}

if(rightSon < sizeOfHeap && arr[rightSon] < min)

{

//min = arr[rightSon]; //这一行代码是多余的

minIndex = rightSon;

}

return minIndex; //返回最小值的元素的编号

}

//向下调整函数

//传入一个需要向下调整的结点编号i。

//这里一直传入0,即从堆的顶点开始向下调整

void shiftDown(int i)

{

int minIndex = 0; //较大元素的index

while(true)

{

//左儿子的编号是i * 2 + 1,右儿子的编号是i * 2 + 2

minIndex = getMin(i, i * 2 + 1, i * 2 + 2);

if(minIndex != i) //父节点不是最小结点时

{

swap(minIndex, i); //交换,使父节点成为最小结点

i = minIndex; //更新i结点,继续向下调整

}

else //父节点是最小结点时

{

break; //退出循环

}

}

}

//建立小顶堆的函数

void createMinHeap()

{

//从最后一个非叶结点到第0个结点一次进行向上调整

//维护一个3个元素的堆

for (int i = sizeOfHeap / 2; i >= 0; i--)

{

shiftDown(i);

}

}

int main()

{

int size = sizeof(arr) / sizeof(int); //计算数组大小

createMinHeap(); //建堆

//遍历剩余元素

for (int i = sizeOfHeap; i < size; i++)

{

printf("开始: %d, %d, %d\r\n", arr[0], arr[1], arr[2]);

//如果其它元素比堆顶元素还要小,则丢弃该元素

if (arr[i] <= arr[0])

{

printf("新元素:%d,丢弃。剩余: %d, %d, %d", arr[i], arr[0], arr[1], arr[2]);

}

else

{

//如果比堆顶元素大,则用它代替堆顶元素,并维护这个堆。

arr[0] = arr[i];

//这个堆只有三个元素,每次都要判断0元素是否要向下调整

shiftDown(0);

printf("新元素:%d,替换首元素,调整小顶堆后:%d, %d, %d", arr[i], arr[0], arr[1], arr[2]);

}

printf("\r\n\r\n");

}

//最后,这个堆顶元素就是第3大的数据。剩余两个元素的值的次序是无所谓的

printf("第3大的元素是:%d\r\n\r\n", arr[0]);

printf("数组中最大的三个元素是:%d, %d, %d\r\n\r\n", arr[0], arr[1], arr[2]);

printf("能保证首元素比另外两个元素小,但不能保证后面两个元素的次序。");

return 0;

}

运行结果:

9c6ac740bf503f3fa03b08ed8b82c55e.pngTop-K问题是很常见的,例如用搜索引擎搜索的时候,可能搜出10000个结果,但是网站上只显示最前面的10个结果。如何快速的从这10000个结果中找到前10个结果,这就是典型的top-K问题。

我们在药物设计时可能设计出上万个结构 (甚至更多),药物设计者往往只是对得分最高的10个结构感兴趣。我们需要把设计出来的药物与模板药物进行对比和打分,选取得分最高的1000个结构,这也是用top-K方法解决的。在得到这1000个结构之后,还需要进行一次堆排序,将数据输出。这样就可以得到得分从高到低的结构。Top-K问题的终极解决方案是本书的压轴算法:线性查找。

本文分享 CSDN - wangeil007。

如有侵权,请联系 support@oschina.cn 删除。

本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值