leetcode回溯算法(46.全排列)

首先排列是有序的,也就是说 1,22,1 是两个集合,这和之前分析的子集以及组合所不同的地方

可以看出元素1在1,2中已经使用过了,但是在2,1中还要在使用一次1,所以处理排列问题就不用使用startIndex了。

但排列问题需要一个used数组,标记已经选择的元素,如图橘黄色部分所示:

可以看出叶子节点,就是收割结果的地方。

那么什么时候,算是到达叶子节点呢?

当收集元素的数组path的大小达到和nums数组一样大的时候,说明找到了一个全排列,也表示到达了叶子节点。

cpp 复制代码
if (path.size() == nums.size()) {
    result.push_back(path);
    return;
}

本题的for循环里不用startIndex了。

因为排列问题,每次都要从头开始搜索,例如元素1在1,2中已经使用过了,但是在2,1中还要再使用一次1。

而used数组,其实就是记录此时path里都有哪些元素使用了,一个排列里一个元素只能使用一次

cpp 复制代码
class Solution {
public:
    vector<vector<int>> result; // 存储所有排列结果的二维数组
    vector<int> path; // 存储当前正在构建的排列(路径)
    
    // 回溯函数
    // nums: 原始输入数组
    // used: 标记数组元素是否已被使用的布尔数组
    void backtracking (vector<int>& nums, vector<bool>& used) {
        // 终止条件:当前路径长度等于原始数组长度
        // 说明已经构造出一个完整的排列
        if (path.size() == nums.size()) {
            result.push_back(path); // 将当前排列加入结果集
            return; // 返回上一层递归
        }
        
        // 遍历所有可能的元素选择
        for (int i = 0; i < nums.size(); i++) {
            // 跳过已使用的元素(避免重复选择)
            if (used[i] == true) continue;
            
            // 做出选择
            used[i] = true; // 标记当前元素为已使用
            path.push_back(nums[i]); // 将当前元素加入路径
            
            // 递归进入下一层决策树
            backtracking(nums, used);
            
            // 撤销选择(回溯)
            path.pop_back(); // 从路径中移除最后一个元素
            used[i] = false; // 标记当前元素为未使用
        }
    }
    
    // 主函数:生成全排列
    vector<vector<int>> permute(vector<int>& nums) {
        result.clear(); // 清空结果集(防止多次调用时数据残留)
        path.clear(); // 清空当前路径
        vector<bool> used(nums.size(), false); // 初始化used数组,所有元素初始未使用
        backtracking(nums, used); // 开始回溯
        return result; // 返回所有排列结果
    }
};

nums = [1,2,3] 为例,详细分析代码执行过程

初始状态

复制代码
nums = [1,2,3]
used = [false, false, false]
path = []
result = []

递归 backtracking1,2,3false, false, false)

复制代码
if判断,path = [] (长度0 < 3,继续执行
cpp 复制代码
for (int i = 0; i < nums.size(); i++) {
    if (used[i] == true) continue; // path里已经收录的元素,直接跳过
    used[i] = true;
    path.push_back(nums[i]);
    backtracking(nums, used);
    path.pop_back();
    used[i] = false;
}
复制代码
开始for循环,i=0
if (used[i] == true) → 因为used[0]=false,所以不执行if语句
used[0]=true,used=[True,F,F]
path.push_back(nums[0]) → path.push_back(1),path变为[1]
调用backtracking(nums, used)

递归 backtracking1,2,3true, false, false)

复制代码
path=[1], used=[T,F,F]
if (path.size() == nums.size()) → 1 == 3 ❌,所以不执行if语句
for 开始循环  , i=0:
if (used[i] == true) → used[0]==true ✅,执行continue,跳到下一个i

i++,现在i=1:
if (used[i] == true) → used[1]==false ❌,所以不执行if语句
used[1]=true,used变为[T,T,F]
path.push_back(nums[0]) → path.push_back(2),path变为[1,2]
backtracking(nums, used)

递归 backtracking1,2,3true, true, false)

复制代码
path=[1,2], used=[T,T,F]
if (path.size() == nums.size()) → 2 == 3 ❌,不执行if语句
for开始循环 , i=0:
if (used[i] == true) → used[0]==true ✅,continue

i++,i=1:
第13行: if (used[i] == true) → used[1]==true ✅,continue

循环 i=2:
if (used[i] == true) → used[2]==false ❌,不执行if语句
used[i] = true → used[2]=true,used变为[T,T,T]
path.push_back(nums[2]) → path.push_back(3),path变为[1,2,3]
backtracking(nums, used) 

递归 backtracking1,2,3true, true, true)

复制代码
path=[1,2,3], used=[T,T,T]
if (path.size() == nums.size()) → 3 == 3 ✅,进入if
执行if语句,result.push_back(path) → result为[1,2,3]
return → 这个递归结束,返回到上一个递归

返回backtracking1,2,3true, true, false)

复制代码
之前path=[1,2,3], used=[T,T,T]
执行回溯
path.pop_back() → path变为[1,2]
used[i] = false → used[2]=false,used变为[T,T,F]
i++,i=3 ,for循环结束 (i从2增加到3,循环条件i<3不满足)
这个递归结束

.........

.........

.........

相关推荐
枕星而眠21 小时前
【数据结构】红黑树入门指南
运维·数据结构·c++·后端
Keven_111 天前
算法札记:如何卡SPFA
算法·spfa
可编程芯片开发1 天前
基于电压电流双闭环控制的三相整流器系统simulink建模与仿真
算法
可编程芯片开发1 天前
基于ADRC自抗扰算法的UAV飞行姿态控制系统simulink建模与仿真
算法
学究天人1 天前
数学公理体系大全:第七章 连续统假设与力迫法简介
人工智能·算法·机器学习·数学建模·动态规划·图论·抽象代数
Keven_111 天前
算法札记:SPFA什么时候用队列什么时候用栈
算法·spfa
:-)1 天前
基础算法-选择排序
数据结构·算法·排序算法
浮沉9871 天前
二分查找算法例题(一)
算法
退休倒计时1 天前
【每日一题】LeetCode 17. 电话号码的字母组合 TypeScript
算法·leetcode·typescript
粘稠的浆糊1 天前
[AtCoder - abc465_d ]X to Y题解
数据结构·c++·算法