赞
踩
电面的时候问了经典的topK问题,没准备到被问了个措不及防,现在把相关知识点记录下来。
假设我们有一些数据,需要按照某种关键字求出这些数据中最小(大)的K个数,即求出这些数据的topK。
当数据量较小的时候,一个简单的想法是直接对数据进行排序,然后取最前面的K个数据;但是当数据量较大,数据无法一次性放入内存的时候应该怎么办呢?
这时候就需要借助堆这种数据结构。堆通常是一个可以被看做一棵树的数组对象,其总是满足下列性质:
(a)堆中某个节点的值总是不大于或不小于其父节点的值
(b)堆总是一棵完全二叉树
节点值不大于其父节点的堆称为大根堆,反之称为小根堆。
堆排序与topK问题无关,以下内容纯属展开:
以堆为基础的排序方法称为堆排序。堆排序主要可以分为以下几个步骤:
(a)建堆
(b)将堆顶与堆的最后一个元素对换
(c)将堆的大小-1(此时最后一个元素看作已排序好),并对剩下的部分保持堆的性质
(d)重复(b)、(c)直至排序完成
代码如下:
- public class MinHeap {
- public final static int MAX = 100;
- private int[] heap = null;
- private int size = 0;
- public MinHeap(){
- heap = new int[MAX];
- }
- public boolean add(int num){
- if (size >= MAX){
- return false;
- }
- heap[size] = num;
- size++;
- return true;
- }
- public void buildHeap(){
- for (int i = (size - 1) / 2;i >= 0;i--){
- heapify(i);
- }
- }
- private void heapify(int pos){
- int left = pos * 2 + 1, right = pos * 2 + 2;
- if (left >= size){
- return;
- }else if (right >= size){
- if (heap[pos] > heap[left]){
- int tmp = heap[pos];
- heap[pos] = heap[left];
- heap[left] = tmp;
- }
- return;
- }else{
- int minPos = pos;
- if (heap[pos] > heap[left]){
- if (heap[left] > heap[right]){
- minPos = right;
- }else{
- minPos = left;
- }
- }else if (heap[pos] > heap[right]){
- minPos = right;
- }
- int tmp = heap[pos];
- heap[pos] = heap[minPos];
- heap[minPos] = tmp;
- if (minPos != pos){
- heapify(minPos);
- }
- }
- }
- public void heapSort(){
- buildHeap();
- int length = size;
- while (size > 0){
- int tmp = heap[size-1];
- heap[size-1] = heap[0];
- heap[0] = tmp;
- size--;
- heapify(0);
- }
- size = length;
- }
- public void print(){
- for (int i = 0;i < size;i++){
- System.out.print(heap[i] + " ");
- }
- System.out.println();
- }
- public static void main(String[] args) {
- MinHeap heap = new MinHeap();
- for (int i = 10;i > 0;i--){
- heap.add(i);
- }
- heap.heapSort();
- heap.print();
- }
- }
堆排序heapSort()的时间复杂度为O(nlgn),其中建堆buildHeap()的时间复杂度为O(n)(不是笔误,具体见算法导论),每一轮保持堆的性质heapify(int pos)的时间复杂度为O(lgn),一共O(n)轮。
回到topK问题上,我们可以维护一个大小为K的堆来帮助我们解决topK问题。以最小的K个数据为例,我们维护一个大小为K的大根堆在内存中,接下来每次从那一大堆数据当中读出一个数据与堆顶比较,若该数据比堆顶数据小,则说明它比当前topK的最大值要小,因此将堆顶替换为该数据,再用heapify(0)保持该堆的最大堆性质即可。
---------------------
作者:xmkid
来源:CSDN
原文:https://blog.csdn.net/xmkid/article/details/51125196
版权声明:本文为博主原创文章,转载请附上博文链接!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。