LeetCode 热题 HOT 100(P21~P30)

 系列文章: 

LeetCode 热题 HOT 100(P1~P10)-CSDN博客

LeetCode 热题 HOT 100(P11~P20)-CSDN博客

LeetCode 热题 HOT 100(P21~P30)-CSDN博客

LC48rotate_image

. - 力扣(LeetCode)

题目:

给定一个 n × n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。

你必须在 原地 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。

解法:

也是套路题,乍一看完全没思路,旋转到底要怎么弄?实际的解法是把旋转变成翻转的组合,比如这里顺时针旋转90度,相当于先上下翻转,再沿对角线翻转,针对翻转的代码就比较好写了。
 public void rotate(int[][] matrix) {
        final int row = matrix.length;
        final int col = matrix[0].length;
        //上下翻转
        for (int i = 0; i < row / 2; i++) {
            for (int j = 0; j < col; j++) {
                swap(matrix, i, j, row - i - 1, j);
            }
        }
        //对角线翻转
        for (int i = 0; i < row; i++) {
            for (int j = i + 1; j < col; j++) {
                swap(matrix, i, j, j, i);
            }
        }
    }

    private void swap(int[][] matrix, int rowIndex, int colIndex, int newRowIndex, int newColIndex) {
        int tmp = matrix[rowIndex][colIndex];
        matrix[rowIndex][colIndex] = matrix[newRowIndex][newColIndex];
        matrix[newRowIndex][newColIndex] = tmp;
    }

LC49group_anagrams

. - 力扣(LeetCode)

题目:

给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。

字母异位词 是由重新排列源单词的所有字母得到的一个新单词。

示例 1:

输入: strs = ["eat", "tea", "tan", "ate", "nat", "bat"]

输出: [["bat"],["nat","tan"],["ate","eat","tea"]]

解法:

理解题目的意思有点费劲,实际就是由相同字母组成单词的集合,很自然的想到用HashMap 。

 public List<List<String>> groupAnagrams(String[] strs) {
        Map<String, List<String>> cache = new HashMap<>();
        for (String str : strs) {
            final char[] chars = str.toCharArray();
            Arrays.sort(chars);
            cache.computeIfAbsent(new String(chars), (k) -> new ArrayList<String>()).add(str);
        }
        return new ArrayList<>(cache.values());
    }

写出代码不难,关键是能否写的尽量简练。

LC53maximum_subarray

. - 力扣(LeetCode)

题目:

给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

子数组是数组中的一个连续部分。

输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。

解法:

一般这种最*** 的题目理论上都可以用动态规划,核心难点是动态数组的定义,很多时候动态数组定义好了,动态推导方程也能比较简单的推演出来。

动态数组:dp[i] 表示包含下标i的连续子数组最大和

动态方程:dp[i] = Max(i,dp[i-1]+i) 

比较好理解的,这里稍微解释下包含i的数组最大和,基本要看前一位最大和是否小于0,如果是负数带上前面的肯定更小,还不如自己玩(i),如果前面大于0,那么带上肯定更大。

public int maxSubArray(int[] nums) {
        int[] dp = new int[nums.length];
        dp[0] = nums[0];
        int max = dp[0];
        for (int i = 1; i < nums.length; i++) {
            dp[i] = Math.max(nums[i], dp[i - 1] + nums[i]);
            max = Math.max(max, dp[i]);
        }
        return max;
    }

因为i只跟i-1 有关,因此动态数组可以简化为单个变量,其实动态规划很多代码都可以简化为单个或多个变量,但是这样的话代码就不太好理解。

LC55jump_game

. - 力扣(LeetCode)

题目:

给你一个非负整数数组 nums ,你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。

判断你是否能够到达最后一个下标,如果可以,返回 true ;否则,返回 false 。

输入:nums = [2,3,1,1,4]
输出:true
解释:可以先跳 1 步,从下标 0 到达下标 1, 然后再从下标 1 跳 3 步到达最后一个下标。

解法:

可能也算套路题,有思路之后很简单。其实就是每跳一步就记录当前能跳到的最远位置,然后一个个位置跳过去,判断当前记录最远位置能否到下一个。实际上也是动态规划的思路。

动态数组:dp[i] 表示在i位置时能跳到的最远下标

动态方程:dp[i] = max(dp[i-1],i+nums[i])

 public boolean canJump(int[] nums) {
        //表示能达到的最远距离
        int k = 0;
        for (int i = 0; i < nums.length; i++) {
            if (k < i) {
                return false;
            }
            k = Math.max(k, i + nums[i]);
        }
        //能抵达最后一块石头就ok
        return true;
    }

