LeetCode 热题 HOT 100(P21~P30)

 系列文章: 

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

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

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

LeetCode 热题 HOT 100(P31~P40)-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;
    }

  • 24
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
1. Two Sum 2. Add Two Numbers 3. Longest Substring Without Repeating Characters 4. Median of Two Sorted Arrays 5. Longest Palindromic Substring 6. ZigZag Conversion 7. Reverse Integer 8. String to Integer (atoi) 9. Palindrome Number 10. Regular Expression Matching 11. Container With Most Water 12. Integer to Roman 13. Roman to Integer 14. Longest Common Prefix 15. 3Sum 16. 3Sum Closest 17. Letter Combinations of a Phone Number 18. 4Sum 19. Remove Nth Node From End of List 20. Valid Parentheses 21. Merge Two Sorted Lists 22. Generate Parentheses 23. Swap Nodes in Pairs 24. Reverse Nodes in k-Group 25. Remove Duplicates from Sorted Array 26. Remove Element 27. Implement strStr() 28. Divide Two Integers 29. Substring with Concatenation of All Words 30. Next Permutation 31. Longest Valid Parentheses 32. Search in Rotated Sorted Array 33. Search for a Range 34. Find First and Last Position of Element in Sorted Array 35. Valid Sudoku 36. Sudoku Solver 37. Count and Say 38. Combination Sum 39. Combination Sum II 40. First Missing Positive 41. Trapping Rain Water 42. Jump Game 43. Merge Intervals 44. Insert Interval 45. Unique Paths 46. Minimum Path Sum 47. Climbing Stairs 48. Permutations 49. Permutations II 50. Rotate Image 51. Group Anagrams 52. Pow(x, n) 53. Maximum Subarray 54. Spiral Matrix 55. Jump Game II 56. Merge k Sorted Lists 57. Insertion Sort List 58. Sort List 59. Largest Rectangle in Histogram 60. Valid Number 61. Word Search 62. Minimum Window Substring 63. Unique Binary Search Trees 64. Unique Binary Search Trees II 65. Interleaving String 66. Maximum Product Subarray 67. Binary Tree Inorder Traversal 68. Binary Tree Preorder Traversal 69. Binary Tree Postorder Traversal 70. Flatten Binary Tree to Linked List 71. Construct Binary Tree from Preorder and Inorder Traversal 72. Construct Binary Tree from Inorder and Postorder Traversal 73. Binary Tree Level Order Traversal 74. Binary Tree Zigzag Level Order Traversal 75. Convert Sorted Array to Binary Search Tree 76. Convert Sorted List to Binary Search Tree 77. Recover Binary Search Tree 78. Sum Root to Leaf Numbers 79. Path Sum 80. Path Sum II 81. Binary Tree Maximum Path Sum 82. Populating Next Right Pointers in Each Node 83. Populating Next Right Pointers in Each Node II 84. Reverse Linked List 85. Reverse Linked List II 86. Partition List 87. Rotate List 88. Remove Duplicates from Sorted List 89. Remove Duplicates from Sorted List II 90. Intersection of Two Linked Lists 91. Linked List Cycle 92. Linked List Cycle II 93. Reorder List 94. Binary Tree Upside Down 95. Binary Tree Right Side View 96. Palindrome Linked List 97. Convert Binary Search Tree to Sorted Doubly Linked List 98. Lowest Common Ancestor of a Binary Tree 99. Lowest Common Ancestor of a Binary Search Tree 100. Binary Tree Level Order Traversal II
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值