top k 算法
2013-10-12 00:02 youxin 阅读(1497) 评论(0) 编辑 收藏 举报对于一个非有序的数组A[p..r],求数组中第k小的元素。
如何考虑
排序(部分排序)就不用说了。。o(nlgn),当然如果在实际情况中要一直取值,当然要排序后,一次搞定,以后都是O(1)
我们这里提供了取一次最K小的一个o(n)的解法,
用了快速排序的一种思想,
关键在于划分只一个部分,我们知道快速排序选择一个pivot对数组进行划分,左边小于pivot,右边大于等于pivot,
所以我们计算左边小于pivot(加上pivot)的个数count总共有多少,如果等于k,正是我们所要的,如果大于k,说明第k小的
数在左边,那就在左边进行我们的递归;否则,在右边,那么说明右边的第k-count小的数就是我们所要的,在右边进行
我们的递归。
3.代码
当k>(r-p+1),返回最大值,当k<1,返回最小值
#include<iostream> using namespace std; #include<time.h> inline void Swap(int a[],int i,int j) { int temp=a[i]; a[i]=a[j]; a[j]=temp; } int Partition(int a[],int p,int r)//根据pivot a[r]来划分数组 { int pivot=a[r]; int low=p-1; int i; for(i=p;i<r;i++) { if(a[i]<=pivot) Swap(a,++low,i); } Swap(a,++low,r); return low; } int RondomPartition(int a[],int p,int r) { int i=p+rand()%(r-p+1);//随机选择一个pivot来划分 Swap(a,i,r); return Partition(a,p,r); } int SelectKMin(int a[],int p,int r,int k) { if(p==r) return a[p]; int q=RondomPartition(a,p,r); int count=q-p+1;//计算 a[p..q]的元素数量 if(k==count)//刚好,返回 return a[q]; else if(k<count)//在前半部分 return SelectKMin(a,p,q-1,k); else //在后半部分 return SelectKMin(a,q+1,r,k-count); //这里的q+1很重要,不然得不出正确的结果 } const int N=100; const int K=10; int a[N]; int main() { int i; for(i=0;i<N;i++) a[i]=i; srand(unsigned(time(NULL))); for(i=0;i<K;i++){//获得0-100不重复随机数 Swap(a,i,i+rand()%(N-i)); } int k; while(cin>>k) { cout<<SelectKMin(a,0,K-1,k)<<endl; } return 0; }
本文实例讲述了C++实现查找中位数的O(N)算法和Kmin算法,分享给大家供大家参考。具体方法如下: 利用快速排序的partition操作来完成O(N)时间内的中位数的查找算法如下: #include <iostream> #include <cassert> #include <algorithm> #include <iterator> using namespace std; int array[] = {1, 2, 10, 8, 9, 7, 5}; const int size = sizeof array / sizeof *array; int partition(int *array, int left, int right) { if (array == NULL) return -1; int pos = right; right--; while (left <= right) { while (left < pos && array[left] <= array[pos]) left++; while (right >= 0 && array[right] > array[pos]) right--; if (left >= right) break; swap(array[left], array[right]); } swap(array[left], array[pos]); return left; } int getMidIndex(int *array, int size) { if (array == NULL || size <= 0) return -1; int left = 0; int right = size - 1; int midPos = right >> 1; int index = -1; while (index != midPos) { index = partition(array, left, right); if (index < midPos) { left = index + 1; } else if (index > midPos) { right = index - 1; } else { break; } } assert(index == midPos); return array[index]; } void main() { int value = getMidIndex(array, size); cout << "value: " << value << endl; } 寻找kmin算法如下: 15 int findKMin(int *array, int size, int k) { if (array == NULL || size <= 0) return -1; int left = 0; int right = size - 1; int index = -1; while (index != k) { index = partition(array, left, right); if (index < k) { left = index + 1; } else if (index > k) { right = index - 1; } else { break; } } assert(index == k); return array[index]; } 希望本文所述
利用最小堆
此种方法为常用方法,建立一个大小为K的最小堆。每次遍历数组时,需要判断是否需要加入堆中。
堆中存储着的是最大的k个数字,但是若是需要插入堆中,则需要对堆进行调整时间为o(log k)。
全部的时间复杂度为o(n * log k)。
这种方法当数据量比较大的时候,比较方便。因为对所有的数据只会遍历一次,第一种方法则会多次遍历
数组。 如果所查找的k的数量比较大。可以考虑先求出k` ,然后再求出看k`+1 到 2 * k`之间的数字,然后
一次求取。
#include<iostream> using namespace std; void minHeapify(int a[],int heapSize,int i) { int l=2*i+1; int r=2*i+2; int smallest; if(l<heapSize && a[l]<a[i]) smallest=l; else smallest=i; if(r<heapSize && a[r]<a[smallest]) smallest=r; if(smallest!=i) { swap(a[i],a[smallest]); minHeapify(a,heapSize,smallest); } } void bulidMinHeap(int a[],int n) { int heapsize=n; for(int i=n/2-1;i>=0;i--) minHeapify(a,heapsize,i); } #define aSize 10; int main() { int a[10]={4,1,3,2,16, 9,10,14,8,7}; int n=sizeof(a)/sizeof(a[0]); int k=3; bulidMinHeap(a,k); //如果X比堆顶元素Y小,则不需要改变原来的堆 //如果X比堆顶元素Y大,那么用X替换堆顶元素Y,在替换之后,X可能破坏了最小堆的结构,需要调整堆来维持堆的性质 for(int i=n-1;i>=k;i--)//>=k,因为第0个也存 { if(a[0]<a[i]) { swap(a[0],a[i]); minHeapify(a,k,0); } } for(int i=0;i<k;i++) cout<<a[i]<<endl; }
上面的输出:10,16,14,可以看到第k大的存在a[0],为10.
http://blog.csdn.net/sjf0115/article/details/8603441
http://blog.csdn.net/rein07/article/details/6742933
堆的设计对于处理top K问题十分方便。首先设置一个大小为K的堆(如果求最大top K,那么用最小堆,如果求最小top K,那么用最大堆),然后扫描数组。并将数组的每个元素与堆的根比较,符合条件的就插入堆中,同时调整堆使之符合堆的特性,扫描完成后,堆中保留的元素就是最终的结果。说到调整堆,不得不提的是调整的算法,分为两类:向上调整和向下调整
有了堆的基本操作,top K问题就有了一个基础(当然也可以完全不用堆解决top K问题)。以最小top K问题为例(此时需要建立大小为k的最大堆),top K的求解过程是:扫描原数组,将数组的前K个元素扔到堆中,调整使之保持堆的特性。对于k之后的元素,如果比堆顶元素小,那么替换堆顶元素并调整堆,扫描是数组完成后,堆中保存的元素就是最终的结果。
————————————————
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
2012-10-12 关于闭包一个常见的错误
2011-10-12 Dan Saks
2011-10-12 '=' : left operand must be l-value 左值和右值
2011-10-12 结构体前面加不加typedef有什么区别?
2011-10-12 fatal error C1083: 无法打开包括文件:“iostream.h”: No such file or directory