算法题小记

文章目录

一、TOPK问题

技巧,中等题就用优先队列,难题就用二分法

优先队列:
在这里插入图片描述

class Solution {
    public int[] topKFrequent(int[] nums, int k) {
               int[] result = new int[k];
        HashMap<Integer, Integer> map = new HashMap<>();
        for (int num : nums) {
            map.put(num, map.getOrDefault(num, 0) + 1);
        }

        Set<Map.Entry<Integer, Integer>> entries = map.entrySet();
        // 根据map的value值正序排,相当于一个小顶堆
        PriorityQueue<Map.Entry<Integer, Integer>> queue = new PriorityQueue<>((o1, o2) -> o1.getValue() - o2.getValue());
        for (Map.Entry<Integer, Integer> entry : entries) {
            queue.offer(entry);
            if (queue.size() > k) {
                queue.poll();
            }
        }
        for (int i = k - 1; i >= 0; i--) {
            result[i] = queue.poll().getKey();
        }
        return result;

    }
}

二分法:

在这里插入图片描述
上面面这题如果用优先队列的话,是可能会产生内存限制
在这里插入图片描述
因此提出了二分查找法:

二分查找算法步骤:

  • 初始化 left = 1, right = m*n, 进行二分搜索找到 k-th小数字。
  • 我们使用自定义的getCount函数来计算当前矩阵中小于等于mid值的数字数量。
  • 当二分搜索结束后,如果当前count < k,那么我们应该调整left值将其变大使得新的mid能逼近k, 及left = mid + 1
  • 反之count >= k,那么我们应该调整right值将其变小使得新的mid也能逼近k, 及right = mid

getCount()方法
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
以此类推

代码:

class Solution {

    // 寻找比小于等于mid的数字个数
    public int getCount(int mid, int m, int n){
        // 从左下角往右上角进行遍历
        int i=m,j=1;
        int count=0;
        while(i>=1&&j<=n){
            if(i*j<=mid){
                count+=i;  
                j++;  // 往右升一列
            }else{
                i--;  // 往上升一行
            }
        }

        return count;

    }


    public int findKthNumber(int m, int n, int k) {

        int left = 1, right = m*n;
        while(left<right){
            // 求中间值
            int mid = (left+right)/2;
            int count = getCount(mid,m,n);
            if(count>=k){
                // count>=mid时,mid有可能为答案,所以right=mid;比如{1,2,2,2,2}中小于等于2的数有5个,我们找第2小的数,(5>2)但是2为答案
                right=mid;
            }
            if(count<k){
                // count<k 时,mid一定不是答案,所以
                left = mid+1;

            }

        }
        return left;
        
        

    }
}

关于最后的mid一定会在数组中的问题:

我们先看getCount()函数
getCount()函数的目的是统计矩阵里小等于mid的元素数目count. 再判断count和k的关系.因为mid = (left + right) / 2这种划分方法是把矩阵划分成了[left , mid] 与[mid + 1, right]两部分. 当 count < k 时, 说明mid太小了, 我们应该在[mid + 1, right] 这个范围里查找. 否则在[left, mid]范围里查找.

如果存在一个不在矩阵中的数a满足条件, 因为a不在矩阵中,那count统计的元素肯定都是小于a的, 那一定存在一个比a小且在矩阵中的数b满足条件,即从小于a的数变成了小于等于b的数 .等用题目中的例子,x = 13 和x = 14 都满足小于等于x的元素数目等于8, 对14来说统计的都是小于它的数, 而对13来说统计的都是小于等于它的数. 问题来了, 那为何取到的不是14而是13呢?

因为我们取mid的取法是 mid = (left + right) / 2, 当left < right时, mid 永远 取不到right, 想要mid取到right ,只有left == right. 但循环条件是 while(left < right), 当 left == right时循环已经终止. 所以我们得到会是一个左边界. 还是用题目中的例子, 假设left = 13, right = 14mid = (13 + 14) / 2 = 13

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值