• 力扣热门算法题 46-48


    46. 全排列,47. 全排列 II,48. 旋转图像,每题做详细思路梳理,配套Python&Java双语代码, 2024.03.19 可通过leetcode所有测试用例

    目录

    46. 全排列

    解题思路

    完整代码

    Python

    Java

    47. 全排列 II

    解题思路

    完整代码

    Python

    ​编辑

    Java

    48. 旋转图像

    解题思路

    完整代码

    Python

    Java


    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 中的所有整数 互不相同

    解题思路

            我们可以定义一个辅助函数来实现回溯。这个函数将维护一个当前的排列path和一个记录哪些数字已被使用的used数组。每次递归时,我们遍历nums数组中的每个数字,如果这个数字尚未被使用,我们就把它添加到当前排列中,并标记为已使用,然后递归调用辅助函数。递归结束后,我们需要撤销当前数字的使用状态,以便进行下一次循环的尝试。

    步骤如下:

    1. 定义结果列表res来存储所有可能的全排列。
    2. 定义辅助函数backtrack,它接受当前路径path和使用状态数组used
    3. backtrack函数中,如果path的长度等于nums的长度,说明找到了一个全排列,将其添加到res中。
    4. 遍历nums数组,如果当前元素未被使用,则将其添加到path中,并标记为已使用,然后递归调用backtrack。递归返回后,撤销当前元素的使用状态和path中的元素,以进行下一轮尝试。
    5. 调用backtrack函数开始回溯过程,并返回res作为所有可能的全排列。

    完整代码

    Python
    1. class Solution:
    2. def permute(self, nums: List[int]) -> List[List[int]]:
    3. def backtrack(path, used):
    4. # 如果当前路径的长度等于 nums 的长度,说明找到了一个全排列
    5. if len(path) == len(nums):
    6. res.append(path[:]) # 深拷贝当前路径到结果列表
    7. return
    8. for i in range(len(nums)):
    9. if not used[i]: # 检查 nums[i] 是否已在当前路径中
    10. path.append(nums[i]) # 添加到路径
    11. used[i] = True # 标记为已使用
    12. backtrack(path, used) # 递归
    13. path.pop() # 回溯,移除路径最后一个元素
    14. used[i] = False # 回溯,标记为未使用
    15. res = [] # 存储所有可能的全排列
    16. backtrack([], [False] * len(nums)) # 初始化路径和使用状态数组
    17. return res
    Java
    1. public class Solution {
    2. public List<List<Integer>> permute(int[] nums) {
    3. List<List<Integer>> res = new ArrayList<>();
    4. backtrack(res, nums, new ArrayList<>(), new boolean[nums.length]);
    5. return res;
    6. }
    7. private void backtrack(List<List<Integer>> res, int[] nums, List path, boolean[] used) {
    8. if (path.size() == nums.length) {
    9. res.add(new ArrayList<>(path)); // 添加一种全排列到结果列表
    10. return;
    11. }
    12. for (int i = 0; i < nums.length; i++) {
    13. if (!used[i]) {
    14. path.add(nums[i]); // 添加到路径
    15. used[i] = true; // 标记为已使用
    16. backtrack(res, nums, path, used); // 递归
    17. path.remove(path.size() - 1); // 回溯,移除路径最后一个元素
    18. used[i] = false; // 回溯,标记为未使用
    19. }
    20. }
    21. }
    22. }

    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

    解题思路

    解决包含重复数字序列的全排列问题,核心思路仍然是使用回溯算法,但需要额外注意如何避免生成重复的排列。为此,我们可以采取以下策略:

    1. 排序:首先将数组排序,这样可以让相同的数字聚在一起,便于后续步骤中判断重复。
    2. 选择路径:在遍历每个数字时,如果当前数字与前一个数字相同,并且前一个数字未被使用(说明在同一层递归中),则跳过当前数字,以避免产生重复的排列。
    3. 回溯:与之前相同,通过递归调用来探索所有可能的路径,每次调用都尝试添加一个尚未被选择的数字。
    4. 终止条件:当当前排列的长度等于原数组长度时,说明找到了一个全排列,将其添加到结果集中。

    完整代码

    Python
    1. class Solution:
    2. def permuteUnique(self, nums: List[int]) -> List[List[int]]:
    3. res = []
    4. nums.sort() # 排序,让相同的数字聚在一起
    5. def backtrack(path, used):
    6. if len(path) == len(nums):
    7. res.append(path[:])
    8. return
    9. for i in range(len(nums)):
    10. # 如果当前数字已被使用,或者与前一个数字相同且前一个数字未被使用,则跳过
    11. if used[i] or (i > 0 and nums[i] == nums[i-1] and not used[i-1]):
    12. continue
    13. used[i] = True
    14. path.append(nums[i])
    15. backtrack(path, used)
    16. used[i] = False
    17. path.pop()
    18. backtrack([], [False] * len(nums))
    19. return res
    Java
    1. public class Solution {
    2. public List<List<Integer>> permuteUnique(int[] nums) {
    3. List<List<Integer>> res = new ArrayList<>();
    4. Arrays.sort(nums); // 排序
    5. backtrack(res, nums, new ArrayList<>(), new boolean[nums.length]);
    6. return res;
    7. }
    8. private void backtrack(List<List<Integer>> res, int[] nums, List path, boolean[] used) {
    9. if (path.size() == nums.length) {
    10. res.add(new ArrayList<>(path));
    11. return;
    12. }
    13. for (int i = 0; i < nums.length; i++) {
    14. if (used[i] || (i > 0 && nums[i] == nums[i - 1] && !used[i - 1])) {
    15. continue;
    16. }
    17. used[i] = true;
    18. path.add(nums[i]);
    19. backtrack(res, nums, path, used);
    20. used[i] = false;
    21. path.remove(path.size() - 1);
    22. }
    23. }
    24. }


    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]]

    解题思路

    将图像顺时针旋转90度,可以通过以下两步完成:

    1. 转置矩阵:转置指的是将矩阵的行变成列,即matrix[i][j]变成matrix[j][i]
    2. 翻转每一行:将每一行的元素翻转,即第一个元素和最后一个元素交换,第二个元素和倒数第二个元素交换,以此类推。

    详细步骤:

    • 对于矩阵的每一行,我们使用一个循环交换元素,即matrix[i][j]matrix[j][i]进行交换,这样可以得到矩阵的转置。
    • 转置之后,我们对每一行进行翻转。可以通过双指针的方法来实现,即一个指针指向行的开始,另一个指针指向行的结束,交换这两个指针指向的元素,然后移动指针直到它们相遇。

    完整代码

    Python
    1. class Solution:
    2. def rotate(self, matrix: List[List[int]]) -> None:
    3. n = len(matrix)
    4. # 转置矩阵
    5. for i in range(n):
    6. for j in range(i, n):
    7. matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j]
    8. # 翻转每一行
    9. for i in range(n):
    10. matrix[i].reverse()
    Java
    1. public class Solution {
    2. public void rotate(int[][] matrix) {
    3. int n = matrix.length;
    4. // 转置矩阵
    5. for (int i = 0; i < n; i++) {
    6. for (int j = i; j < n; j++) {
    7. int temp = matrix[i][j];
    8. matrix[i][j] = matrix[j][i];
    9. matrix[j][i] = temp;
    10. }
    11. }
    12. // 翻转每一行
    13. for (int i = 0; i < n; i++) {
    14. for (int j = 0, k = n - 1; j < k; j++, k--) {
    15. int temp = matrix[i][j];
    16. matrix[i][j] = matrix[i][k];
    17. matrix[i][k] = temp;
    18. }
    19. }
    20. }
    21. }

  • 相关阅读:
    windows设置右键打开 vscode的方法(简易版)
    「数据结构详解·二」二叉树的初步
    RabbitMQ: return机制
    多域名SSL证书的优势
    【擎标】CCID信息系统服务商交付能力等级认证标准
    极值点偏移练习1
    kafka消息重复消费解决方案
    QT中怎么设置定时器/周期任务/定时触发任务
    UE4和C++ 开发-C++与UMG的交互2(C++获取UMG的属性)
    手把手带你搭建个人博客系统(一)
  • 原文地址:https://blog.csdn.net/qq_52213943/article/details/136813485