LeetCode刷题笔记(Java)---第501-520题

文章目录

      • 前言
      • 笔记导航
      • 502. IPO
      • 503. 下一个更大元素 II
      • 504. 七进制数
      • 506. 相对名次
      • 507. 完美数
      • 508. 出现次数最多的子树元素和
      • 509. 斐波那契数
      • 514. 自由之路
      • 515. 在每个树行中找最大值
      • 516. 最长回文子序列
      • 517. 超级洗衣机
      • 518. 零钱兑换 II
      • 519. 随机翻转矩阵
      • 520. 检测大写字母

前言

需要开通vip的题目暂时跳过

笔记导航

点击链接可跳转到所有刷题笔记的导航链接

文章目录

      • 前言
      • 笔记导航
      • 502. IPO
      • 503. 下一个更大元素 II
      • 504. 七进制数
      • 506. 相对名次
      • 507. 完美数
      • 508. 出现次数最多的子树元素和
      • 509. 斐波那契数
      • 514. 自由之路
      • 515. 在每个树行中找最大值
      • 516. 最长回文子序列
      • 517. 超级洗衣机
      • 518. 零钱兑换 II
      • 519. 随机翻转矩阵
      • 520. 检测大写字母

502. IPO

假设 力扣(LeetCode)即将开始其 IPO。为了以更高的价格将股票卖给风险投资公司,力扣 希望在 IPO 之前开展一些项目以增加其资本。 由于资源有限,它只能在 IPO 之前完成最多 k 个不同的项目。帮助 力扣 设计完成最多 k 个不同项目后得到最大总资本的方式。

给定若干个项目。对于每个项目 i,它都有一个纯利润 Pi,并且需要最小的资本 Ci 来启动相应的项目。最初,你有 W 资本。当你完成一个项目时,你将获得纯利润,且利润将被添加到你的总资本中。

总而言之,从给定项目中选择最多 k 个不同项目的列表,以最大化最终资本,并输出最终可获得的最多资本。

LeetCode刷题笔记(Java)---第501-520题_第1张图片

  • 解答

    public int findMaximizedCapital(int k, int W, int[] Profits, int[] Capital) {
            PriorityQueue<PC> queue = new PriorityQueue<>(new Comparator<PC>(){
                public int compare(PC p1,PC p2){
                    return p2.profit - p1.profit;
                }
            });
            for(int i = 0;i < Profits.length;i++){
                queue.add(new PC(Profits[i],Capital[i]));
            }
            while(k > 0 && !queue.isEmpty()){
                List<PC> list = new ArrayList<>();
                boolean flag = false;
                while(!queue.isEmpty()){
                    PC pc = queue.poll();
                    if(pc.capital <= W){
                        W += pc.profit;
                        k--;
                        flag = true;
                        break;
                    }
                    list.add(pc);
                }
                if(flag)
                    queue.addAll(list);
            }
            return W;
        }
    
        public class PC{
            int profit;
            int capital;
            public PC(int profit,int capital){
                this.profit = profit;
                this.capital = capital;
            }
        }
    
  • 分析

    1. 优先级队列,每次从队中取出的项目是profit最大的元素。
    2. 然后判断当前项目的启动资金是否小于等于W,若满足则W + profit
    3. 将没有用到的项目放回优先级队列,重复以上操作k次
  • 提交结果LeetCode刷题笔记(Java)---第501-520题_第2张图片

503. 下一个更大元素 II

给定一个循环数组(最后一个元素的下一个元素是数组的第一个元素),输出每个元素的下一个更大元素。数字 x 的下一个更大的元素是按数组遍历顺序,这个数字之后的第一个比它更大的数,这意味着你应该循环地搜索它的下一个更大的数。如果不存在,则输出 -1。

