LeetCode 热题 第46题 全排列

LeetCode 热题 第46题 全排列

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

解题:

#include <vector>
using namespace std;

class Solution {
public:
    vector<vector<int>> permute(vector<int>& nums) {
        vector<vector<int>> result;
        backtrack(nums, 0, result);
        return result;
    }

private:
    void backtrack(vector<int>& nums, int start, vector<vector<int>>& result) {
        if (start == nums.size()) {
            result.push_back(nums);
            return;
        }
        
        for (int i = start; i < nums.size(); ++i) {
            // 交换当前元素与起始位置元素
            swap(nums[start], nums[i]);
            // 递归调用,填充下一个数
            backtrack(nums, start + 1, result);
            // 回溯,撤销选择
            swap(nums[start], nums[i]);
        }
    }
};

关键点

  • starti 的作用start 表示当前正在处理的数组索引位置,而 i 是一个循环变量,它从 start 开始遍历到数组的末尾。这意呀着对于每个 starti 不仅仅等于 start,还会取 start 之后的所有值。
  • 为什么交换?:交换 nums[start]nums[i] 的目的是将 i 位置上的元素固定到 start 位置,然后递归地对剩余部分进行排列。当 i == start 时,实际上没有发生实际的交换,因为是同一个位置的元素互换;但这并不影响算法的正确性,因为它确保了每个元素都有机会被放置在 start 位置。
  • 回溯:每次递归调用后,我们再次交换 nums[start]nums[i] 来恢复数组到之前的状态,这样可以尝试其他可能性。这个过程称为回溯。

实现过程

初始状态

  • nums = [1, 2, 3]
  • start = 0

第一层递归(start = 0)

第一次调用 (i = 0)
  • 交换swap(nums[0], nums[0]) -> nums = [1, 2, 3] (无变化)
  • 递归调用backtrack(nums, 1, result)
第二层递归(start = 1)
第一次调用 (i = 1)
  • 交换swap(nums[1], nums[1]) -> nums = [1, 2, 3] (无变化)
  • 递归调用backtrack(nums, 2, result)
第三层递归(start = 2)
  • 交换swap(nums[2], nums[2]) -> nums = [1, 2, 3] (无变化)
  • 保存排列result.push_back([1, 2, 3])
  • 回溯swap(nums[2], nums[2]) -> nums = [1, 2, 3] (无变化)
第二次调用 (i = 2)
  • 交换swap(nums[1], nums[2]) -> nums = [1, 3, 2]
  • 递归调用backtrack(nums, 2, result)
第三层递归(start = 2)
  • 交换swap(nums[2], nums[2]) -> nums = [1, 3, 2] (无变化)
  • 保存排列result.push_back([1, 3, 2])
  • 回溯swap(nums[2], nums[2]) -> nums = [1, 3, 2] (无变化)
  • 回溯swap(nums[1], nums[2]) -> nums = [1, 2, 3] (恢复原状)
第二次调用 (i = 1)
  • 交换swap(nums[0], nums[1]) -> nums = [2, 1, 3]
  • 递归调用backtrack(nums, 1, result)
第二层递归(start = 1)
第一次调用 (i = 1)
  • 交换swap(nums[1], nums[1]) -> nums = [2, 1, 3] (无变化)
  • 递归调用backtrack(nums, 2, result)
第三层递归(start = 2)
  • 交换swap(nums[2], nums[2]) -> nums = [2, 1, 3] (无变化)
  • 保存排列result.push_back([2, 1, 3])
  • 回溯swap(nums[2], nums[2]) -> nums = [2, 1, 3] (无变化)
第二次调用 (i = 2)
  • 交换swap(nums[1], nums[2]) -> nums = [2, 3, 1]
  • 递归调用backtrack(nums, 2, result)
第三层递归(start = 2)
  • 交换swap(nums[2], nums[2]) -> nums = [2, 3, 1] (无变化)
  • 保存排列result.push_back([2, 3, 1])
  • 回溯swap(nums[2], nums[2]) -> nums = [2, 3, 1] (无变化)
  • 回溯swap(nums[1], nums[2]) -> nums = [2, 1, 3] (恢复原状)
  • 回溯swap(nums[0], nums[1]) -> nums = [1, 2, 3] (恢复原状)
第三次调用 (i = 2)
  • 交换swap(nums[0], nums[2]) -> nums = [3, 2, 1]
  • 递归调用backtrack(nums, 1, result)
第二层递归(start = 1)
第一次调用 (i = 1)
  • 交换swap(nums[1], nums[1]) -> nums = [3, 2, 1] (无变化)
  • 递归调用backtrack(nums, 2, result)
第三层递归(start = 2)
  • 交换swap(nums[2], nums[2]) -> nums = [3, 2, 1] (无变化)
  • 保存排列result.push_back([3, 2, 1])
  • 回溯swap(nums[2], nums[2]) -> nums = [3, 2, 1] (无变化)
第二次调用 (i = 2)
  • 交换swap(nums[1], nums[2]) -> nums = [3, 1, 2]
  • 递归调用backtrack(nums, 2, result)
第三层递归(start = 2)
  • 交换swap(nums[2], nums[2]) -> nums = [3, 1, 2] (无变化)
  • 保存排列result.push_back([3, 1, 2])
  • 回溯swap(nums[2], nums[2]) -> nums = [3, 1, 2] (无变化)
  • 回溯swap(nums[1], nums[2]) -> nums = [3, 2, 1] (恢复原状)
  • 回溯swap(nums[0], nums[2]) -> nums = [1, 2, 3] (恢复原状)

最终结果

最终的结果将是所有可能的排列组合:

[
  [1, 2, 3],
  [1, 3, 2],
  [2, 1, 3],
  [2, 3, 1],
  [3, 1, 2],
  [3, 2, 1]
]

通过上述步骤,我们可以看到每个元素都有机会被放置在每一个位置上,并且在每次递归返回后,我们都恢复了数组到之前的状态,以确保可以探索所有可能的排列组合。

如果想不明白的话建议在草稿纸上一步一步实现程序实现过程

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值