这里将动态数组精简为单个变量。

LC56merge_intervals

. - 力扣(LeetCode)

题目:

以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。

输入:intervals = [[1,3],[2,6],[8,10],[15,18]]
输出:[[1,6],[8,10],[15,18]]
解释:区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].

解法:

区间合并的问题,首先是排序,按照starti 排序。然后就是进行入队操作,在入队的时候跟队首元素进行比较,看下当前元素starti 是否大于队首元素的endi,如果大于说明需要开一个新的区间,如果小于那就加入当前区间。

public int[][] merge(int[][] intervals) {
        // 先按照区间起始位置排序
        Arrays.sort(intervals, (v1, v2) -> v1[0] - v2[0]);
        // 遍历区间
        List<int[]> result = new ArrayList<>();
        for (int[] interval : intervals) {
            // 如果结果数组是空的,或者当前区间的起始位置 > 结果数组中最后区间的终止位置,
            // 则不合并,直接将当前区间加入结果数组。
            if (result.isEmpty() || interval[0] > result.get(result.size() - 1)[1]) {
                result.add(interval);
            } else {
                final int[] ints = result.get(result.size() - 1);
                ints[1] = Math.max(ints[1], interval[1]);
            }
        }
        return result.toArray(new int[0][]);
    }

注意合并的写法,需要取当前元素的endi 和队首元素的endi 的最大值,在这里踩过坑。

LC62unique_paths

. - 力扣(LeetCode)

题目:

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。

问总共有多少条不同的路径?

解法:

因为每次只能向下或者向右移动一步,因此要走到右下角,上一步只能从下面下来,或者从左边过来。对每个位置来说也可以这么推断,这里动态数组定义比较直观dp[i,j] 就是走到坐标[i,j] 一共有多少路径。动态方程dp[i,j] = dp[i-1,j] + dp[i,j-1]  。这里还涉及到动态数组的初始化,第一列和第一排的走法只有1种。

 public int uniquePaths(int m, int n) {
        int[][] dp = new int[m][n];
        //初始化行列的情况
        for (int i = 0; i < m; i++) {
            dp[i][0] = 1;
        }
        for (int i = 0; i < n; i++) {
            dp[0][i] = 1;
        }

        for (int i = 1; i < m; i++) {
            for (int j = 1; j < n; j++) {
                dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
            }
        }
        return dp[m - 1][n - 1];
    }

LC64minimum_path

. - 力扣(LeetCode)

题目:

给定一个包含非负整数的 m x n 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。

解法:

这道题跟上一道类似,不同的是这里求最小和,其实走法是一样的,因为只能从上面或者左边过来。动态数组定义为当前位置的最小值,动态方程有所调整,dp[i,j]=min(dp[i-1,j],dp[j,i-1]) +nums[i,j] 。 

 public int minPathSum(int[][] grid) {
        final int m = grid.length, n = grid[0].length;
        int[][] dp = new int[m][n];
        dp[0][0] = grid[0][0];
        for (int i = 1; i < m; i++) {
            dp[i][0] = grid[i][0] + dp[i - 1][0];
        }
        for (int i = 1; i < n; i++) {
            dp[0][i] = grid[0][i] + dp[0][i - 1];
        }
        for (int i = 1; i < m; i++) {
            for (int j = 1; j < n; j++) {
                dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j];
            }
        }
        return dp[m - 1][n - 1];
    }

这里有个优化,可以不用新增动态数组dp ,直接在原数组grid 上直接操作,这样能节省内存开销。

LC70climbing_stairs

. - 力扣(LeetCode)

题目:

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。

每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

解法:

经典的爬楼梯算法题,他可以用递归的方式,也可以使用动态规划的思路。这类题目乍一看没思路,实际跟上面机器人走路是一样,要么从前1个台阶过来,要么从前2个台阶过来。动态方程比较简单:dp[i] = dp[i-1]+dp[i-2] 。因为只涉及前2个值,可以用2个变量替代数组。

public int climbStairs(int n) {
        if (n < 3) {
            return n;
        }
        int one = 1;
        int two = 2;
        for (int i = 3; i <= n; i++) {
            int cur = one + two;
            one = two;
            two = cur;
        }
        return two;
    }

LC72edit_distance

. - 力扣(LeetCode)

题目:

给你两个单词 word1 和 word2, 请返回将 word1 转换成 word2 所使用的最少操作数  。

你可以对一个单词进行如下三种操作:

  • 插入一个字符
  • 删除一个字符
  • 替换一个字符

解法:

差不多也是套路题,这道题目跟机器走路的思路类似,可以参考下面的图