LeetCode刷题笔记(Java)---第501-520题_第3张图片

  • 解答

    //方法1
    public int[] nextGreaterElements(int[] nums) {
            int len = nums.length;
            int[] res = new int[len];
            int max = Integer.MIN_VALUE;
            for(int i = 0;i < len;i++){
                max = Math.max(max,nums[i]);
            }
            for(int i = 0;i < len;i++){
                if(nums[i] == max){
                    res[i] = -1;
                    continue;
                }
                for(int j = i + 1;j < i + len;j++){
                    int temp = j;
                    if(j >= len){
                        temp -= len;
                    }
                    if(nums[temp] > nums[i]){
                        res[i] = nums[temp];
                        break;
                    }
                }
            }
            return res;
        }
    //方法2
    public int[] nextGreaterElements(int[] nums) {
            int[] res = new int[nums.length];
            Stack<Integer> stack = new Stack<>();
            for (int i = 2 * nums.length - 1; i >= 0; --i) {
                while (!stack.empty() && nums[stack.peek()] <= nums[i % nums.length]) {
                    stack.pop();
                }
                res[i % nums.length] = stack.empty() ? -1 : nums[stack.peek()];
                stack.push(i % nums.length);
            }
            return res;
        }
    
  • 分析

    1. 先找到最大值,最大值的下一个更大元素的结果必定是-1
    2. 然后根据题意两层循环来寻找下一个更大的值即可。
    3. 方法2使用单调栈
    4. 从后往前遍历,当栈顶元素大于当前元素的时候,则说明找到了下一个更大的值,当前元素入栈
    5. 当栈顶元素小于当前元素的时候,栈顶元素依次出栈,若栈空了,则当前元素的下一个更大的值记为-1,若不为空,则当前元素的下一个更大的值就是栈顶元素。当前元素入栈
    6. 因为是循环数组,所以再从后往前遍历一次,根据上面的规则就可以找到所有元素的下一个更大的值。
  • 提交结果

    方法1LeetCode刷题笔记(Java)---第501-520题_第4张图片

    方法2LeetCode刷题笔记(Java)---第501-520题_第5张图片

504. 七进制数

给定一个整数,将其转化为7进制,并以字符串形式输出。

LeetCode刷题笔记(Java)---第501-520题_第6张图片

  • 解答

    public String convertToBase7(int num) {
            if(num == 0) return "0";
            StringBuilder sb = new StringBuilder();
            boolean pos = num >= 0;
            num = Math.abs(num);
            while(num != 0) {
                sb.append(num % 7);
                num = num / 7;
            }
    
            if(!pos) {
                sb.append('-');
            }
            return sb.reverse().toString();
        }
    
  • 分析

    1. 参考Java当中的Integer.toString(int num,int radio)的源码
    2. 每次将num 余 7的结果添加到答案中,然后num /= 7
    3. 直到num = 0 即可得到结果
  • 提交结果LeetCode刷题笔记(Java)---第501-520题_第7张图片

506. 相对名次

给出 N 名运动员的成绩,找出他们的相对名次并授予前三名对应的奖牌。前三名运动员将会被分别授予 “金牌”,“银牌” 和“ 铜牌”(“Gold Medal”, “Silver Medal”, “Bronze Medal”)。

(注:分数越高的选手,排名越靠前。)

LeetCode刷题笔记(Java)---第501-520题_第8张图片

  • 解答

    public String[] findRelativeRanks(int[] nums) {
            String[] res = new String[nums.length];
            String[] medals = new String[]{"Gold Medal", "Silver Medal", "Bronze Medal"};
            int index = 1;
            PriorityQueue<A> queue = new PriorityQueue<>(new Comparator<A>(){
                public int compare(A a1,A a2){
                    return a2.score - a1.score;
                }
            });
            for(int i = 0;i < nums.length;i++){
                queue.add(new A(nums[i],i));
            }
            while(!queue.isEmpty()){
                A cur = queue.poll();
                if(index <= 3){
                    res[cur.index] = medals[index-1];
                }else{
                    res[cur.index] = ""+index;
                }
                index++;
            }
            return res;
        }
    
        class A{
            int score;
            int index;
            public A(int score,int index){
                this.score = score;
                this.index = index;
            }
        }
    
  • 分析

    1. 利用优先级队列,记录下选手的得分以及对应的索引位置。
    2. 根据得分从高到低排序。
    3. 然后依次出队,赋予名次
  • 提交结果LeetCode刷题笔记(Java)---第501-520题_第9张图片

507. 完美数

对于一个 正整数,如果它和除了它自身以外的所有 正因子 之和相等,我们称它为 「完美数」。

给定一个 整数 n, 如果是完美数,返回 true,否则返回 false

