力扣每日一题(2023年2月)

第一天 (2325. 解密消息)

问题
在这里插入图片描述
代码

class Solution {
    public String decodeMessage(String key, String message) {
        /** 
            根据key串出现的字符顺序 对应 字母表 生成对应的哈希表
            将明文根据哈希表解密
         */

         char cur = 'a';
         Map<Character, Character> rules = new HashMap<Character, Character>();  // 用于保存根据key生成的对应关系

         for(int i=0; i<key.length(); i++) {
             // 生成哈希表
             char c = key.charAt(i);  // 拿到对应的字符
             if(c!=' ' && !rules.containsKey(c)) {
                 // 当前字符未对应字母表的字符时
                 rules.put(c, cur);
                 cur++;  // 字母表指针后移
             }
         }

         // 根据生成的哈希表解密message
         StringBuilder jm = new StringBuilder();
         for(int i=0; i<message.length(); i++) {
             char c = message.charAt(i);
             if(c!=' ') {
                 // c不等于空格时,获取解密后的字符
                 c = rules.get(c);
             }
             jm.append(c);
         }* 
         return jm.toString();
    }
}

第十六天(2341. 数组能形成多少数对)

问题
在这里插入图片描述
代码

class Solution {
    public int[] numberOfPairs(int[] nums) {
        /** 可利用哈希表存储遍历到的每个数作为键位, 当某键位值为2时 */
        Map<Integer, Integer> cnt = new HashMap<Integer, Integer>();   // 用于记录每个数字出现的次数
        int count = 0;  // 用于记录出现成对的数字次数

        for(int num:nums) {
            cnt.put(num, cnt.getOrDefault(num,0) + 1);   // 当该键位已有时获取(没有时获取0) 再加1
            if(cnt.get(num) % 2 == 0) {
                // 成对出现一定为偶数
                count++;
            }
        }

        return new int[] {count, nums.length - count*2};
    }
}

第十七天 (1139. 最大的以 1 为边界的正方形)

问题
在这里插入图片描述
思想

/**
做题思想:
利用前缀和的方法来做,因为使用0/1来表示是否链接, 所以当(0,0)到(0,5)这个横线连通的话前缀和就是5了
利用两个二维度的数组, 分别用来计算 从左侧(i,0)到右侧(i,j)的前缀和,以及从上侧(0,j)到下侧(i,j)的前缀和
然后用一个二重循环分别测试以(i,j)点为左上点坐标时,
*/

代码

class Solution {
    public int largest1BorderedSquare(int[][] grid) {
        /** 
            做题思想: 
                利用前缀和的方法来做,因为使用0/1来表示是否链接, 所以当(0,0)到(0,5)这个横线连通的话前缀和就是5了
                利用两个二维度的数组, 分别用来计算  从左侧(i,0)到右侧(i,j)的前缀和,以及从上侧(0,j)到下侧(i,j)的前缀和
                然后用一个二重循环分别测试以(i,j)点为左上点坐标时,
         */

         int m=grid.length, n = grid[0].length;  // m表示行,n表示列
         int maxEdge = Math.max(m,n);   // 最大的长度边为mxn的较小一个
         int [][] rowSum = new int [m][n+1];  // 横向统计前缀和
         int [][] colSum = new int [m+1][n];  // 竖向统计前缀和

         for(int i=0; i<m; i++) {
             for(int j=0; j<n; j++) {
                 rowSum[i][j+1] = rowSum[i][j]+grid[i][j];  // 横向计算前缀和
                 colSum[i+1][j] = colSum[i][j]+grid[i][j];  // 竖向计算前缀和
             }
         }

         int ans = 0; // 用于记录最大的正方形边界
         // 遍历判断,以(i,j)为左上角点 找最大的正方形
         for(int i=0; i<m; i++) {
             for(int j=0; j<n; j++) {
                 for(int edge=ans+1; edge<=maxEdge; edge++) {
                     // ans为记录当前探测到的最大的正方形长度边, 又因为又用1表示连接
                     // 所以edge从ans+1开始表示用(i+edge-1,j)减去(i,j)位置前缀,可得出这两点间的实际距离,如果不等于edge,说明中间不连接
                     if(i+edge-1>=m || j+edge-1>=n) {
                         // 说明越界了
                         break;
                     }

                     int left = colSum[i+edge][j]-colSum[i][j];  // 左侧边长度
                     int top = rowSum[i][j+edge] - rowSum[i][j];  // 上侧边长度

                     if(left!=edge || top!=left) {
                         // 假如 左侧或者上侧 长度不等于edge, 说明不连续
                         break;
                     }

                     int right = colSum[i+edge][j+edge-1] - colSum[i][j+edge-1]; // 右侧边长度
                     int bottom = rowSum[i+edge-1][j+edge] - rowSum[i+edge-1][j]; // 底侧边长度

                     if(right==edge && bottom==edge) {
                         // 底侧与右侧边也要等于edge时,说明又找到更大的正方形了
                        ans=edge;
                     }
                 }
             }
         }

         return ans*ans;   // ans为边,以ans为边的正方形包括元素为ans的平方个

    }
}

