面试题:出现次数的Top K问题

题目:
给定String类型的数组strArr,再给定整数k,请严格按照排名顺序打印出现次数前k名的字符串。
举例:
strArr=[“1”,”2”,”3”,”4”], k=2
No.1:1,times:1
No.1:2,times:1
这种情况下,所有的字符串都出现一样多,随便打印任何两个字符串都可以。
strArr=[“1”,”1”,”2”,”3”], k=2
No.1:1,times:2
No.1:2,times:1
或则
No.1:1,times:2
No.1:3,times:1
要求:如果strArr长度为N,时间复杂度请达到O(Nlogk)

解答:
首先遍历strArr并统计字符串的词频,例如,strArr=[“a”,”b”,”b”,”a”,”c”],遍历后可以生成每种字符串及其相关词频的哈希表:
用哈希表的每条信息可以生成Node类的实例,Node类如下:

public class Node{
public String str;
public int times;

public Node(String s,int t){
    str=s;
    times=t;
    }
}

哈希表中有多少信息,就建立多少Node类的实例,并且依次放入堆中,具体过程为:
(1)建立一个大小为k的小根堆,这个堆放入的是Node类的实例。
(2)遍历哈希表的每条记录,假设一条记录为(s,t),s表示一种字符串,s的词频为t,则生成Node类的实例,记为(str,times)。
a、如果小根堆没有满,就直接将(str,times)加入堆,然后进行建堆调整(heapInsert调整),堆中Node类实例之间都以词频(times)来进行比较,词频越小,位置越往上。
b、如果小根堆已满,说明此时小根堆已经选出k个最高词频的字符串,那么整个小根堆的堆顶自然代表已经选出的k个最高词频的字符串中,词频最低的那个。堆顶的元素记为(headStr,minTimes)。如果minTimes小于times,说明字符串str有资格进入当前k个最高词频字符串的范围。而headStr应该被移出这个范围,所以把当前的堆顶(headStr,minTimes)替换成(str,times),然后从堆顶的位置进行堆的调整(heapify),如果minTimes>=times,说明字符串str没有资格进入当前k个最高词频字符串的范围,因为str的词频还不如目前选出的k个最高词频字符串中词频最少的那个,所以说明也不做。
c、遍历完strArr之后,小根堆里就是所有字符串中k个最高词频的字符串,但要求严格按排名打印,所以还需要根据词频从大到小完成k个元素间的排序。
遍历strArr建立哈希表的过程是O(N),哈希表中记录的条数最多为N条,每一条记录进堆时,堆的调整时间复杂度为O(logk),所以根据记录更新小根堆的过程为O(Nlogk)。k条记录排序的时间复杂度为O(klogk),所以总的时间复杂度为O(N)+O(Nlogk)+O(klogk),即O(Nlogk),具体代码如下:

package QuestionTest;

import java.util.HashMap;
import java.util.Map;

/**
 * Created by L_kanglin on 2017/4/23.
 * 出现次数的top K问题
 */
public class Test20 {
    public static  class Node{
        public String str;
        public int times;
        public Node(String s,int t){
            str=s;
            times=t;
        }
    }
    public static void main(String[] args){
        String[] strArr={"a","b","b","a","c"};
        int k=2;
        printTopKAndRank(strArr,k);
    }
    public static void printTopKAndRank(String[] arr,int topK){
        if(arr==null|| topK<1){
            return;
        }
        HashMap<String,Integer> map=new HashMap<String,Integer>();
        //生成哈希表(字符串词频)
        //注意词频表的处理
        for(int i=0;i!=arr.length;i++){
            String cur = arr[i];
            if(!map.containsKey(cur)){
                map.put(cur,1);
            }else{
                map.put(cur,map.get(cur)+1);
            }
        }
        Node[] heap =new Node[topK];
        int index=0;
        for(Map.Entry<String,Integer> entry:map.entrySet()){
            String str=entry.getKey();
            int times=entry.getValue();
            Node node=new Node(str,times);
            if(index!=topK){
                heap[index]=node;
                heapInsert(heap,index++);
            }else{
                if(heap[0].times<node.times){
                    heap[0]=node;
                    heapify(heap,0,topK);
                }
            }
        }
        //把小根堆的所有元素按词频从大到小排序
        for(int i=index-1;i!=0;i--){
            swap(heap,0,i);
            heapify(heap,0,i);
        }
        //严格按照排名打印k条记录
        for(int i=0;i!=heap.length;i++){
            if(heap[i]==null){
                break;
            }else{
                System.out.print("No."+(i+1)+": ");
                System.out.print(heap[i].str+",times: ");
                System.out.println(heap[i].times);
            }

        }
    }
    public static void heapInsert(Node[] heap,int index){
        while(index!=0){
            int parent =(index-1)/2;
            if(heap[index].times<heap[parent].times){
                swap(heap,parent,index);
                index=parent;
            }else{
                break;
            }
        }
    }
    public static void heapify(Node[] heap,int index,int heapSize){
        int left=index*2+1;
        int right=index*2+2;
        int smallest=index;
        while(left<heapSize){
            if(heap[left].times<heap[index].times){
                smallest=left;
            }
            if(right<heapSize && heap[right].times<heap[smallest].times){
                smallest=right;
            }else{
                break;
            }
            index=smallest;
            left=index*2+1;
            right=index*2+1;
        }
    }
    public static void swap(Node[] heap,int index1,int index2){
        Node tmp=heap[index1];
        heap[index1]=heap[index2];
        heap[index2]=tmp;

    }
}

运行结果如下:

No.1: b,times: 2
No.2: a,times: 2
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值