LeetCode刷题笔记(Java)---第501-520题_第10张图片

  • 解答

    //方法1
    public boolean checkPerfectNumber(int num) {
            if (num <= 0) {
                return false;
            }
            int sum = 0;
            for (int i = 1; i * i <= num; i++) {
                if (num % i == 0) {
                    sum += i;
                    if (i * i != num) {
                        sum += num / i;
                    }
    
                }
            }
            return sum - num == num;
        }
    //方法2
    public int pn(int p) {
            return (1 << (p - 1)) * ((1 << p) - 1);
        }
        public boolean checkPerfectNumber(int num) {
            int[] primes=new int[]{2,3,5,7,13,17,19,31};
            for (int prime: primes) {
                if (pn(prime) == num)
                    return true;
            }
            return false;
        }
    
  • 分析

    1. 遍历 i = 1到sqrt(num),因为因数都是成对出现的,所以只需要考虑 开方前的数字即可。

    2. 若可以整除 i 那么就加上i和 num/i 两个数

    3. 若i = num/i 那么只加一个即可。

    4. 方法2

    5. 所有的偶完全平方数可以写成LeetCode刷题笔记(Java)---第501-520题_第11张图片

      其中p是素数

  • 提交结果

    方法1LeetCode刷题笔记(Java)---第501-520题_第12张图片

    方法2LeetCode刷题笔记(Java)---第501-520题_第13张图片

508. 出现次数最多的子树元素和

LeetCode刷题笔记(Java)---第501-520题_第14张图片

  • 解答

    Map<Integer,Integer> map = new HashMap<>();
        public int[] findFrequentTreeSum(TreeNode root) {
            if(root == null)return new int[0];
            dfs(root);
            dfs2(root);
            Map<Integer, List<Integer>> map2 = new HashMap<>();
            for (int key : map.keySet()) {
                int number = map.get(key);
                List<Integer> list = map2.getOrDefault(number, new ArrayList<>());
                list.add(key);
                map2.put(number, list);
            }
            int targetKey = 0;
            for (int key : map2.keySet()) {
                if (key > targetKey) {
                    targetKey = key;
                }
            }
            List<Integer> list = map2.get(targetKey);
            int[] res = new int[list.size()];
            for (int i = 0; i < list.size(); i++) {
                res[i] = list.get(i);
            }
            return res;
        }
    
        public int dfs(TreeNode root){
            if(root.left !=null){
                root.val += dfs(root.left);
            }
            if(root.right != null){
                root.val += dfs(root.right);
            }
            return root.val;
        }
        public void dfs2(TreeNode root){
            map.put(root.val,map.getOrDefault(root.val,0) + 1);
            if(root.left !=null) dfs2(root.left);
            if(root.right !=null) dfs2(root.right);
        }
    
  • 分析

    1. dfs 更新结点的值为当前结点子树和。
    2. dfs2 记录下 每个和出现的次数。
    3. 遍历map,将出现相同次数的和放在一起,记录在map2当中。map2中的key是出现的次数,value是子树和的列表,表示这些子树和出现的次数都为key。
    4. 遍历map2 找到key最大的,也就是出现次数最多的子树和。
    5. 有了key 就可以得到对应的子树和列表。
    6. 转换为数组,输出
  • 提交结果LeetCode刷题笔记(Java)---第501-520题_第15张图片

509. 斐波那契数

LeetCode刷题笔记(Java)---第501-520题_第16张图片

  • 解答

    public int fib(int N) {
            if(N <= 1)return N;
            int first = 0;
            int second = 1;
            int res = 0;
            for(int i = N;i > 1;i--){
                res = first + second;
                first = second;
                second = res;
            }
            return res;
        }
    
  • 分析

    1. 每一位数字和前两位数字有关,所以需要记录前两位的数字。
    2. 然后循环 求当前数字并更新前两位数字,即可得到结果
  • 提交结果LeetCode刷题笔记(Java)---第501-520题_第17张图片

514. 自由之路

视频游戏“辐射4”中,任务“通向自由”要求玩家到达名为“Freedom Trail Ring”的金属表盘,并使用表盘拼写特定关键词才能开门。