第十八天 (1237. 找出给定方程的正整数解)

问题
在这里插入图片描述
代码

class Solution {
    public List<List<Integer>> findSolution(CustomFunction customfunction, int z) {
        /** 通过二分查找, 调用给定函数,添加符合题意得数对 */

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

        for(int x=1; x<1000; x++) {
            int yleft = 1, yright = 1000;
            while(yleft <= yright) {
                int ymid = (yleft + yright) / 2;
                if(customfunction.f(x,ymid) == z) {
                    // 找到符合规定得数
                    List<Integer> pair = new ArrayList<Integer>();
                    pair.add(x);
                    pair.add(ymid);
                    res.add(pair);
                }

                if(customfunction.f(x,ymid)>z) {
                    // 说明ymid大了,调整高位
                    yright = ymid-1;
                }else {
                    // 说明ymid低了,调整低位
                    yleft = ymid + 1;
                }    
            }
        }
        return res;
    }

}

第十九天 (1792. 最大平均通过率)

问题
在这里插入图片描述
思想·
在这里插入图片描述
代码

class Solution {
    public double maxAverageRatio(int[][] classes, int extraStudents) {
        PriorityQueue<int[]> pq = new PriorityQueue<int[]>((a,b)->{  //PriorityQueue每次会取出优先级最小的
            long val1 = (long)(b[1]+1)*b[1]*(a[1]-a[0]);
            long val2 = (long)(a[1]+1)*a[1]*(b[1]-b[0]);
            if(val1 == val2) {
                return 0;
            }
            return val1<val2?1:-1;
        });


        for(int[] c:classes) {
            pq.offer(new int[] {c[0], c[1]});
        }

        for(int i=0; i<extraStudents; i++) {// 将给定必过的学生分配给权值最小的班级
            int[] arr = pq.poll();
            int pass = arr[0], total = arr[1];
            pq.offer(new int[] {pass+1, total+1});
        }

        double res = 0;

        // 计算最大平均通过率
        for(int i=0; i<classes.length; i++) {
            int[] arr = pq.poll();
            int pass = arr[0], total = arr[1];
            res += 1.0 * pass / total;
        }

        return res/classes.length;
    }
}

第二十天(2347. 最好的扑克手牌)

问题
在这里插入图片描述
代码

class Solution {
    public String bestHand(int[] ranks, char[] suits) {
        /**
            根据 题目规则编写函数即可
         */
        Set<Character> suitsSet = new HashSet<Character>();

        for(char suit : suits) {
            // 遍历一遍花色,加入到集合中
            suitsSet.add(suit);
        }

        // 因为集合相同得元素只能保存一次
        if(suitsSet.size() == 1) {
            return "Flush";  // 当花色相同时,size为1
        }

        // 用于保存出现数字的个数
        Map<Integer,Integer> ranksMap = new HashMap<>();
        for(int rank : ranks) {
            ranksMap.put(rank, ranksMap.getOrDefault(rank, 0) + 1);
        }


        // 假如有五个数字都不一样
        if(ranksMap.size()==5) {
            return "High Card";
        }

        // 判断是否有三张花色以上相同的
        for(Map.Entry<Integer,Integer> entry : ranksMap.entrySet()) {
            if(entry.getValue()>2) {
                return "Three of a Kind";
            }
        }

        // 不满足上面的情况,只能是两张数字一样了
        return "Pair";
    }
}