dp[i][j] 表示wrod1 的前 i 个字符转换成word2 的前j个字段所用的最小操作数 。相当于求最后一个格子的值。有点复杂的地方在于第一行和第一列作为空串进行初始化,第一行的意思是空串 ‘’ 变成'ros'  所需的最小步骤。第一列同理,这样就初始化好了动态数组。这里的动态方程有点复杂,需要分情况判断:

  • 当 word1[i] == word2[j],dp[i][j] = dp[i-1][j-1];
  • 当 word1[i] != word2[j],dp[i][j] = min(dp[i-1][j-1], dp[i-1][j], dp[i][j-1]) + 1 ;
    • dp[i-1][j-1] 表示替换操作
    • dp[i-1][j] 表示删除操作
    • dp[i][j-1] 表示插入操作。
public int minDistance(String word1, String word2) {
        final int row = word1.length();
        final int col = word2.length();
        // 因为有初始行和初始列,所以这里长度+1
        int[][] dp = new int[row + 1][col + 1];
        for (int i = 0; i <= row; i++) {
            dp[i][0] = i;
        }
        for (int i = 0; i <= col; i++) {
            dp[0][i] = i;
        }
        for (int i = 0; i < row; i++) {
            for (int j = 0; j < col; j++) {
                if (word1.charAt(i) == word2.charAt(j)) {
                    dp[i + 1][j + 1] = dp[i][j];
                } else {
                    dp[i + 1][j + 1] = Math.min(Math.min(dp[i][j], dp[i][j + 1]), dp[i + 1][j]) + 1;
                }

            }
        }
        return dp[row][col];
    }

LC75sort_colors

. - 力扣(LeetCode)

题目:

给定一个包含红色、白色和蓝色、共 n 个元素的数组 nums ,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。

我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。

必须在不使用库内置的 sort 函数的情况下解决这个问题。

解法:

套路题,需要了解思路。维护0,1,2的初始下标,其中0,1初始在0,2初始在len-1,然后迭代数组,对数组中的数字进行判断,并相应的移动下标。可以参考官方的讲解配图,比较好理解。

public void sortColors(int[] nums) {
        if (nums.length == 0) {
            return;
        }
        //定义几个下标,需要实现
        // all in [0, zero] = 0
        // all in (zero, i) = 1
        // all in (two, len - 1] = 2
        int zero = 0, cur = 0, two = nums.length - 1;
        while (cur <= two) {
            if (nums[cur] == 0) {
                swap(nums, zero, cur);
                zero++;
                cur++;
            } else if (nums[cur] == 1) {
                cur++;
            } else {
                swap(nums, cur, two);
                //注意 这个时候cur 不能移动,因为交换之后还要再判断下
                two--;
            }
        }
    }


private void swap(int[] nums, int i, int j) {
        final int tmp = nums[i];
        nums[i] = nums[j];
        nums[j] = tmp;
    }

相关推荐

  1. LeetCode HOT 100(P11~P20)

    2024-03-26 23:22:02       36 阅读
  2. LeetCodeHot100 - 括号生成

    2024-03-26 23:22:02       38 阅读
  3. LeetCode hot100-21

    2024-03-26 23:22:02       39 阅读
  4. LeetCodeHot100-两数之和

    2024-03-26 23:22:02       42 阅读
  5. LeetCodeHot100-两数相加

    2024-03-26 23:22:02       42 阅读
  6. LeetCodeHot100 - 正则表达式匹配

    2024-03-26 23:22:02       33 阅读
  7. LeetCodeHot100 - 三数之和

    2024-03-26 23:22:02       33 阅读
  8. LeetCodeHot100 - 有效的括号

    2024-03-26 23:22:02       38 阅读
  9. LeetCodeHot100 - 最长有效括号

    2024-03-26 23:22:02       32 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-03-26 23:22:02       98 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-03-26 23:22:02       106 阅读
  3. 在Django里面运行非项目文件

    2024-03-26 23:22:02       87 阅读
  4. Python语言-面向对象

    2024-03-26 23:22:02       96 阅读

热门阅读

  1. 验证回文串

    2024-03-26 23:22:02       40 阅读
  2. 掌握大型语言模型的指南

    2024-03-26 23:22:02       43 阅读
  3. 如何解析Mysql中的binlog日志?

    2024-03-26 23:22:02       38 阅读
  4. Linux 下移植代码到ARM9 芯片需要注意的事项

    2024-03-26 23:22:02       40 阅读
  5. AI大模型学习

    2024-03-26 23:22:02       44 阅读
  6. vue的setup语法糖?

    2024-03-26 23:22:02       39 阅读