优先队列常见算法题

数组中的第K个最大元素

数组中的第K个最大元素

在未排序的数组中找到第 k 个最大的元素,请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

示例1:

输入: [3,2,1,5,6,4] 和 k = 2
输出: 5

示例2:

输入: [3,2,3,1,2,4,5,5,6] 和 k = 4
输出: 4

使用优先队列和栈解决

  • 建堆
  • 前k个元素出队
  • 栈保存出队元素
  • 栈顶即为第k大元素
    /**
     * 基于优先队列实现
     * @param nums
     * @param k
     * @return
     */
    public int findKthLargest(int[] nums, int k) {

        //自定义排序规则
        Comparator<Integer> comparator = new Comparator<Integer>(){
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2 - o1;
            }
        };
        //使用优先队列建堆
        Queue<Integer> queue = new PriorityQueue<>(comparator);
        for(int i : nums){
            queue.offer(i);
        }
        //获取前k个最大元素
        Stack<Integer> stack = new Stack<>();
        for(int i = 1;i <= k;i++){
            stack.push(queue.poll());
        }
        //栈顶即第k大元素
        return stack.pop();
    }

也可以基于数组实现

  • 排序
  • 数组指定定位获取
    /**
     * 基于数组实现
     * @param nums
     * @param k
     * @return
     */
    public int findKthLargest1(int[] nums, int k) {
        //排序
        Arrays.sort(nums);
        //因为排序是从小到大的,所以需要获取倒数的,即 len - k位置
        return nums[nums.length - k];
    }

设计一个找到数据流中第K大元素的类

设计一个找到数据流中第K大元素的类(class)。注意是排序后的第K大元素,不是第K个不同的元素。你的 KthLargest 类需要一个同时接收整数 k 和整数数组nums 的构造器,它包含数据流中的初始元素。每次调用 KthLargest.add,返回当前数据流中第K大的元素。

示例:

      int k = 3;
      int[] arr = [4,5,8,2];
      KthLargest kthLargest = new KthLargest(3, arr);
      kthLargest.add(3);   // returns 4
      kthLargest.add(5);   // returns 5
      kthLargest.add(10);  // returns 5
      kthLargest.add(9);   // returns 8
      kthLargest.add(4);   // returns 8

这个与前面不同的是,要可以这不断有元素入队和出队的过程中,能正确返回正确的结果

  • 维护一个长度为k的堆,此堆就是维护了前k大的元素集合

  • add()是构造堆,关于具体元素的上滤和下滤的位置调整交由PriorityQueue

  • 入堆:

    • 若堆元素 < k,直接入队

    • 若堆元素 > k

      • 如果堆顶元素 < 插入元素x,堆顶元素先出队,后让x入队
    • 返回此时堆顶元素即为第k大元素

    /**
     * 【基于堆】
     * 使用优先队列最小堆,维护一个 k 大小的最小堆
     */
    int k;
    Queue<Integer> queue;

    public KthLargest(int k, int[] nums) {
        this.k = k;
        //构造堆
        queue = new PriorityQueue<>(k);
        for(int i : nums){
           add(i);
        }
    }

    /**
     * 入队并且获取第k大的数
     * @param val
     * @return
     */
    public int add(int val) {
        if(queue.size() < k){
            queue.offer(val);
        }else if(queue.peek() < val){
            queue.poll();
            queue.offer(val);
        }
        return queue.peek();
    }

前 K 个高频元素

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

示例 1:

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

示例 2:

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

这道题相比之前的两道题目多了频次的,我们可以用之前学过的哈希表去保存某数字和其出现的频次,然后频次的比较作为比较规则

    public List<Integer> topKFrequent(int[] nums, int k) {
        //使用散列表来保存数组每一个数的出现频率
        Map<Integer,Integer> map = new HashMap<>();
        for (int n: nums) {
            map.put(n, map.getOrDefault(n, 0) + 1);
        }
        //根据频次排序作为比较规则
        Queue<Integer> queue = new PriorityQueue<>((x,y)->map.get(x) - map.get(y));
        //所有键入队,当队长大于k时,原来入队的元素出队
        for(int i : map.keySet()){
            queue.offer(i);
            if(queue.size() > k){
                queue.poll();
            }
        }
        //保存结果
        ArrayList<Integer> list = new ArrayList<>(k);
        while(!queue.isEmpty()){
            list.add(queue.poll());
        }
        return list;
    }

根据字符出现频率排序

给定一个字符串,请将字符串里的字符按照出现的频率降序排列。

示例 1:

输入:
"tree"

输出:
"eert"

解释:
'e'出现两次,'r'和't'都只出现一次。
因此'e'必须出现在'r'和't'之前。此外,"eetr"也是一个有效的答案。

示例 2:

输入:
"cccaaa"

输出:
"cccaaa"

解释:
'c'和'a'都出现三次。此外,"aaaccc"也是有效的答案。
注意"cacaca"是不正确的,因为相同的字母必须放在一起。

示例 3:

输入:
"Aabb"

输出:
"bbAa"

解释:
此外,"bbaA"也是一个有效的答案,但"Aabb"是不正确的。
注意'A'和'a'被认为是两种不同的字符。

实现

    /**
     * 基于优先队列解决
     * @param s
     * @return
     */
    public String frequencySort(String s) {
        char[] chars = s.toCharArray();
        StringBuilder sb = new StringBuilder();
        Map<Character, Integer> map = new HashMap<>();
        //map统计频次
        for (char c : chars) {
            map.put(c, map.getOrDefault(c, 0) + 1);
            System.out.println(map.get(c));
        }
        PriorityQueue<Map.Entry<Character, Integer>> queue = new PriorityQueue<>((v1, v2) -> v2.getValue() - v1.getValue());//大顶堆
        //建堆
        queue.addAll(map.entrySet());
        int len = queue.size();
        for (int i = 0; i < len; i++) {
            char key = queue.peek().getKey();  //获取堆顶元素的键
            int value = queue.poll().getValue();//获取并弹出堆顶元素的值
            //组合字符
            while (value-- > 0) {
                sb.append(key);
            }
        }
        return sb.toString();
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值