最小堆求topk个数
堆是一个完全二叉树,第i个节点的左子节点编号为2i+1,右子节点编号2i+2
最小堆性质:root->data < root->left->data && root->data < root->right->data
建堆->调整->排序+调整->取前k个数
调整:从第len/2-1个节点开始,len为数组长度,倒数第一个非叶子结点,依次从右往左,从底至上调整堆,满足最小堆性质.
[4,5,1,6,2,7,3,8]
1.为什么用最小堆?
最小堆满足根<左 且根<右的性质,树节点的值随层数增加是递增的。维护一个大小为k的堆,树中共有k个结点,其中堆顶也就是根结点的值是最小的。
当数组中的值大于堆顶元素时,用该值替换堆顶,并调整堆,使其满足最小堆的性质。排序结束时,堆中的k个元素就是最大的k个数
假设现在有7个数[2,5,4,3,1,6,7],要求前top k, k = 3, 先构建一棵大小为k的堆,求解过程如下图,如果当前数 > 堆顶元素,则替换之,调整堆, 所有的元素遍历完后,最后的堆就是要求的 top k
2.如何处理原数组中有重复元素的情况?
替换堆顶只有当 当前排序的元素大于堆顶才会替换,否则舍弃该元素。
也可以事先对数组进行去重处理
Java:
import java.util.*;
public class topK {
public static int findK(ArrayList<Integer> arr,int k){
ArrayList<Integer> heap = new ArrayList<>();
for(int i=0;i<k;i++){
heap.add(arr.get(i));
}
createHeap(heap,k);
for(int i=k;i<arr.size();i++){
if(heap.get(0)<arr.get(i)){ //根结点值最小,替换根结点的值
heap.set(0,arr.get(i));
heapRify(heap,0,k);
}
}
return heap.get(0);
}
public static void createHeap(ArrayList<Integer>arr,int k){
for(int i=k/2-1;i>=0;i--){
heapRify(arr,i,k);
}
}
public static void heapRify(ArrayList<Integer> arr,int i,int len){
int left = 2*i+1;
int right = 2*i+2;
int largest = i;
if(left<len && arr.get(largest)>arr.get(left)){ //建立的是小根堆,根结点值最小,树的层数越深,值越大
largest = left;
}
if(right<len && arr.get(largest)>arr.get(right)){
largest = right;
}
if(largest != i){
int tmp = arr.get(largest);
arr.set(largest,arr.get(i));
arr.set(i,tmp);
heapRify(arr,largest,len);
}
}
public static void quicksort(ArrayList<Integer> arr, int left,int right){
if(left>=right){
return;
}
int povit = arr.get(left);
int i=left,j=right;
while(i<j){
while(i<j&&arr.get(j)>=povit){
j--;
}
while(i<j&&arr.get(i)<=povit){
i++;
}
if(i!=j){
int tmp = arr.get(i);
arr.set(i,arr.get(j));
arr.set(j,tmp);
}
}
arr.set(left,arr.get(i));
arr.set(i,povit);
quicksort(arr,left,i-1);
quicksort(arr,i+1,right);
}
public static void main(String[] args) {
//输入格式[3,1,2,2,5,4],2
Scanner sc = new Scanner(System.in);
String line = sc.nextLine();
int k = line.charAt(line.length()-1) - '0';
ArrayList<Integer> arr = new ArrayList<>();
for(int i=0;i<line.length()-3;i++){
char ch = line.charAt(i);
if(ch==','||ch=='['){
continue;
}
int num = ch-'0';
if(! arr.contains(num)){
arr.add(num);
}
}
int res = findK(arr,k);
quicksort(arr,0,arr.size()-1);
System.out.println(arr.get(arr.size()-k));
System.out.println(res);
}
}
C++
#include<vector>
#include<iostream>
using namespace std;
class Solution {
public:
vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
if(input.size()==0 || k==0){
return vector<int>();
}
if(k>input.size()){
return vector<int>();
}
createHeap(input);
int len = input.size();
for(int i=0;i<input.size();i++){
int last = input[len-1];
input[len-1] = input[0];
input[0] = last;
len -= 1;
heapRify(input,0,len);
}
return vector<int>(input.begin(),input.begin()+k);
}
void heapRify(vector<int> &input,int i,int len){
int left = 2*i+1;
int right = 2*i+2;
int largest = i;
if(left<len && input[left]>input[largest]){
largest = left;
}
if(right<len && input[right]>input[largest]){
largest = right;
}
if(largest !=i){
int tmp = input[largest];
input[largest] = input[i];
input[i] = tmp;
heapRify(input,largest,len);
}
}
void createHeap(vector<int> &input){
int len = input.size();
for(int i=len/2-1;i>=0;i--){
heapRify(input,i,len);
}
}
};
int main(){
int k = 4;
Solution s;
vector<int> nums = {4,5,1,6,2,7,3,8};
vector<int> v = s.GetLeastNumbers_Solution(nums,k);
return 0;
}