Leetcode: 0041-0050题速览

news2024/11/25 22:34:42

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 + 1
        return 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]] * n
        right = [height[-1]] * n
        for 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 * b
        for i in range(m + n - 1, 0, -1):
            arr[i - 1] += arr[i] // 10
            arr[i] %= 10
        i = 0 if arr[0] else 1
        return "".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:
        @cache
        def 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 False
            if 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 = 0
        for i, x in enumerate(nums[:-1]):
            mx = max(mx, i + x)
            if last == i:
                ans += 1
                last = mx
        return 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[:])
                return
            for j in range(n):
                if not vis[j]:
                    vis[j] = True
                    t[i] = nums[j]
                    dfs(i + 1)
                    vis[j] = False

        n = len(nums)
        vis = [False] * n
        t = [0] * n
        ans = []
        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[:])
                return
            for j in range(n):
                if vis[j] or (j and nums[j] == nums[j - 1] and not vis[j - 1]):
                    continue
                t[i] = nums[j]
                vis[j] = True
                dfs(i + 1)
                vis[j] = False

        n = len(nums)
        nums.sort()
        ans = []
        t = [0] * n
        vis = [False] * n
        dfs(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 = 1
            while n:
                if n & 1:
                    ans *= a
                a *= a
                n >>= 1
            return ans

        return 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.coloradmin.cn/o/2187713.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CycleGAN图像风格迁移互换

tutorials/application/source_zh_cn/generative/cyclegan.ipynb MindSpore/docs - Gitee.com 本案例运行需要较大内存&#xff0c;建议在Ascend/GPU上运行。 模型介绍 模型简介 CycleGAN(Cycle Generative Adversarial Network) 即循环对抗生成网络&#xff0c;来自论文 U…

【Java】—— 集合框架:Collection子接口:Set不同实现类的对比及使用(HashSet、LinkedHashSet、TreeSet)

目录 5. Collection子接口2&#xff1a;Set 5.1 Set接口概述 5.2 Set主要实现类&#xff1a;HashSet 5.2.1 HashSet概述 5.2.2 HashSet中添加元素的过程&#xff1a; 5.2.3 重写 hashCode() 方法的基本原则 5.2.4 重写equals()方法的基本原则 5.2.5 练习 5.3 Set实现类…

map部分重点

1.map的方括号 给key,返回value的引用&#xff0c;如果没有key&#xff0c;就插入一个key,无参构造的value的pair<> 适用&#xff1a;没有就插入&#xff0c;有就拿找到的值 insert返回pair<iterator,bool>,[]返回值 #include<iostream> #include<map&…

更美观的HTTP性能监测工具:httpstat

reorx/httpstat是一个旨在提供更美观和详细HTTP请求统计信息的cURL命令行工具&#xff0c;它能够帮助开发者和运维人员深入理解HTTP请求的性能和状态。 1. 基本概述 项目地址&#xff1a;https://github.com/reorx/httpstat语言&#xff1a;该工具主要是以Python编写&#xff…

偏标记学习+图像分类(论文复现)

偏标记学习图像分类&#xff08;论文复现&#xff09; 本文所涉及所有资源均在传知代码平台可获取 文章目录 偏标记学习图像分类&#xff08;论文复现&#xff09;概述算法原理核心逻辑效果演示使用方式 概述 本文复现论文提出的偏标记学习方法&#xff0c;随着深度神经网络的发…

异常场景分析

优质博文&#xff1a;IT-BLOG-CN 为了防止黑客从前台异常信息&#xff0c;对系统进行攻击。同时&#xff0c;为了提高用户体验&#xff0c;我们都会都抛出的异常进行拦截处理。 一、异常处理类 Java把异常当做是破坏正常流程的一个事件&#xff0c;当事件发生后&#xff0c;…

CMU 10423 Generative AI:lec16(Mixture of Experts 混合专家模型)

关于MoE推荐博客&#xff1a; https://huggingface.co/blog/zh/moehttps://www.paddlepaddle.org.cn/documentation/docs/zh/guides/06_distributed_training/moe_cn.html 1 概述 这个文档是关于Mixture of Experts (MoE) 的介绍和实现&#xff0c;主要内容如下&#xff1a;…

virtualbox配置为NAT模式后物理机和虚拟机互通

virtualbox配置为 NAT模式后&#xff0c;虚拟机分配到的 IP地址一般是 10.xx网段的&#xff0c;虚拟机可以通过网络地址转换访问物理机所在的网络&#xff0c;但若不做任何配置&#xff0c;则物理机无法直接访问虚拟机。 virtualbox在提供 NAT配置模式时&#xff0c;也提供了端…

深度学习:CycleGAN图像风格迁移转换

基础概念 CycleGAN是一种GAN的变体&#xff0c;它被设计用来在没有成对训练数据的情况下学习两种不同域之间的图像到图像的转换&#xff0c;不需要同一场景或物体在两个不同域中的对应图像。 CycleGAN由Jun-Yan Zhu等人在2017年提出。 CycleGAN的模型架构主要由两组生成器和…

mac配置python出现DataDirError: Valid PROJ data directory not found错误的解决

最近在利用python下载SWOT数据时出现以下的问题&#xff1a; import xarray as xr import s3fs import cartopy.crs as ccrs from matplotlib import pyplot as plt import earthaccess from earthaccess import Auth, DataCollections, DataGranules, Store import os os.env…

CSS3--美开二度

免责声明&#xff1a;本文仅做分享&#xff01; 目录 定位 相对定位 绝对定位 定位居中 固定定位 堆叠层级 z-index 定位-小结 CSS 精灵 京东案例 字体图标 下载字体 使用字体 上传矢量图 CSS 修饰属性 垂直对齐方式 vertical-align 过渡 transition 透明度 opa…

【西门子V20变频器】 变频器运行时报A922报警

报警说明 原因&#xff1a; 1.变频器未接负载 2.变频器设定的电机参数与实际电机不匹配 3.查看P2179查看 无负载监控 设定的电流极限值&#xff0c;出厂默认为“3.0”

mysql事务 -- 事务的隔离性(测试实验+介绍,脏读,不可重复读,可重复度读,幻读)

目录 事务的隔离性 引入 测试 读未提交 脏读 读提交 不可重复读 属于问题吗? 例子 可重复读 幻读 串行化 原理 总结 事务的隔离性 引入 当我们让两个客户端共同执行begin语句时,就开始了两个事务并发访问 在这个过程中,可能会出现sql交叉的问题 但我们不希望因为…

项目定位与服务器(SERVER)模块划分

目录 定位 HTTP协议以及HTTP服务器 高并发服务器 单Reactor单线程 单Reactor多线程 多Reactor多线程 模块划分 SERVER模块划分 Buffer 模块 Socket模块 Channel 模块 Connection模块 Acceptor模块 TimerQueue模块 Poller模块 EventLoop模块 TcpServer模块 SE…

【ADC】噪声(1)噪声分类

概述 本文学习于TI 高精度实验室课程&#xff0c;总结 ADC 的噪声分类&#xff0c;并简要介绍量化噪声和热噪声。 文章目录 概述一、ADC 中的噪声类型二、量化噪声三、热噪声四、量化噪声与热噪声对比 一、ADC 中的噪声类型 ADC 固有噪声由两部分组成&#xff1a;第一部分是量…

【树莓派系列】树莓派wiringPi库详解,官方外设开发

树莓派wiringPi库详解&#xff0c;官方外设开发 文章目录 树莓派wiringPi库详解&#xff0c;官方外设开发一、安装wiringPi库二、wiringPi库API大全1.硬件初始化函数2.通用GPIO控制函数3.时间控制函数4.串口通信串口API串口通信配置多串口通信配置串口自发自收测试串口间通信测…

Django 后端数据传给前端

Step 1 创建一个数据库 Step 2 在Django中点击数据库连接 Step 3 连接成功 Step 4 settings中找DATABASES Step 5 将数据库挂上面 将数据库引擎和数据库名改成自己的 Step 6 在_init_.py中加上数据库的支持语句 import pymysql pymysql.install_as_MySQLdb() Step7 简单创建两…

以企业的视角进行大学生招聘

课程来源&#xff1a;中国计算机学会---朱颖韶&#xff08;资深人力资源领域--HR&#xff09; 一、招聘流程 1.简历->门槛 注重&#xff1a;专业学历、行业经验 2.笔试面试->专业知识与技能 3.简历面试-> 过往的成果 4.面试 沟通能力、学习力-----了解动机、价值观…

Pikachu-Sql Inject-insert/update/delete注入

insert 注入 插入语句 insert into tables values(value1,value2,value3); 如&#xff1a;插入用户表 insert into users (id,name,password) values (id,username,password); 当点击注册 先判断是否有SQL注入漏洞&#xff0c;经过判断之后发现存在SQL漏洞。构造insert的pa…

8644 堆排序

### 思路 堆排序是一种基于堆数据结构的排序算法。堆是一种完全二叉树&#xff0c;分为最大堆和最小堆。堆排序的基本思想是将待排序数组构造成一个最大堆&#xff0c;然后依次将堆顶元素与末尾元素交换&#xff0c;并调整堆结构&#xff0c;直到排序完成。 ### 伪代码 1. 读取…