自己实现可以动态调整的堆结构

题目一

给你一些词汇及其词频,如何实现求出词频最高的前k个?
思路
这题不难,你可以很容易使用堆结构来实现,比如,如果用大根堆的话,最后输出大根堆的前k个堆顶就行了。同时,如果用小根堆,也可以实现求词频最多的前k个,如何实现?可以创建一个大小为k的小根堆,小根堆的堆顶其实就相当于一个门槛,维护前k个中最小的值,当又来一个词汇时,如果他的词频大于门槛,则他干掉门槛进入小根堆,然后小根堆重新调整,如果新来的连门槛都没干掉,那么他不可能是前k个。

题目二

借鉴题目一的思路,我们可以使用小根堆来实现统计前k个最大值的操作。现在,题目要求改变了,题目一中,给你的是已经确定的词频,现在,如果词频不确定呢?或者说,词频是在动态改变的话,如何实现统计最大的前k个呢?显然,此时,再使用题目一的方法就不合适了,因为,每次词频改变,都需要更新堆中的元素及其位置,而这个过程,系统给我们提供的堆结构是没有这个功能的,系统只会重新创建一个,而不会动态修改,因此,每次更新就重新创建,这复杂度就太高了。
因此,此时,就需要我们自己实现一种能够动态调整的堆结构,来完成此题。
数据结构分析:
首先,大思路仍然不变,我们还是使用小根堆来实现统计k个最大值,因此,我们需要一个长度为k的数组,来表示小根堆。
其次,因为词频会动态改变,因此,我们需要维护一个词频表,来保存当前词频以及词频的更新操作。
最后,来分析一下,为什么系统的堆无法实现这个功能呢?最主要的问题就是,当我们修改一个词频的时候,我们是无法在系统已经构建好的堆上面,寻找到该词的位置,因此,只能遍历寻找,这个复杂度就很高了,因此,如果我们自己实现的话,可以直接定位在堆上的位置,应该很舒服吧,因为,最后还需要一个能够获取堆上每个词的位置的数据结构,用map来保存。
思路:
首先,每来一个词,就看看词频表中是否存在该次,如果不存在,就创建该词,并把词频标做1,然后,观察小根堆是否已满,如果未满,直接入堆,如果满了,就把该词的词频与小根堆堆顶比较,如果赢了,就入堆,然后调整堆。如果该词已经在小根堆中,则因为词频的更改调整小根堆。同时,对应小根堆位置的map中,保存该词及其在小根堆上的位置,如果不在小根堆上,就保存-1。
然后动态更新维护这三个数据结构。遍历小根堆就是输出当前最大的k个词频。

代码

public class MyHeap {

    //小根堆
    private String[] heap;
    //保存词频
    private HashMap<String,Integer> wordFrequence;
    //保存在小根堆中的位置
    private HashMap<String,Integer> heapSite;
    //用来判断小根堆是否已满
    private int index;

    public MyHeap(int k){
        heap=new String[k];
        wordFrequence=new HashMap<>();
        heapSite=new HashMap<>();
        index=0;
    }
    //打印显示词频最高的k个
    public void show(){
        for (int i = 0; i < heap.length; i++) {
            System.out.println(heap[i]);
        }
    }

    //动态添加一个词
    public void add(String str){
        //先更新词频
        if(!wordFrequence.containsKey(str)){
            wordFrequence.put(str,1);
            heapSite.put(str,-1);
        }else {
            //更新词频
            wordFrequence.put(str,wordFrequence.get(str)+1);
        }
        //判断是否已经出现过,以及是否在小根堆中,如果在,直接调整堆即可
        if(heapSite.containsKey(str)&&heapSite.get(str)!=-1){
            heapify(heapSite.get(str));
        }else {
            //堆未满,直接入堆后向上调整堆
            if(index<=heap.length-1){
                heap[index]=str;
                heapSite.put(str,index);
                heapInsert(index);
                index++;
            }else {
                //堆已满,判断是否词频比门槛高?
                if(wordFrequence.get(str)>wordFrequence.get(heap[0])){
                    //比门槛高,交换后向下调整堆
                    heapSite.put(heap[0],-1);
                    heapSite.put(str,0);
                    heap[0]=str;
                    heapify(0);
                }else {
                    //比不过门槛
                    heapSite.put(str,-1);
                }
            }
        }
    }

    //堆不满时,插入堆后,向上调整
    private void heapInsert(int index) {
        while (wordFrequence.get(heap[index])<wordFrequence.get(heap[(index-1)/2])){
            swap(index,(index-1)/2);
            index=(index-1)/2;
        }
    }

    //交换堆中两个位置,同时更新heapSite
    private void swap(int index, int i) {
        String temp=heap[index];
        heap[index]=heap[i];
        heap[i]=temp;
        heapSite.put(heap[i],i);
        heapSite.put(heap[index],index);
    }

    //干掉门槛后,向下调整堆
    private void heapify(int index) {
        while (index<heap.length){
            int left=index*2+1;
            int right=left+1;
            int min;
            if(left>=heap.length){
                return ;
            }else if(right>=heap.length){
                min=left;
            }else {
                min=wordFrequence.get(heap[left]) < wordFrequence.get(heap[right]) ? left:right;
            }
            if(wordFrequence.get(heap[index])>wordFrequence.get(heap[min])){
                swap(index,min);
            }
            index=min;
        }
    }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值