给定一个字符串 ring,表示刻在外环上的编码;给定另一个字符串 key,表示需要拼写的关键词。您需要算出能够拼写关键词中所有字符的最少步数。

最初,ring 的第一个字符与12:00方向对齐。您需要顺时针或逆时针旋转 ring 以使 key 的一个字符在 12:00 方向对齐,然后按下中心按钮,以此逐个拼写完 key 中的所有字符。

旋转 ring 拼出 key 字符 key[i] 的阶段中:

  1. 您可以将 ring 顺时针或逆时针旋转一个位置,计为1步。旋转的最终目的是将字符串 ring 的一个字符与 12:00 方向对齐,并且这个字符必须等于字符 key[i] 。
  2. 如果字符 key[i] 已经对齐到12:00方向,您需要按下中心按钮进行拼写,这也将算作 1 步。按完之后,您可以开始拼写 key 的下一个字符(下一阶段), 直至完成所有拼写。

LeetCode刷题笔记(Java)---第501-520题_第18张图片

  • 解答

    public int findRotateSteps(String ring, String key) {
            int n = ring.length(), m = key.length();
            List<Integer>[] pos = new List[26];
            for (int i = 0; i < 26; ++i) {
                pos[i] = new ArrayList<Integer>();
            }
            for (int i = 0; i < n; ++i) {
                pos[ring.charAt(i) - 'a'].add(i);
            }
            int[][] dp = new int[m][n];
            for (int i = 0; i < m; ++i) {
                Arrays.fill(dp[i], 0x3f3f3f);
            }
            for (int i : pos[key.charAt(0) - 'a']) {
                dp[0][i] = Math.min(i, n - i) + 1;
            }
            for (int i = 1; i < m; ++i) {
                for (int j : pos[key.charAt(i) - 'a']) {
                    for (int k : pos[key.charAt(i - 1) - 'a']) {
                        dp[i][j] = Math.min(dp[i][j], dp[i - 1][k] + Math.min(Math.abs(j - k), n - Math.abs(j - k)) + 1);
                    }
                }
            }
            int[] last = dp[m - 1];
            int res = Integer.MAX_VALUE;
            for(int i = 0;i < last.length;i++){
                res = Math.min(last[i],res);
            }
            return res;
        }
    
  • 分析

    1. 记录下字符串key 中每一个字符,在ring中出现的位置。记为pos

    2. 动态规划 dp[i] [j]表示 key中的第i个字符,ring中第j个字符在12点钟方向 上相等的最小步数。

    3. 初始化dp[0] [j] 就是从pos[key.charAt(0) - ‘a’] 中找一个最小的值 + 1.即为key第一个字符 在 ring中第j个字符 匹配的最小步数。

    4. 从key的下一个字符开始遍历。

    5. 第二层循环 遍历当前字符在ring中出现的位置。

    6. 第三层循环 表示上一个字符匹配的位置。

    7. 所以动态转移方程可以写为

      dp[i] [j] = Math.min(dp[i] [j],dp[i - 1] [k] + Math.min(Math.abs(j-k),n - Math.abs(j - k)) + 1);

      表示上一个位置 到j的最小距离 再 加1步拼写步骤。

  • 提交结果
    LeetCode刷题笔记(Java)---第501-520题_第19张图片

515. 在每个树行中找最大值

您需要在二叉树的每一行中找到最大的值。

LeetCode刷题笔记(Java)---第501-520题_第20张图片

  • 解答

    public List<Integer> largestValues(TreeNode root) {
            List<Integer> res = new ArrayList<>();
            if(root == null)return res;
            LinkedList<TreeNode> queue = new LinkedList<>();
            queue.addLast(root);
            while(!queue.isEmpty()){
                LinkedList<TreeNode> temp = new LinkedList<>();
                int max = Integer.MIN_VALUE;
                while(!queue.isEmpty()){
                    TreeNode cur = queue.removeFirst();
                    max = Math.max(cur.val,max);
                    if(cur.left!=null)temp.addLast(cur.left);
                    if(cur.right!=null)temp.addLast(cur.right);
                }
                res.add(max);
                queue = temp;
            }
            return res;
        }
    
  • 分析

    1. 层次遍历,然后取每一行中的最大值,加入答案集合当中。
  • 提交结果LeetCode刷题笔记(Java)---第501-520题_第21张图片

