LeetCode【347】 Top K Frequent Elements

Given a non-empty array of integers, return the k most frequent elements.

Example 1:

Input: nums = [1,1,1,2,2,3], k = 2
Output: [1,2]

Example 2:

Input: nums = [1], k = 1
Output: [1]

思路:

1、首先遍历数组,使用HashMap freq的存储每个数出现的频率——freq(num, 频率);

2、遍历map,对频率进行排序,这里使用优先队列priorityQueue的方式维护出现频率最多的k个数

      a.PriorityQueue pq的泛型为自定义的一个Pair类, pair(频率,num),因为要对频率进行比较,因此频率就作为第一个数

      b.优先队列用最小堆实现方式,只存储k个数,队首即为频率最小的pair

      c.遍历map,当pq的size小于k值时一直往里边添加pair,当=k时获得队首pair,比较pair的频率和此时entry的值,前者小则               poll,offer进该entry(也就是一直维护pq为频率最多的k个数)

3、将pq中的值放入结果list中。

代码:

class Solution {
    public List<Integer> topKFrequent(int[] nums, int k) {
        //1. traversing the nums to get freq of every nums
        Map<Integer, Integer> freq = new HashMap<>();
        for(int num : nums){
            freq.put(num, freq.getOrDefault(num, 0) + 1);
        }
        //2. sort the freq,使用最小堆来维护出现频率最多的k个数
        PriorityQueue<Pair> pq = new PriorityQueue<>(k, idComparator);
        for(Map.Entry<Integer, Integer> entry : freq.entrySet()){
            if(pq.size()==k){
                //取出pq中出现频率最少的元素,然后删除,再添加进此时的entry
                if(pq.peek().getFreq() < entry.getValue()){
                    pq.poll();
                    pq.offer(new Pair(entry.getValue(), entry.getKey()));
                }
               
            }else{
                pq.offer(new Pair(entry.getValue(), entry.getKey())); 
            }
        }
        System.out.println(pq.size());
        //3. 结果
        List<Integer> res = new ArrayList<>();
        while(!pq.isEmpty()){
            res.add(pq.peek().getNum());
            pq.poll();
        }
        return res;
    }
    
    //自定义匿名比较器(最小堆的方式)
    public static Comparator<Pair> idComparator = new Comparator<Pair>(){
        public int compare(Pair p1, Pair p2){
            return p1.getFreq() - p2.getFreq();
        }
    };
}

class Pair{
    private int freq;
    private int num;
    
    Pair(int freq, int num){
        this.freq = freq;
        this.num = num;
    }
    
    public void setFreq(int a){
        this.freq = a;
    }
    public int getFreq(){
        return freq;
    }
    
    public void setNum(int b){
        this.num = b;
    }
    public int getNum(){
        return num;
    }
}

时间复杂度:遍历数组O(n),优先队列O(nlogk),总:O(nlogk)

知识点:

1、使用HashMap存储某数据中元素出现的次数。

2、优先队列:维护前k大哥元素或前k小个元素。

3、遍历Map的方法:

    a.for-each循环中使用entry遍历


Map<Integer, Integer> map = new HashMap<Integer, Integer>();
 
for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
 
    entry.getKey();
    entry.getValue();
}
 

  b. 在for-each中循环keys或values

Map<Integer, Integer> map = new HashMap<Integer, Integer>();
 
for (int k: map.keySet()) {
 
    int value = map.get(k);
}
for (int v: map.values()) {
 
    ...
}

   c.通过iterator

HashMap<Integer, Integer> map = new HashMap<>();
Iterator<Map.Entry<Integer, Integer>> it = map.entrySet().iterator();
while(it.hasNext()){
    Entry<Integer, Integer> entry = it.next();
    entry.getKey();
    entry.getValue();
}

 其他:

看了discuss中快的部分答案,使用桶排序完成对频率的排序。

思路:

排序部分:按数据元素出现的频次进行排序,那么“桶”的数量范围是可以确定的——桶的数量小于等于给定数组元素的个数。编号为i的桶用于存放数组中出现频次为i的元素——即编号为i的桶存放map中值等于i的键。

   排序完成后,编号大的桶中元素出现的频次高,因此,我们“逆序”(先取桶编号大的桶的元素)获取桶中数据,直到获取数据的个数等于k。

discuss中代码:

public List<Integer> topKFrequent(int[] nums, int k) {

	List<Integer>[] bucket = new List[nums.length + 1];
	Map<Integer, Integer> frequencyMap = new HashMap<Integer, Integer>();

	for (int n : nums) {
		frequencyMap.put(n, frequencyMap.getOrDefault(n, 0) + 1);
	}

	for (int key : frequencyMap.keySet()) {
		int frequency = frequencyMap.get(key);
		if (bucket[frequency] == null) {
			bucket[frequency] = new ArrayList<>();
		}
		bucket[frequency].add(key);
	}

	List<Integer> res = new ArrayList<>();

	for (int pos = bucket.length - 1; pos >= 0 && res.size() < k; pos--) {
		if (bucket[pos] != null) {
			res.addAll(bucket[pos]);
		}
	}
	return res;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值