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

首先排列是有序的,也就是说 [1,2] 和 [2,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 = []

递归 backtracking ([1,2,3],[false, 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)

递归 backtracking ([1,2,3],[true, 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)

递归 backtracking ([1,2,3],[true, 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) 

递归 backtracking ([1,2,3],[true, 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 → 这个递归结束,返回到上一个递归

返回backtracking ([1,2,3],[true, 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不满足)
这个递归结束

.........

.........

.........

相关推荐
yashuk2 分钟前
C语言入门教程:程序结构与算法举例
c语言·算法·教程·程序设计·开发过程
zsc_1186 分钟前
pvz3解码小游戏求解算法 (二)
算法
hanbr14 分钟前
每日一题day1(Leetcode 76最小覆盖子串)
算法·leetcode
AI科技星15 分钟前
张祥前统一场论中两个电荷定义的统一性解析
开发语言·线性代数·算法·数学建模·平面
代码地平线16 分钟前
C语言实现堆与堆排序详解:从零手写到TopK算法及时间复杂度证明
c语言·开发语言·算法
小江的记录本16 分钟前
【大语言模型】大语言模型——核心概念(预训练、SFT监督微调、RLHF/RLAIF对齐、Token、Embedding、上下文窗口)
java·人工智能·后端·python·算法·语言模型·自然语言处理
炘爚18 分钟前
LeetCode(两两交换链表中的节点)
算法·leetcode·链表
wsoz18 分钟前
Leetcode矩阵-day7
c++·算法·leetcode·矩阵
念越18 分钟前
算法每日一题 Day01|双指针解决移动零问题
java·算法·力扣
不想看见40420 分钟前
Merge k Sorted Lists 优先队列--力扣101算法题解笔记
笔记·算法·leetcode