516. 最长回文子序列

给定一个字符串 s ,找到其中最长的回文子序列,并返回该序列的长度。可以假设 s 的最大长度为 1000 。

LeetCode刷题笔记(Java)---第501-520题_第22张图片

  • 解答

    public int longestPalindromeSubseq(String s) {
            int n = s.length();
            int[][] dp = new int[n][n];
            for(int i = n - 1;i >=0 ;i--){
                dp[i][i] = 1;
                for(int j = i + 1;j < n;j++){
                    if(s.charAt(i) == s.charAt(j)){
                        dp[i][j] = dp[i+1][j-1] + 2;
                    }else{
                        dp[i][j] = Math.max(dp[i+1][j],dp[i][j-1]);
                    }
                }
            }
            return dp[0][n-1];
        }
    
  • 分析

    1. dp[i] [j]表示 i到j的最长回文子序列。

    2. i从后往前遍历,j从i+1往后遍历。这样就可以求得所有的子问题。

    3. 初始化dp[i] [i] = 1

    4. 动态转移方程,当i和j对应的字符相同的时候

      dp[i] [j] = dp[i+1] [j-1] + 2;

    5. 否则 取他们子问题的最大值

      即 dp[i] [j] = Math.max(dp[i+1] [j],dp[i] [j-1])

    6. 最后返回dp[0] [n-1]即为答案。

  • 提交结果LeetCode刷题笔记(Java)---第501-520题_第23张图片

517. 超级洗衣机

假设有 n 台超级洗衣机放在同一排上。开始的时候,每台洗衣机内可能有一定量的衣服,也可能是空的。

在每一步操作中,你可以选择任意 m (1 ≤ m ≤ n) 台洗衣机,与此同时将每台洗衣机的一件衣服送到相邻的一台洗衣机。

给定一个非负整数数组代表从左至右每台洗衣机中的衣物数量,请给出能让所有洗衣机中剩下的衣物的数量相等的最少的操作步数。如果不能使每台洗衣机中衣物的数量相等,则返回 -1。

LeetCode刷题笔记(Java)---第501-520题_第24张图片

  • 解答

    public int findMinMoves(int[] machines) {
            int res = Integer.MIN_VALUE;
            int sum = 0;
            for(int i = 0;i < machines.length;i++){
                sum += machines[i];
            }
            if(sum % machines.length != 0)return -1;
            int main = sum / machines.length;
            for(int i = 0;i < machines.length;i++){
                machines[i] -= main;
            }
            int preSum = 0;
            int max = Integer.MIN_VALUE;
            int preSumMax = Integer.MIN_VALUE;
            for(int i = 0;i < machines.length;i++){
                preSum += machines[i];
                preSumMax = Math.max(preSumMax,Math.abs(preSum));
                max = Math.max(max,machines[i]);
                res = Math.max(preSumMax,max);
            }
            return res;
        }
    
  • 分析

    1. 计算所有的衣服总和
    2. 若无法平均,则返回-1
    3. 计算平均值。
    4. 每一个洗衣机的值减去平均值,即为这个洗衣机需要增加或者减少的衣服的数量。
    5. 若一个洗衣机为正数x,那么必定需要执行x次操作。
    6. 若对于前i台洗衣机,他们对应的数的和若为y,那么需要和后面的N-i台洗衣机至少传递|y|次衣服,每次只能传递一件(若y为正数,则前者将衣服传递给后者,若y为负数,则后者将衣服传递给前者),操作步数至少为|y|次。
    7. 所以最少的操作次数是数组元素中的最大值和数组前缀和的绝对值的最大值中的较大者
    8. 遍历数组
    9. 每次计算前缀和的绝对值,然后得到最大的前缀和
    10. 并记录最大值。
    11. 最后比较最大值和最大前缀和绝对值。返回较大者即为答案。
  • 提交结果LeetCode刷题笔记(Java)---第501-520题_第25张图片

518. 零钱兑换 II

给定不同面额的硬币和一个总金额。写出函数来计算可以凑成总金额的硬币组合数。假设每一种面额的硬币有无限个。

