前 K 个高频元素-LeetCode

前 K 个高频元素

问题描述

给定一个非空的整数数组,返回其中出现频率前 k 高的元素。

示例 1:

输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]
示例 2:

输入: nums = [1], k = 1
输出: [1]

说明
你可以假设给定的 k 总是合理的,且 1 ≤ k ≤ 数组中不相同的元素的个数。
你的算法的时间复杂度必须优于 O(n log n) , n 是数组的大小。

解题思路

1、遍历一遍数组,将所有数字(key),和个数(value)统计装进HashMap
2、创建一个最小堆,当然你也可以直接使用PriorityQueue类
3、将HashMap中的数据根据元素的频率装进最小堆,堆的大小为k
4、这样频率前k高的元素就装进最小堆里
5、从堆里取出就可以了

代码实现

package solution;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

class Solution11 {
    public static void main(String[] args) {
        Solution11 solution11=new Solution11();
        List<Integer> list=solution11.topKFrequent(new int[]{1,1,1,2,2,3},2);
        System.out.println(list.toString());
    }

    /**
     * 最小堆
     * 基于Entry数组实现
     */
    private class Max{
        
        private class Entry{
            private int k;
            private int v;
            public Entry(int k,int v){
                this.k=k;
                this.v=v;
            }
        }
        //Entry数组
        private Entry[] entries;
        private int size;//元素的个数

        /**
         * 构造函数,创建一个大小为K的数组,保存k个元素
         * @param k
         */
        public Max(int k){
            
            entries=new Entry[k];
            size=0;
        }
        //父节点
        private int parent(int index){
            return (index-1)>>1;
        }
        //左子节点
        private int leftChild(int index){
            return (index<<1)+1;
        }
        //右子节点
        private int rightChild(int index){
            return (index<<1)+2;
        }
        //上浮动作,频率小的元素向上浮动
        private void shiftUp(int index){
            while (index>0){
                int parent=parent(index);
                if(entries[parent].v>entries[index].v){
                    swap(index,parent);
                    index=parent;
                }else {
                    break;
                }
            }
        }
        //获得最小元素,也就是最顶端元素
        public int getMin(){
            return entries[0].k;
        }
        //删除最小元素
        public int removeMin(){
            int k=getMin();
            //交换最顶端和最后一个元素的位置
            swap(0,size-1);
            //删除最后一个元素
            entries[size-1]=null;
            size--;
            //再做下沉操作
            shiftDown(0);
            return k;
        }
        //下沉操作,将频率大的元素向下沉
        private void shiftDown(int index){
            while (leftChild(index)<size){
                int k=leftChild(index);
                if(k+1<size&&entries[k+1].v<entries[k].v){
                    k=rightChild(index);
                }
                if(entries[index].v>entries[k].v){
                    swap(index,k);
                    index=k;
                }else {
                    break;
                }
            }
        }
        //交换
        private void swap(int a,int b){
            if(a==b){
                return;
            }
            Entry temp=entries[a];
            entries[a]=entries[b];
            entries[b]=temp;
        }
        //添加元素
        public void add(int k,int v){
            Entry entry=new Entry(k,v);
            //第一个元素直接添加
            if(size==0){
                entries[0]=entry;
                size++;
                return;
            }
            //堆里只添加k个元素
            if(size<entries.length){
                entries[size]=entry;
                shiftUp(size);
                size++;
            }else{
                //堆满了之后,添加元素与顶端元素进行比较,
                // 如果顶端元素的频率小,则替换
                if(entries[0].v<entry.v){
                    entries[0]=entry;
                    //再做下沉操作
                    shiftDown(0);
                }
            }

        }

    }
    public List<Integer> topKFrequent(int[] nums, int k) {
        Map<Integer,Integer> map=new HashMap<>();
        //将所有元素统计进map
        for(int i=0;i<nums.length;i++){
            if(map.get(nums[i])==null){
                map.put(nums[i],1);
            }else{
                map.put(nums[i],map.get(nums[i])+1);
            }
        }
        //创建堆
        Max max=new Max(k);
        //将频率前k高的元素添加进堆
        for (Map.Entry<Integer,Integer> entry:map.entrySet()){
            max.add(entry.getKey(),entry.getValue());
        }
        List<Integer> list=new ArrayList<>();
        int[] va=new int[k];
        //应该建个最大堆,最小堆取出来是反序
        for (int i=k-1;i>=0;i--){
            va[i]=max.removeMin();
        }
        for (int n:va){
            list.add(n);
        }
        return list;

    }
}

发布了149 篇原创文章 · 获赞 137 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/zhang_ye_ye/article/details/98743701