第二十一天 (1326. 灌溉花园的最少水龙头数目)

问题
在这里插入图片描述
思想

/**
思想:
可将本题看成将没个i处可覆盖的区域为一个小区间, 求最小覆盖的区间数
*/

代码

class Solution {
    public int minTaps(int n, int[] ranges) {
        /**
            思想:
                可将本题看成将没个i处可覆盖的区域为一个小区间, 求最小覆盖的区间数
         */

        int [][] intervals = new int[n+1][];   // 创建保存每个i可覆盖的区间
        for(int i=0; i<=n; i++) {// 将这些小区间保存为一些数组

            // 确保这些区间不会超过(0-n)这个数值范围
            int start= Math.max(0, i-ranges[i]);  // 区间开头
            int end = Math.min(n,i+ranges[i]);    // 区间结尾

            intervals[i] = new int[] {start, end};
        }

        // 将每个小区间的开始端按升序排序
        Arrays.sort(intervals, (a,b) -> a[0]-b[0]);
        int[] dp = new int[n+1];  // 创建动态规划dp,记录到第i个区间覆盖[0-i]的最小区间数
        Arrays.fill(dp, Integer.MAX_VALUE);

        dp[0] = 0;

        for(int [] interval : intervals) {
            int start = interval[0], end = interval[1];
            if(dp[start] == Integer.MAX_VALUE) {
                return -1;   // 当dp[start] 等于 int上限时,说明当前区间覆盖不完整个区域
            }

            for(int j=start; j<=end; j++) {
                dp[j] = Math.min(dp[j], dp[start]+1);
            }
        }
        return dp[n];

    }
}

第二十四天(2357. 使数组中所有元素都等于零)

问题
在这里插入图片描述
代码

class Solution {
    public int minimumOperations(int[] nums) {
        /** 思想:
            读懂题目后,会发现,每次都会减去最小的操作数
            因此操作数不同的个数,就是要减去最少的非零元素
            (注意不要统计0)
         */


        int ans = 0;

        // 用于保存值
        HashSet<Integer> hashSet = new HashSet<>();

        for(int num : nums) {
            if(!hashSet.contains(num) && num != 0) {
                // 只有当前遍历到的值不在set中,并且不为零,才统计
                ans++;
            }
            hashSet.add(num);

        }

        return ans;
    }
}

第二十五天 (1247. 交换字符使得字符串相同)

问题
在这里插入图片描述

思想

/**
思想:
出现交换的情况无非 s1为x,s2为y (称之为xy)或者s1为y,s2为x(称之为yx)
我们记录这些不对称的情况, 可以通过交换去消除
消除时如图所示,可通过一次交换,消除 两次xy情况 “或者” 消除两次yx情况
也可以通过两次交换消除 一次xy情况与一次yx情况
因此。xy情况加yx情况一定是偶数次才有可能通过交换换成两个相同字符串
*/

在这里插入图片描述
代码