LeetCode刷题笔记(Java)---第501-520题_第26张图片

  • 解答

    public int change(int amount, int[] coins) {
            int k = coins.length+1;
            int[][] dp = new int[k + 1][amount + 1];
            for(int i = 0;i <= k;i++){
                dp[i][0] = 1;
            }
            for(int i = 1;i <= amount;i++){
                for(int j = 1;j < k;j++){
                    if(i >= coins[j-1]){
                        dp[j][i] = dp[j-1][i] + dp[j][i - coins[j-1]];
                    }else{
                        dp[j][i] = dp[j-1][i];
                    }
                }
            }
            return dp[k-1][amount];
        }
    
  • 分析

    1. dp[j] [i]表示前j个硬币 得到i金额的组合数。

    2. 初始化dp[i] [0] = 1;前i个硬币得到金额0的组合数为1

    3. 动态转移方程

      当前金额大于当前遍历的硬币金额

      dp[j] [i] = dp[j-1] [i] + dp[j] [i-coins[j-1]];

      表示前j个硬币得到i金额的组合数量。= 前j-1个硬币得到i金额的组合数量 + 前j个硬币得到i-coins[j-1]的金额的组合数量。

  • 提交结果LeetCode刷题笔记(Java)---第501-520题_第27张图片

519. 随机翻转矩阵

题中给出一个 n_rows 行 n_cols 列的二维矩阵,且所有值被初始化为 0。要求编写一个 flip 函数,均匀随机的将矩阵中的 0 变为 1,并返回该值的位置下标 [row_id,col_id];同样编写一个 reset 函数,将所有的值都重新置为 0。尽量最少调用随机函数 Math.random(),并且优化时间和空间复杂度。

LeetCode刷题笔记(Java)---第501-520题_第28张图片

  • 解答

    class Solution {
        Map<Integer, Integer> V = new HashMap<>();
        int nr, nc, rem;
        Random rand = new Random();
    
        public Solution(int n_rows, int n_cols) {
            nr = n_rows;
            nc = n_cols;
            rem = nr * nc;
        }
    
        public int[] flip() {
            int r = rand.nextInt(rem--);
            int x = V.getOrDefault(r, r);
            V.put(r, V.getOrDefault(rem, rem));
            return new int[]{x / nc, x % nc};
        }
    
        public void reset() {
            V.clear();
            rem = nr * nc;
        }
    }
    
  • 分析

    1. 将2维数组映射到一维数组上。
    2. 用map来记录已经filp的位置,value为总数量-已经转换的数量。
    3. 根据rem随机生成一个数字,
    4. 若当前map中有这个数字,那么说明这个数字对应的二维数组的位置被转换了,那么就替换成map中对应的映射value
    5. 如果没有,那么直接使用这个数字对应的二维数组中的位置,进行转换。
  • 提交结果LeetCode刷题笔记(Java)---第501-520题_第29张图片

520. 检测大写字母

给定一个单词,你需要判断单词的大写使用是否正确。

我们定义,在以下情况时,单词的大写用法是正确的:

  1. 全部字母都是大写,比如"USA"。
  2. 单词中所有字母都不是大写,比如"leetcode"。
  3. 如果单词不只含有一个字母,只有首字母大写, 比如 “Google”。

否则,我们定义这个单词没有正确使用大写字母。

LeetCode刷题笔记(Java)---第501-520题_第30张图片

  • 解答

    public boolean detectCapitalUse(String word) {
            boolean flagFirst = false;
            int number = 0;
            for(int i = 0;i < word.length();i++){
                char cur = word.charAt(i);
                if(cur >= 'A' && cur <= 'Z'){
                    if(i == 0) flagFirst = true;
                    number++;
                }
            }
            return (flagFirst && number == 1) || number == word.length() || number == 0;
        }
    
  • 分析

    1. 三种情况正确
    2. flagFirst用来记录首字母是否大写
    3. number用来记录大写字母的个数
    4. 当flagFirst == true 并且number == 1的时候 为true。表示首字母大写,其余小写。
    5. 当number == word.length()的时候为true。表示全部都大写
    6. 当number == 0的时候为true,表示全部都小写。
  • 提交结果LeetCode刷题笔记(Java)---第501-520题_第31张图片

你可能感兴趣的:(#,LeetCode刷题笔记,算法,leetcode,数据结构,java)