求数组中最小的k个数以及海量数据最大堆、multiset解决方案
来源:互联网 发布:mac杀毒软件知乎 编辑:程序博客网 时间:2024/05/16 00:29
【题目】
输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。
【方案一】
主要有两种方案。第一是利用我们熟知的 partition 算法,它是快速排序的核心,相信每个人都会。它可以用来求取数组的任意第 k 大的数,时间复杂度是O(n)。我们不断对数据 partition,当它返回的 index 为第 k-1 是,那么就说明前 k 个数(包括 index对应的数)就是最小的 k 个数了。因为 index 对应数的左侧都比它小,一共 0~k-2 即 k-1 个,加上它自己,就是 k 个了。
代码:
class Solution {public: vector<int> GetLeastNumbers_Solution(vector<int> input, int k) { const int size = input.size(); vector<int> res; if(size == 0 || k <= 0 || k > size) return res; if(k == size) return input; int start = 0; int end = size - 1; int index = partition(input, start, end); while(index != k - 1){ if(index > k - 1) end = index - 1; else start = index + 1; index = partition(input, start, end); } for(int i=0; i<k; ++i) res.push_back(input[i]); return res; } private: int partition(vector<int>& arr, int start, int end){ //int index = ( [start, end] (void) //我试图利用随机法,但是这不是快排,外部输入不能保证end-start!=0,所以可能发生除零异常 // {return random()%(end-start)+start;} )(); //std::swap(arr[start], arr[end]); int small = start - 1; for(int index=start; index<end; ++index){ if(arr[index] < arr[end]){ ++small; if(small != index) std::swap(arr[small], arr[index]); } } ++small; std::swap(arr[small], arr[end]); return small; }};
【方案二】
上面的 partition 算法有两个缺点,其一必须修改原数组元素(除非你拷贝出来,那也太蠢了),其二是不能针对海量数据。所以就有了最大堆的解法。我们用数组前 k 个元素建立 k 个节点的最大堆,后续输入如果小于最大堆的最大值,即头部,那么恭喜它,它入选了。然后把当前最大堆头部换成新元素,重新堆化。继续,循环,直到数据输入完毕。最大堆中剩余的 k 个元素即为所求。
代码:
class Solution {public: vector<int> GetLeastNumbers_Solution(vector<int> input, int k) { const int size = input.size(); vector<int> heap; if(size == 0 || k <= 0 || k > size) //错误返回空 return heap; if(k == size) //大小相等直接返回 return input; heap.resize(k); //不能用reserve for(int i=0; i<k; ++i) //将前k个分配给堆 heap[i] = input[i]; for(int i=(k>>1)-1; i>=0; --i) sift_down(heap, i, k); //建堆 for(int i=k; i<size; ++i){ //遍历第[k+1..n],如果小于最大堆顶,就放入堆顶,然后重新堆化 if(input[i] < heap[0]){ heap[0] = input[i]; //放入堆顶 sift_down(heap, 0, k); //堆化 } } return heap; }private: int left_child(const int i){ //得到左孩子下标 return (i << 1) + 1; } void sift_down(vector<int>& heap, int i, const int N){int tmp = heap[i]; //保存目标堆化元素 for(int child = -1; left_child(i)<N; i=child){ //i=child child = left_child(i); if(child != N-1 && heap[child] < heap[child+1]) //找出左右孩子中较大的一个 ++child; if(heap[child] > tmp) //如果大于目标,那就让孩子节点覆盖自己 heap[i] = heap[child]; else break; } heap[i] = tmp; //这句话不能写在上面的break之上,因为有课能i是叶子节点,left_child(i)<N不会进入循环 }};
【方案三】
方案三和方案二理论是一样的,只不过使用了 STL 的 multiset,为什么不用 set 是因为 set 元素不可重复。由于 multiset 内部是红黑树,可以自动排序。我们使用 STL 个 greater<int> 让红黑树由大到小排序,红黑树的 begin() (不是一定头结点)就是最大值了。有了最大值剩下的就和方案二几乎一样了,不再赘述。
代码:
class Solution {public: vector<int> GetLeastNumbers_Solution(vector<int> input, int k) { const int size = input.size(); if(k <= 0 || k > size) return std::vector<int>(); std::multiset<int, std::greater<int> > least_nums; for(auto v : input){ if(least_nums.size() < k) least_nums.insert(v); else{ auto begin = least_nums.begin(); if(v < *begin){ least_nums.erase(begin); least_nums.insert(v); } } } return std::vector<int>(least_nums.begin(), least_nums.end()); }};
0 0
- 求数组中最小的k个数以及海量数据最大堆、multiset解决方案
- 利用MapReduce求海量数据中最大的K个数
- 用堆排序的思想求最小的k个数,处理海量数据
- 二.用最小堆方法找出海量数据中最小的k个数
- 求数组中最小的k个数
- 利用堆求最大或最小的前k个数
- 求数组中最大的k个数
- 求数组中最大的K个数
- 寻找数组中最小的k个数 "最小堆方法"
- 从海量数据中找出最小的k个数
- 海量数据最小k个数
- 用堆排序寻找数组中最大的K个数
- 算法之求数组中最小的K个数
- 算法题:求数组中最小的k个数
- 面试30 求数组中最小的k个数
- 求一个数组中最小的k个数
- 求一个数组中最小的K个数
- 求无序数组中最小的k个数
- offset系列浅析
- 第五章 MAD依赖
- Tomcat之the jre_home environment variable is not defined correctly this environment variable is need
- i注释
- bug单的生命周期
- 求数组中最小的k个数以及海量数据最大堆、multiset解决方案
- java泛型中的K,V,T,E,?,object的意思及其用法
- 2016/12/30javascript考试题
- 项目之谈
- 【网络流24题】太空飞行计划问题
- STM32之CAN ---CAN ID过滤器分析
- POJ 3101 Astronomy .
- Linux:同步信号量的应用
- JavaBean简单及使用