class Solution {
    public int minimumSwap(String s1, String s2) {
        /**
            思想:
                出现交换的情况无非   s1为x,s2为y (称之为xy)或者s1为y,s2为x(称之为yx)
                我们记录这些不对称的情况, 可以通过交换去消除
                消除时如图所示,可通过一次交换,消除 两次xy情况  “或者” 消除两次yx情况
                                也可以通过两次交换消除   一次xy情况与一次yx情况
                                因此。xy情况加yx情况一定是偶数次才有可能通过交换换成两个相同字符串
         */


        int xy=0,yx=0;   // 记录对应位置情况的个数
        int n = s1.length();  // 字符串长度相等
        for(int i=0; i<n; i++) {
            char a = s1.charAt(i), b = s2.charAt(i);   // 获取两个字符串相同位置i的字符
            if(a=='x' && b=='y') {  // xy情况
                xy++;
            }
            if(a=='y' && b=='x') {  // yx情况
                yx++;
            }
        }

        if((xy+yx)%2==1) {  // 交换次数为奇数时,换不成一样的串
            return -1;
        }

        /** xy/2或者yx/ 2  是一次交换消除两次不对称的情况; xy%2或者yx%2 一定是最后两次交换消两种情况各一次的时候  */
        return xy/2 + yx/2 + xy%2 + yx%2;    

    }
}

第二十七天 (1144. 递减元素使数组呈锯齿状)

问题
在这里插入图片描述

思想

/**
思想:
变成锯齿状, 无非就是将偶数下标处或者奇数下标处变成小于两侧的值
注意,小于两侧时,只需要小于两侧较小的数即可。
换句话的意思就是 当前值分别减两侧值,得到最大的数+1即可,
当当前值已经小于两侧时, 减去两侧值时就是负数,初始记录res=0,则取大值就是0
*/

代码

class Solution {
    public int movesToMakeZigzag(int[] nums) {
        /** 
            思想:
                变成锯齿状, 无非就是将偶数下标处或者奇数下标处变成小于两侧的值
                注意,小于两侧时,只需要小于两侧较小的数即可。
                换句话的意思就是  当前值分别减两侧值,得到最大的数+1即可, 
                当当前值已经小于两侧时, 减去两侧值时就是负数,初始记录res=0,则取大值就是0
         */

        int res0 = 0, res1 = 0;

        for(int i=0; i<nums.length; i++) {
            int a = 0;  // 临时计算减去数
            if(i>0) {  // i>0就是满足与左侧比较的条件
                a = Math.max(a, nums[i] - nums[i-1] + 1);
            }

            if(i<nums.length-1) {  // 满足与右侧比较的条件
                a = Math.max(a, nums[i] - nums[i+1] + 1);
            }

            if(i%2==0) { // 偶数为低位
                res0+=a;
            }else { // 偶数为低位
                res1+=a;
            }

          
        }
        return res0>res1?res1:res0;  // 返回小的
    }
}

第二十八天 (2363. 合并相似的物品)

问题
在这里插入图片描述

思想

/**
思想: 遍历两个价值重量列表,将相同价值的作为键重量和为值保存到map中
再遍历map,以键为数组第一个元素,值为数组第二个元素
*/

代码

class Solution {
    public List<List<Integer>> mergeSimilarItems(int[][] items1, int[][] items2) {
        /** 
            思想: 遍历两个价值重量列表,将相同价值的作为键重量和为值保存到map中
                    再遍历map,以键为数组第一个元素,值为数组第二个元素
         */


        // 创建一个Map
        Map<Integer, Integer> hashMap = new HashMap<>();  // 用于记录价值-重量和

        for(int i=0; i<items1.length; i++) {  // 统计第一个二维数组的重量和
            hashMap.put(items1[i][0], hashMap.getOrDefault(items1[i][0], 0) + items1[i][1]);
        }

        for(int i=0; i<items2.length; i++) {   // 统计第二个二维数组的重量和
            hashMap.put(items2[i][0], hashMap.getOrDefault(items2[i][0], 0) + items2[i][1]);
        }


        // 创建保存返回值的二维列表
        List<List<Integer>> ret = new ArrayList<List<Integer>>();

        //遍历map,将[键,值]加入二维列表
        for(Map.Entry<Integer, Integer> entry : hashMap.entrySet()) {
            List<Integer> listT = new ArrayList<>();

            // 将键值加入小集合
            listT.add(entry.getKey());
            listT.add(entry.getValue());
            // 将小集合加入大集合
            ret.add(listT);
        } 

        // 排序!
        Collections.sort(ret, (a,b)->a.get(0)-b.get(0));
        return ret;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值