Leetcode: 0041-0050题速览

Leetcode: 0041-0050题速览

本文材料来自于LeetCode solutions in any programming language | 多种编程语言实现 LeetCode、《剑指 Offer(第 2 版)》、《程序员面试金典(第 6 版)》题解

遵从开源协议为知识共享 版权归属-相同方式共享 4.0 国际 公共许可证

研一在读备战就业,制此集合便于空闲时间浏览,有任何疑惑问题欢迎讨论,共同进步

目录

  • Leetcode: 0041-0050题速览
    • [41. 缺失的第一个正数](https://leetcode.cn/problems/first-missing-positive)
      • 题目描述
      • 解法
        • 方法一:原地交换
          • Python3
          • Java
          • C++
    • [42. 接雨水](https://leetcode.cn/problems/trapping-rain-water)
      • 题目描述
      • 解法
        • 方法一:动态规划
          • Python3
          • Java
          • C++
    • [43. 字符串相乘](https://leetcode.cn/problems/multiply-strings)
      • 题目描述
      • 解法
        • 方法一:数学乘法模拟
          • Python3
          • Java
          • C++
    • [44. 通配符匹配](https://leetcode.cn/problems/wildcard-matching)
      • 题目描述
      • 解法
        • 方法一:记忆化搜索
          • Python3
          • Java
          • C++
    • [45. 跳跃游戏 II](https://leetcode.cn/problems/jump-game-ii)
      • 题目描述
      • 解法
        • 方法一:贪心
          • Python3
          • Java
          • C++
    • [46. 全排列](https://leetcode.cn/problems/permutations)
      • 题目描述
      • 解法
        • 方法一:DFS(回溯)
          • Python3
          • Python3
          • Java
          • C++
    • [47. 全排列 II](https://leetcode.cn/problems/permutations-ii)
      • 题目描述
      • 解法
        • 方法一:排序 + 回溯
          • Python3
          • Java
          • C++
    • [48. 旋转图像](https://leetcode.cn/problems/rotate-image)
      • 题目描述
      • 解法
        • 方法一:原地翻转
          • Python3
          • Java
          • C++
    • [49. 字母异位词分组](https://leetcode.cn/problems/group-anagrams)
      • 题目描述
      • 解法
        • 方法一:哈希表
          • Python3
          • Java
          • C++
    • [50. Pow(x, n)](https://leetcode.cn/problems/powx-n)
      • 题目描述
      • 解法
        • 方法一:数学(快速幂)
          • Python3
          • Java
          • C++

41. 缺失的第一个正数

题目描述

给你一个未排序的整数数组 nums ,请你找出其中没有出现的最小的正整数。

请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。

 

示例 1:

输入:nums = [1,2,0]
输出:3
解释:范围 [1,2] 中的数字都在数组中。

示例 2:

输入:nums = [3,4,-1,1]
输出:2
解释:1 在数组中,但 2 没有。

示例 3:

输入:nums = [7,8,9,11,12]
输出:1
解释:最小的正数 1 没有出现。

 

提示:

  • 1 <= nums.length <= 105
  • -231 <= nums[i] <= 231 - 1

难度:困难

标签:数组,哈希表

解法

方法一:原地交换

我们假设数组 n u m s nums nums 长度为 n n n,那么最小的正整数一定在 [ 1 , . . , n + 1 ] [1, .., n + 1] [1,..,n+1] 之间。我们可以遍历数组,将数组中的每个数 x x x 交换到它应该在的位置上,即 x x x 应该在的位置为 x − 1 x - 1 x1。如果 x x x 不在 [ 1 , n + 1 ] [1, n + 1] [1,n+1] 之间,那么我们就不用管它。

遍历结束后,我们再遍历数组,如果 i + 1 i+1 i+1 不等于 n u m s [ i ] nums[i] nums[i],那么 i + 1 i+1 i+1 就是我们要找的最小的正整数。

时间复杂度 O ( n ) O(n) O(n),其中 n n n 是数组的长度。空间复杂度 O ( 1 ) O(1) O(1)

Python3
class Solution:def firstMissingPositive(self, nums: List[int]) -> int:def swap(i, j):nums[i], nums[j] = nums[j], nums[i]n = len(nums)for i in range(n):while 1 <= nums[i] <= n and nums[i] != nums[nums[i] - 1]:swap(i, nums[i] - 1)for i in range(n):if i + 1 != nums[i]:return i + 1return n + 1
Java
class Solution {public int firstMissingPositive(int[] nums) {int n = nums.length;for (int i = 0; i < n; ++i) {while (nums[i] >= 1 && nums[i] <= n && nums[i] != nums[nums[i] - 1]) {swap(nums, i, nums[i] - 1);}}for (int i = 0; i < n; ++i) {if (i + 1 != nums[i]) {return i + 1;}}return n + 1;}private void swap(int[] nums, int i, int j) {int t = nums[i];nums[i] = nums[j];nums[j] = t;}
}
C++
class Solution {
public:int firstMissingPositive(vector<int>& nums) {int n = nums.size();for (int i = 0; i < n; ++i) {while (nums[i] >= 1 && nums[i] <= n && nums[i] != nums[nums[i] - 1]) {swap(nums[i], nums[nums[i] - 1]);}}for (int i = 0; i < n; ++i) {if (i + 1 != nums[i]) {return i + 1;}}return n + 1;}
};

42. 接雨水

题目描述

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

 

示例 1:

接雨水

输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]
输出:6
解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。

示例 2:

输入:height = [4,2,0,3,2,5]
输出:9

 

提示:

  • n == height.length
  • 1 <= n <= 2 * 104
  • 0 <= height[i] <= 105

难度:困难

标签:栈,数组,双指针,动态规划,单调栈

解法

方法一:动态规划

我们定义 l e f t [ i ] left[i] left[i] 表示下标 i i i 位置及其左边的最高柱子的高度,定义 r i g h t [ i ] right[i] right[i] 表示下标 i i i 位置及其右边的最高柱子的高度。那么下标 i i i 位置能接的雨水量为 min ⁡ ( l e f t [ i ] , r i g h t [ i ] ) − h e i g h t [ i ] \min(left[i], right[i]) - height[i] min(left[i],right[i])height[i]。我们遍历数组,计算出 l e f t [ i ] left[i] left[i] r i g h t [ i ] right[i] right[i],最后答案为 ∑ i = 0 n − 1 min ⁡ ( l e f t [ i ] , r i g h t [ i ] ) − h e i g h t [ i ] \sum_{i=0}^{n-1} \min(left[i], right[i]) - height[i] i=0n1min(left[i],right[i])height[i]

时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( n ) O(n) O(n)。其中 n n n 为数组的长度。

Python3
class Solution:def trap(self, height: List[int]) -> int:n = len(height)left = [height[0]] * nright = [height[-1]] * nfor i in range(1, n):left[i] = max(left[i - 1], height[i])right[n - i - 1] = max(right[n - i], height[n - i - 1])return sum(min(l, r) - h for l, r, h in zip(left, right, height))
Java
class Solution {public int trap(int[] height) {int n = height.length;int[] left = new int[n];int[] right = new int[n];left[0] = height[0];right[n - 1] = height[n - 1];for (int i = 1; i < n; ++i) {left[i] = Math.max(left[i - 1], height[i]);right[n - i - 1] = Math.max(right[n - i], height[n - i - 1]);}int ans = 0;for (int i = 0; i < n; ++i) {ans += Math.min(left[i], right[i]) - height[i];}return ans;}
}
C++
class Solution {
public:int trap(vector<int>& height) {int n = height.size();int left[n], right[n];left[0] = height[0];right[n - 1] = height[n - 1];for (int i = 1; i < n; ++i) {left[i] = max(left[i - 1], height[i]);right[n - i - 1] = max(right[n - i], height[n - i - 1]);}int ans = 0;for (int i = 0; i < n; ++i) {ans += min(left[i], right[i]) - height[i];}return ans;}
};

43. 字符串相乘

题目描述

给定两个以字符串形式表示的非负整数 num1 和 num2,返回 num1 和 num2 的乘积,它们的乘积也表示为字符串形式。

注意:不能使用任何内置的 BigInteger 库或直接将输入转换为整数。

 

示例 1:

输入: num1 = “2”, num2 = “3”
输出: “6”

示例 2:

输入: num1 = “123”, num2 = “456”
输出: “56088”

 

提示:

  • 1 <= num1.length, num2.length <= 200
  • num1 和 num2 只能由数字组成。
  • num1 和 num2 都不包含任何前导零,除了数字0本身。

难度:中等

标签:数学,字符串,模拟

解法

方法一:数学乘法模拟

假设 n u m 1 num1 num1 n u m 2 num2 num2 的长度分别为 m m m n n n,则它们的乘积的长度最多为 m + n m + n m+n

证明如下:

  • 如果 n u m 1 num1 num1 n u m 2 num2 num2 都取最小值,那么它们的乘积为 10 m − 1 × 10 n − 1 = 10 m + n − 2 {10}^{m - 1} \times {10}^{n - 1} = {10}^{m + n - 2} 10m1×10n1=10m+n2,长度为 m + n − 1 m + n - 1 m+n1
  • 如果 n u m 1 num1 num1 n u m 2 num2 num2 都取最大值,那么它们的乘积为 ( 10 m − 1 ) × ( 10 n − 1 ) = 10 m + n − 10 m − 10 n + 1 ({10}^m - 1) \times ({10}^n - 1) = {10}^{m + n} - {10}^m - {10}^n + 1 (10m1)×(10n1)=10m+n10m10n+1,长度为 m + n m + n m+n

因此,我们可以申请一个长度为 m + n m + n m+n 的数组,用于存储乘积的每一位。

从低位到高位,依次计算乘积的每一位,最后将数组转换为字符串即可。

注意判断最高位是否为 0 0 0,如果是,则去掉。

时间复杂度 O ( m × n ) O(m \times n) O(m×n),空间复杂度 O ( m + n ) O(m + n) O(m+n)。其中 m m m n n n 分别为 n u m 1 num1 num1 n u m 2 num2 num2 的长度。

Python3
class Solution:def multiply(self, num1: str, num2: str) -> str:if num1 == "0" or num2 == "0":return "0"m, n = len(num1), len(num2)arr = [0] * (m + n)for i in range(m - 1, -1, -1):a = int(num1[i])for j in range(n - 1, -1, -1):b = int(num2[j])arr[i + j + 1] += a * bfor i in range(m + n - 1, 0, -1):arr[i - 1] += arr[i] // 10arr[i] %= 10i = 0 if arr[0] else 1return "".join(str(x) for x in arr[i:])
Java
class Solution {public String multiply(String num1, String num2) {if ("0".equals(num1) || "0".equals(num2)) {return "0";}int m = num1.length(), n = num2.length();int[] arr = new int[m + n];for (int i = m - 1; i >= 0; --i) {int a = num1.charAt(i) - '0';for (int j = n - 1; j >= 0; --j) {int b = num2.charAt(j) - '0';arr[i + j + 1] += a * b;}}for (int i = arr.length - 1; i > 0; --i) {arr[i - 1] += arr[i] / 10;arr[i] %= 10;}int i = arr[0] == 0 ? 1 : 0;StringBuilder ans = new StringBuilder();for (; i < arr.length; ++i) {ans.append(arr[i]);}return ans.toString();}
}
C++
class Solution {
public:string multiply(string num1, string num2) {if (num1 == "0" || num2 == "0") {return "0";}int m = num1.size(), n = num2.size();vector<int> arr(m + n);for (int i = m - 1; i >= 0; --i) {int a = num1[i] - '0';for (int j = n - 1; j >= 0; --j) {int b = num2[j] - '0';arr[i + j + 1] += a * b;}}for (int i = arr.size() - 1; i; --i) {arr[i - 1] += arr[i] / 10;arr[i] %= 10;}int i = arr[0] ? 0 : 1;string ans;for (; i < arr.size(); ++i) {ans += '0' + arr[i];}return ans;}
};

44. 通配符匹配

题目描述

给你一个输入字符串 ( s) 和一个字符模式 ( p) ,请你实现一个支持 '?''*' 匹配规则的通配符匹配:
  • '?' 可以匹配任何单个字符。
  • '*' 可以匹配任意字符序列(包括空字符序列)。

判定匹配成功的充要条件是:字符模式必须能够 完全匹配 输入字符串(而不是部分匹配)。

 

示例 1:

输入:s = “aa”, p = “a”
输出:false
解释:“a” 无法匹配 “aa” 整个字符串。

示例 2:

输入:s = “aa”, p = ""
输出:true
解释:
’ 可以匹配任意字符串。

示例 3:

输入:s = “cb”, p = “?a”
输出:false
解释:’?’ 可以匹配 ‘c’, 但第二个 ‘a’ 无法匹配 ‘b’。

 

提示:

  • 0 <= s.length, p.length <= 2000
  • s 仅由小写英文字母组成
  • p 仅由小写英文字母、'?''*' 组成

难度:困难

标签:贪心,递归,字符串,动态规划

解法

方法一:记忆化搜索

我们设计一个函数 d f s ( i , j ) dfs(i, j) dfs(i,j),表示从字符串 s s s 的第 i i i 个字符开始和字符串 p p p 的第 j j j 个字符开始是否匹配。那么答案就是 d f s ( 0 , 0 ) dfs(0, 0) dfs(0,0)

函数 d f s ( i , j ) dfs(i, j) dfs(i,j) 的执行过程如下:

  • 如果 i ≥ len ( s ) i \geq \textit{len}(s) ilen(s),那么只有当 j ≥ len ( p ) j \geq \textit{len}(p) jlen(p) 或者 p [ j ] = ′ ∗ ′ p[j] = '*' p[j]= d f s ( i , j + 1 ) dfs(i, j + 1) dfs(i,j+1) 为真时, d f s ( i , j ) dfs(i, j) dfs(i,j) 才为真。
  • 如果 j ≥ len ( p ) j \geq \textit{len}(p) jlen(p),那么 d f s ( i , j ) dfs(i, j) dfs(i,j) 为假。
  • 如果 p [ j ] = ′ ∗ ′ p[j] = '*' p[j]=,那么 d f s ( i , j ) dfs(i, j) dfs(i,j) 为真当且仅当 d f s ( i + 1 , j ) dfs(i + 1, j) dfs(i+1,j) d f s ( i + 1 , j + 1 ) dfs(i + 1, j + 1) dfs(i+1,j+1) d f s ( i , j + 1 ) dfs(i, j + 1) dfs(i,j+1) 中有一个为真。
  • 否则 d f s ( i , j ) dfs(i, j) dfs(i,j) 为真当且仅当 p [ j ] = ′ ? ′ p[j] = '?' p[j]=? s [ i ] = p [ j ] s[i] = p[j] s[i]=p[j] d f s ( i + 1 , j + 1 ) dfs(i + 1, j + 1) dfs(i+1,j+1) 为真。

为了避免重复计算,我们使用记忆化搜索的方法,将 d f s ( i , j ) dfs(i, j) dfs(i,j) 的结果存储在一个哈希表中。

时间复杂度 O ( m × n ) O(m \times n) O(m×n),空间复杂度 O ( m × n ) O(m \times n) O(m×n)。其中 m m m n n n 分别是字符串 s s s p p p 的长度。

Python3
class Solution:def isMatch(self, s: str, p: str) -> bool:@cachedef dfs(i: int, j: int) -> bool:if i >= len(s):return j >= len(p) or (p[j] == "*" and dfs(i, j + 1))if j >= len(p):return Falseif p[j] == "*":return dfs(i + 1, j) or dfs(i + 1, j + 1) or dfs(i, j + 1)return (p[j] == "?" or s[i] == p[j]) and dfs(i + 1, j + 1)return dfs(0, 0)
Java
class Solution {private Boolean[][] f;private char[] s;private char[] p;private int m;private int n;public boolean isMatch(String s, String p) {this.s = s.toCharArray();this.p = p.toCharArray();m = s.length();n = p.length();f = new Boolean[m][n];return dfs(0, 0);}private boolean dfs(int i, int j) {if (i >= m) {return j >= n || (p[j] == '*' && dfs(i, j + 1));}if (j >= n) {return false;}if (f[i][j] != null) {return f[i][j];}if (p[j] == '*') {f[i][j] = dfs(i + 1, j) || dfs(i + 1, j + 1) || dfs(i, j + 1);} else {f[i][j] = (p[j] == '?' || s[i] == p[j]) && dfs(i + 1, j + 1);}return f[i][j];}
}
C++
class Solution {
public:bool isMatch(string s, string p) {int m = s.size(), n = p.size();int f[m + 1][n + 1];memset(f, -1, sizeof(f));function<bool(int, int)> dfs = [&](int i, int j) {if (i >= m) {return j >= n || (p[j] == '*' && dfs(i, j + 1));}if (j >= n) {return false;}if (f[i][j] != -1) {return f[i][j] == 1;}if (p[j] == '*') {f[i][j] = dfs(i + 1, j) || dfs(i, j + 1) ? 1 : 0;} else {f[i][j] = (p[j] == '?' || s[i] == p[j]) && dfs(i + 1, j + 1) ? 1 : 0;}return f[i][j] == 1;};return dfs(0, 0);}
};

45. 跳跃游戏 II

题目描述

给定一个长度为 n0 索引整数数组 nums。初始位置为 nums[0]

每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说,如果你在 nums[i] 处,你可以跳转到任意 nums[i + j] 处:

  • 0 <= j <= nums[i] 
  • i + j < n

返回到达 nums[n - 1] 的最小跳跃次数。生成的测试用例可以到达 nums[n - 1]

 

示例 1:

输入: nums = [2,3,1,1,4]
输出: 2
解释: 跳到最后一个位置的最小跳跃数是 2
  从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。

示例 2:

输入: nums = [2,3,0,1,4]
输出: 2

 

提示:

  • 1 <= nums.length <= 104
  • 0 <= nums[i] <= 1000
  • 题目保证可以到达 nums[n-1]

难度:中等

标签:贪心,数组,动态规划

解法

方法一:贪心

我们可以用变量 m x mx mx 记录当前位置能够到达的最远位置,用变量 l a s t last last 记录上一次跳跃到的位置,用变量 a n s ans ans 记录跳跃的次数。

接下来,我们遍历 [ 0 , . . n − 2 ] [0,..n - 2] [0,..n2] 的每一个位置 i i i,对于每一个位置 i i i,我们可以通过 i + n u m s [ i ] i + nums[i] i+nums[i] 计算出当前位置能够到达的最远位置,我们用 m x mx mx 来记录这个最远位置,即 m x = m a x ( m x , i + n u m s [ i ] ) mx = max(mx, i + nums[i]) mx=max(mx,i+nums[i])。接下来,判断当前位置是否到达了上一次跳跃的边界,即 i = l a s t i = last i=last,如果到达了,那么我们就需要进行一次跳跃,将 l a s t last last 更新为 m x mx mx,并且将跳跃次数 a n s ans ans 增加 1 1 1

最后,我们返回跳跃的次数 a n s ans ans 即可。

时间复杂度 O ( n ) O(n) O(n),其中 n n n 是数组的长度。空间复杂度 O ( 1 ) O(1) O(1)

相似题目:

  • 55. 跳跃游戏
  • 1024. 视频拼接
  • 1326. 灌溉花园的最少水龙头数目
Python3
class Solution:def jump(self, nums: List[int]) -> int:ans = mx = last = 0for i, x in enumerate(nums[:-1]):mx = max(mx, i + x)if last == i:ans += 1last = mxreturn ans
Java
class Solution {public int jump(int[] nums) {int ans = 0, mx = 0, last = 0;for (int i = 0; i < nums.length - 1; ++i) {mx = Math.max(mx, i + nums[i]);if (last == i) {++ans;last = mx;}}return ans;}
}
C++
class Solution {
public:int jump(vector<int>& nums) {int ans = 0, mx = 0, last = 0;for (int i = 0; i < nums.size() - 1; ++i) {mx = max(mx, i + nums[i]);if (last == i) {++ans;last = mx;}}return ans;}
};

46. 全排列

题目描述

给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。

 

示例 1:

输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]

示例 2:

输入:nums = [0,1]
输出:[[0,1],[1,0]]

示例 3:

输入:nums = [1]
输出:[[1]]

 

提示:

  • 1 <= nums.length <= 6
  • -10 <= nums[i] <= 10
  • nums 中的所有整数 互不相同

难度:中等

标签:数组,回溯

解法

方法一:DFS(回溯)

我们设计一个函数 d f s ( i ) dfs(i) dfs(i) 表示已经填完了前 i i i 个位置,现在需要填第 i + 1 i+1 i+1 个位置。枚举所有可能的数,如果这个数没有被填过,就填入这个数,然后继续填下一个位置,直到填完所有的位置。

时间复杂度 O ( n × n ! ) O(n \times n!) O(n×n!),其中 n n n 是数组的长度。一共有 n ! n! n! 个排列,每个排列需要 O ( n ) O(n) O(n) 的时间来构造。

相似题目:

  • 47. 全排列 II
Python3
class Solution:def permute(self, nums: List[int]) -> List[List[int]]:return list(permutations(nums))
Python3
class Solution:def permute(self, nums: List[int]) -> List[List[int]]:def dfs(i):if i == n:ans.append(t[:])returnfor j in range(n):if not vis[j]:vis[j] = Truet[i] = nums[j]dfs(i + 1)vis[j] = Falsen = len(nums)vis = [False] * nt = [0] * nans = []dfs(0)return ans
Java
class Solution {private List<List<Integer>> ans = new ArrayList<>();private List<Integer> t = new ArrayList<>();private boolean[] vis;private int[] nums;public List<List<Integer>> permute(int[] nums) {this.nums = nums;vis = new boolean[nums.length];dfs(0);return ans;}private void dfs(int i) {if (i == nums.length) {ans.add(new ArrayList<>(t));return;}for (int j = 0; j < nums.length; ++j) {if (!vis[j]) {vis[j] = true;t.add(nums[j]);dfs(i + 1);t.remove(t.size() - 1);vis[j] = false;}}}
}
C++
class Solution {
public:vector<vector<int>> permute(vector<int>& nums) {int n = nums.size();vector<vector<int>> ans;vector<int> t(n);vector<bool> vis(n);function<void(int)> dfs = [&](int i) {if (i == n) {ans.emplace_back(t);return;}for (int j = 0; j < n; ++j) {if (!vis[j]) {vis[j] = true;t[i] = nums[j];dfs(i + 1);vis[j] = false;}}};dfs(0);return ans;}
};

47. 全排列 II

题目描述

给定一个可包含重复数字的序列 nums按任意顺序 返回所有不重复的全排列。

 

示例 1:

输入:nums = [1,1,2]
输出:
[[1,1,2],
[1,2,1],
[2,1,1]]

示例 2:

输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]

 

提示:

  • 1 <= nums.length <= 8
  • -10 <= nums[i] <= 10

难度:中等

标签:数组,回溯

解法

方法一:排序 + 回溯

我们可以先对数组进行排序,这样就可以将重复的数字放在一起,方便我们进行去重。

然后,我们设计一个函数 d f s ( i ) dfs(i) dfs(i),表示当前需要填写第 i i i 个位置的数。函数的具体实现如下:

  • 如果 i = n i = n i=n,说明我们已经填写完毕,将当前排列加入答案数组中,然后返回。
  • 否则,我们枚举第 i i i 个位置的数 n u m s [ j ] nums[j] nums[j],其中 j j j 的范围是 [ 0 , n − 1 ] [0, n - 1] [0,n1]。我们需要保证 n u m s [ j ] nums[j] nums[j] 没有被使用过,并且与前面枚举的数不同,这样才能保证当前排列不重复。如果满足条件,我们就可以填写 n u m s [ j ] nums[j] nums[j],并继续递归地填写下一个位置,即调用 d f s ( i + 1 ) dfs(i + 1) dfs(i+1)。在递归调用结束后,我们需要将 n u m s [ j ] nums[j] nums[j] 标记为未使用,以便于进行后面的枚举。

在主函数中,我们首先对数组进行排序,然后调用 d f s ( 0 ) dfs(0) dfs(0),即从第 0 个位置开始填写,最终返回答案数组即可。

时间复杂度 O ( n × n ! ) O(n \times n!) O(n×n!),空间复杂度 O ( n ) O(n) O(n)。其中 n n n 是数组的长度。需要进行 n ! n! n! 次枚举,每次枚举需要 O ( n ) O(n) O(n) 的时间来判断是否重复。另外,我们需要一个标记数组来标记每个位置是否被使用过,因此空间复杂度为 O ( n ) O(n) O(n)

相似题目:

  • 46. 全排列
Python3
class Solution:def permuteUnique(self, nums: List[int]) -> List[List[int]]:def dfs(i: int):if i == n:ans.append(t[:])returnfor j in range(n):if vis[j] or (j and nums[j] == nums[j - 1] and not vis[j - 1]):continuet[i] = nums[j]vis[j] = Truedfs(i + 1)vis[j] = Falsen = len(nums)nums.sort()ans = []t = [0] * nvis = [False] * ndfs(0)return ans
Java
class Solution {private List<List<Integer>> ans = new ArrayList<>();private List<Integer> t = new ArrayList<>();private int[] nums;private boolean[] vis;public List<List<Integer>> permuteUnique(int[] nums) {Arrays.sort(nums);this.nums = nums;vis = new boolean[nums.length];dfs(0);return ans;}private void dfs(int i) {if (i == nums.length) {ans.add(new ArrayList<>(t));return;}for (int j = 0; j < nums.length; ++j) {if (vis[j] || (j > 0 && nums[j] == nums[j - 1] && !vis[j - 1])) {continue;}t.add(nums[j]);vis[j] = true;dfs(i + 1);vis[j] = false;t.remove(t.size() - 1);}}
}
C++
class Solution {
public:vector<vector<int>> permuteUnique(vector<int>& nums) {sort(nums.begin(), nums.end());int n = nums.size();vector<vector<int>> ans;vector<int> t(n);vector<bool> vis(n);function<void(int)> dfs = [&](int i) {if (i == n) {ans.emplace_back(t);return;}for (int j = 0; j < n; ++j) {if (vis[j] || (j && nums[j] == nums[j - 1] && !vis[j - 1])) {continue;}t[i] = nums[j];vis[j] = true;dfs(i + 1);vis[j] = false;}};dfs(0);return ans;}
};

48. 旋转图像

题目描述

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

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

 

示例 1:

矩阵旋转

输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[[7,4,1],[8,5,2],[9,6,3]]

示例 2:

矩阵旋转

输入:matrix = [[5,1,9,11],[2,4,8,10],[13,3,6,7],[15,14,12,16]]
输出:[[15,13,2,5],[14,3,4,1],[12,6,8,9],[16,7,10,11]]

 

提示:

  • n == matrix.length == matrix[i].length
  • 1 <= n <= 20
  • -1000 <= matrix[i][j] <= 1000

 

难度:中等

标签:数组,数学,矩阵

解法

方法一:原地翻转

根据题目要求,我们实际上需要将 m a t r i x [ i ] [ j ] matrix[i][j] matrix[i][j] 旋转至 m a t r i x [ j ] [ n − i − 1 ] matrix[j][n - i - 1] matrix[j][ni1]

我们可以先对矩阵进行上下翻转,即 m a t r i x [ i ] [ j ] matrix[i][j] matrix[i][j] m a t r i x [ n − i − 1 ] [ j ] matrix[n - i - 1][j] matrix[ni1][j] 进行交换,然后再对矩阵进行主对角线翻转,即 m a t r i x [ i ] [ j ] matrix[i][j] matrix[i][j] m a t r i x [ j ] [ i ] matrix[j][i] matrix[j][i] 进行交换。这样就能将 m a t r i x [ i ] [ j ] matrix[i][j] matrix[i][j] 旋转至 m a t r i x [ j ] [ n − i − 1 ] matrix[j][n - i - 1] matrix[j][ni1] 了。

时间复杂度 O ( n 2 ) O(n^2) O(n2),其中 n n n 是矩阵的边长。空间复杂度 O ( 1 ) O(1) O(1)

Python3
class Solution:def rotate(self, matrix: List[List[int]]) -> None:n = len(matrix)for i in range(n >> 1):for j in range(n):matrix[i][j], matrix[n - i - 1][j] = matrix[n - i - 1][j], matrix[i][j]for i in range(n):for j in range(i):matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j]
Java
class Solution {public void rotate(int[][] matrix) {int n = matrix.length;for (int i = 0; i < n >> 1; ++i) {for (int j = 0; j < n; ++j) {int t = matrix[i][j];matrix[i][j] = matrix[n - i - 1][j];matrix[n - i - 1][j] = t;}}for (int i = 0; i < n; ++i) {for (int j = 0; j < i; ++j) {int t = matrix[i][j];matrix[i][j] = matrix[j][i];matrix[j][i] = t;}}}
}
C++
class Solution {
public:void rotate(vector<vector<int>>& matrix) {int n = matrix.size();for (int i = 0; i < n >> 1; ++i) {for (int j = 0; j < n; ++j) {swap(matrix[i][j], matrix[n - i - 1][j]);}}for (int i = 0; i < n; ++i) {for (int j = 0; j < i; ++j) {swap(matrix[i][j], matrix[j][i]);}}}
};

49. 字母异位词分组

题目描述

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

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

 

示例 1:

输入: strs = [“eat”, “tea”, “tan”, “ate”, “nat”, “bat”]
输出: [[“bat”],[“nat”,“tan”],[“ate”,“eat”,“tea”]]

示例 2:

输入: strs = [""]
输出: [[""]]

示例 3:

输入: strs = [“a”]
输出: [[“a”]]

 

提示:

  • 1 <= strs.length <= 104
  • 0 <= strs[i].length <= 100
  • strs[i] 仅包含小写字母

难度:中等

标签:数组,哈希表,字符串,排序

解法

方法一:哈希表
  1. 遍历字符串,对每个字符串按照字符字典序排序,得到一个新的字符串。
  2. 以新字符串为 key[str]value,存入哈希表当中(HashMap<String, List<String>>)。
  3. 后续遍历得到相同 key 时,将其加入到对应的 value 当中即可。

strs = ["eat", "tea", "tan", "ate", "nat", "bat"] 为例,遍历结束时,哈希表的状况:

keyvalue
"aet"["eat", "tea", "ate"]
"ant"["tan", "nat"]
"abt"["bat"]

最后返回哈希表的 value 列表即可。

时间复杂度 O ( n × k × log ⁡ k ) O(n\times k\times \log k) O(n×k×logk)。其中 n n n k k k 分别是字符串数组的长度和字符串的最大长度。

Python3
class Solution:def groupAnagrams(self, strs: List[str]) -> List[List[str]]:d = defaultdict(list)for s in strs:k = ''.join(sorted(s))d[k].append(s)return list(d.values())
Java
class Solution {public List<List<String>> groupAnagrams(String[] strs) {Map<String, List<String>> d = new HashMap<>();for (String s : strs) {char[] t = s.toCharArray();Arrays.sort(t);String k = String.valueOf(t);d.computeIfAbsent(k, key -> new ArrayList<>()).add(s);}return new ArrayList<>(d.values());}
}
C++
class Solution {
public:vector<vector<string>> groupAnagrams(vector<string>& strs) {unordered_map<string, vector<string>> d;for (auto& s : strs) {string k = s;sort(k.begin(), k.end());d[k].emplace_back(s);}vector<vector<string>> ans;for (auto& [_, v] : d) ans.emplace_back(v);return ans;}
};

50. Pow(x, n)

题目描述

实现 pow(x, n) ,即计算 x 的整数 n 次幂函数(即,xn )。

 

示例 1:

输入:x = 2.00000, n = 10
输出:1024.00000

示例 2:

输入:x = 2.10000, n = 3
输出:9.26100

示例 3:

输入:x = 2.00000, n = -2
输出:0.25000
解释:2-2 = 1/22 = 1/4 = 0.25

 

提示:

  • -100.0 < x < 100.0
  • -231 <= n <= 231-1
  • n 是一个整数
  • 要么 x 不为零,要么 n > 0
  • -104 <= xn <= 104

难度:中等

标签:递归,数学

解法

方法一:数学(快速幂)

快速幂算法的核心思想是将幂指数 n n n 拆分为若干个二进制位上的 1 1 1 的和,然后将 x x x n n n 次幂转化为 x x x 的若干个幂的乘积。

时间复杂度 O ( log ⁡ n ) O(\log n) O(logn),空间复杂度 O ( 1 ) O(1) O(1)。其中 n n n 为幂指数。

Python3
class Solution:def myPow(self, x: float, n: int) -> float:def qpow(a: float, n: int) -> float:ans = 1while n:if n & 1:ans *= aa *= an >>= 1return ansreturn qpow(x, n) if n >= 0 else 1 / qpow(x, -n)
Java
class Solution {public double myPow(double x, int n) {return n >= 0 ? qpow(x, n) : 1 / qpow(x, -(long) n);}private double qpow(double a, long n) {double ans = 1;for (; n > 0; n >>= 1) {if ((n & 1) == 1) {ans = ans * a;}a = a * a;}return ans;}
}
C++
class Solution {
public:double myPow(double x, int n) {auto qpow = [](double a, long long n) {double ans = 1;for (; n; n >>= 1) {if (n & 1) {ans *= a;}a *= a;}return ans;};return n >= 0 ? qpow(x, n) : 1 / qpow(x, -(long long) n);}
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/438197.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++ | Leetcode C++题解之第447题回旋镖的数量

题目&#xff1a; 题解&#xff1a; class Solution { public:int numberOfBoomerangs(vector<vector<int>> &points) {int ans 0;for (auto &p : points) {unordered_map<int, int> cnt;for (auto &q : points) {int dis (p[0] - q[0]) * (p…

【Node.js】内置模块FileSystem的保姆级入门讲解

作者&#xff1a;CSDN-PleaSure乐事 欢迎大家阅读我的博客 希望大家喜欢 使用环境&#xff1a;Vscode 本文代码都经由博主PleaSure乐事实操后得出&#xff0c;可以放心使用。 1.FileSystem介绍 Node.js 的 fs&#xff08;filesystem&#xff09;模块是一个核心模块&#xff0c…

C++入门基础知识97——【关于C++ 条件运算符 ? :】

成长路上不孤单&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a; 【14后&#x1f60a;///C爱好者&#x1f60a;///持续分享所学&#x1f60a;///如有需要欢迎收藏转发///&#x1f60a;】 今日分享关于C 条件运算符 ? :的相关内容&#xff…

【PCL】Ubuntu22.04 安装 PCL 库

文章目录 前言一、更新系统软件包二、安装依赖项三、下载 PCL 源码四、编译和安装 PCL五、测试安装成功1、 pcd_write.cpp2、CMakeLists.txt3、build 前言 PCL&#xff08;Point Cloud Library&#xff09;是一个开源的大型项目&#xff0c;专注于2D/3D图像和点云处理。PCL为点…

在WPF中实现多语言切换的四种方式

在WPF中有多种方式可以实现多语言&#xff0c;这里提供几种常用的方式。 一、使用XML实现多语言切换 使用XML实现多语言的思路就是使用XML作为绑定的数据源。主要用到XmlDataProvider类. 使用XmlDataProvider.Source属性指定XML文件的路径或通过XmlDataProvider.Document指定…

【折半查找】

目录 一. 折半查找的概念二. 折半查找的过程三. 折半查找的代码实现四. 折半查找的性能分析 \quad 一. 折半查找的概念 \quad 必须有序 \quad 二. 折半查找的过程 \quad \quad 三. 折半查找的代码实现 \quad 背下来 \quad 四. 折半查找的性能分析 \quad 记住 比较的是层数 …

git 报错git: ‘remote-https‘ is not a git command. See ‘git --help‘.

报错内容 原因与解决方案 第一种情况&#xff1a;git路径错误 第一种很好解决&#xff0c;在环境变量中配置正确的git路径即可&#xff1b; 第二种情况 git缺少依赖 这个情况&#xff0c;网上提供了多种解决方案。但如果比较懒&#xff0c;可以直接把仓库地址的https改成ht…

Android 简单实现联系人列表+字母索引联动效果

效果如上图。 Main Ideas 左右两个列表左列表展示人员数据&#xff0c;含有姓氏首字母的 header item右列表是一个全由姓氏首字母组成的索引列表&#xff0c;点击某个item&#xff0c;展示一个气泡组件(它会自动延时关闭)&#xff0c; 左列表滚动并显示与点击的索引列表item …

Elasticsearch 8.16 和 JDK 23 中的语言环境变化

作者&#xff1a;来自 Elastic Simon Cooper 随着 JDK 23 即将发布&#xff0c;语言环境信息中有一些重大变化&#xff0c;这将影响 Elasticsearch 以及你提取和格式化日期时间数据的方式。首先&#xff0c;介绍一些背景知识。 什么是语言环境&#xff1f; 每次 Java 程序需要…

理解Matplotlib构图组成

介绍 Matplotlib 是 Python 中最流行的数据可视化库之一。它提供了一系列丰富的工具&#xff0c;可以绘制高度自定义且适用于各种应用场景的图表。无论你是数据科学家、工程师&#xff0c;还是需要处理数据图形表示的任何人&#xff0c;理解如何操作和定制 Matplotlib 中的图表…

三、数据链路层(上)

目录 3.1数据链路层概述 3.1.1术语 3.1.2功能 3.2封装成帧和透明传输 3.2.1封装成帧 ①字符计数法 ②字符&#xff08;节&#xff09;填充法 ③零比特填充法 ④违规编码法 3.2.2透明传输 3.2.3差错控制 差错原因 检错编码 奇偶校验 ☆循环冗余码CRC 例题 纠错…

骨架屏 (懒加载优化)

骨架屏 &#xff08;懒加载优化&#xff09; 即便通过 Webpack 的按需加载、CDN 静态资源缓存 和 代码分割 等技术来减少首屏的代码体积&#xff0c;首屏加载时的白屏时间&#xff08;也称为首屏等待时间&#xff09;仍然可能存在&#xff0c;尤其在网络条件较差或页面内容复杂…

MongoDB 快速入门+单机部署(附带脚本)

目录 介绍 体系结构 数据模型 BSON BSON 数据类型 特点 高性能 高可用 高扩展 丰富的查询支持 其他特点 部署 单机部署 普通安装 脚本安装 Docker Compose 安装 卸载 停止 MongoDB 删除包 删除数据目录 参考&#xff1a; https://docs.mongoing.com/ 介绍…

python全栈学习记录(二十一)类的继承、派生、组合

类的继承、派生、组合 文章目录 类的继承、派生、组合一、类的继承二、派生三、组合 一、类的继承 继承是一种新建类的方式&#xff0c;新建的类称为子类&#xff0c;被继承的类称为父类。 继承的特性是&#xff1a;子类会遗传父类的属性&#xff08;继承是类与类之间的关系&a…

2024年研究生数学建模“华为杯”E题——肘部法则、k-means聚类、目标检测(python)、ARIMA、逻辑回归、混淆矩阵(附:目标检测代码)

文章目录 一、情况介绍二、思路情况二、代码展示三、感受 一、情况介绍 前几天也是参加了研究生数学建模竞赛&#xff08;也就是华为杯&#xff09;&#xff0c;也是和本校的两个数学学院的朋友在网上组的队伍。昨天&#xff08;9.25&#xff09;通宵干完论文&#xff08;一条…

Prompt 初级版:构建高效对话的基础指南

Prompt 初级版&#xff1a;构建高效对话的基础指南 文章目录 Prompt 初级版&#xff1a;构建高效对话的基础指南一 “标准”提示二 角色提示三 多范例提示四 组合提示五 规范化提示 本文介绍了提示词的基础概念与不同类型&#xff0c;帮助用户更好地理解如何在对话中构建有效的…

Pytorch实现玉米基因表达量预测模型

一、实验要求 通过搭建残差卷积网络&#xff0c;实现对玉米基因表达量的预测 二、实验目的 理解基因表达量预测问题&#xff1a;基因表达预测是生物信息学和基因组学领域中的重要任务之一&#xff0c;促进学科交叉融合。熟悉深度学习框架PyTorch&#xff1a;通过实现基因表达量…

css 数字比汉字要靠上

这个问题通常是由于数字字体的下排的问题造成的&#xff0c;也就是数字的底部边缘位置比汉字的顶部边缘位置更靠下。为了解决这个问题&#xff0c;可以尝试以下几种方法&#xff1a; 使用CSS的vertical-align属性来调整对齐方式。例如&#xff0c;可以将数字的对齐方式设置为to…

Linux高级编程_27_系统调用

文章目录 系统调用函数分类系统编程概述系统调用概述**类UNIX系统的软件层次** 用户态和内核态系统调用与库函数的关系文件操作符概述文件磁盘权限 系统调用之文件操作open:打开文件close:关闭文件write:写入read:读取 文件状态fcntl 函数stat 函数 st_mode的值示例 1&#xff…

【优选算法之队列+宽搜/优先级队列】No.14--- 经典队列+宽搜/优先级队列算法

文章目录 前言一、队列宽搜示例&#xff1a;1.1 N 叉树的层序遍历1.2 ⼆叉树的锯⻮形层序遍历1.3 ⼆叉树最⼤宽度1.4 在每个树⾏中找最⼤值 二、优先级队列&#xff08;堆&#xff09;示例&#xff1a;2.1 最后⼀块⽯头的重量2.2 数据流中的第 K ⼤元素2.3 前 K 个⾼频单词2.4 …