算法题(回溯)

一、题目

1、全排列(LC 46)

2、子集(LC 78)

二、题解

1、全排列(LC 46)

(1)分析

使用回溯的方式解决。按排列的位置依次填充元素,并通过标记数组避免元素重复使用。定义用于存储单个排列结果的固定长度列表 path,存储所有排列结果的二维集合 ans,以及标记数组元素是否被使用的 onPath 数组。递归时考虑边界条件,当 i 等于数组长度时,说明已完成一个完整排列的构造,将当前排列存入结果集并终止当前递归。

在递归过程中,通过循环遍历所有数组元素,若元素未被标记使用,则将其放入当前排列的对应位置,标记为已使用后递归处理下一个位置;递归返回后,将元素的使用标记重置为未使用状态,完成回溯恢复现场,保证后续可以正常使用该元素,最终生成所有不重复的全排列。

时间复杂度:O(n * n!),其中 n 为 nums 的长度。

空间复杂度:O(n)。返回值的空间不计入。

(2)解答
java 复制代码
class Solution {
    public List<List<Integer>> permute(int[] nums) {
        //初始化
        int n = nums.length;
        List<Integer> path = Arrays.asList(new Integer[n]); //存放每个排列的列表
        List<List<Integer>> ans = new ArrayList<>();        //存放所有排列
        boolean[] onPath = new boolean[n];                  //标记数组中的元素是否可选
        dfs(0, nums, ans, onPath, path);          
        return ans;
    }

    public void dfs(int i, int[] nums, List<List<Integer>> ans, boolean[] onPath ,List<Integer> path){
        if( i == nums.length){                       //边界条件,i超过了数组的最大下标
            ans.add(new ArrayList<>(path));          //把这次所得的排列保存在结果中
            return;
        }

        for(int j = 0; j < nums.length; j++){
            if(!onPath[j]){
                path.set(i,nums[j]);         //当前元素可以选择时,放在列表中
                onPath[j] = true;            //标记这个元素已经被选择了
                dfs(i + 1, nums, ans, onPath ,path); //考虑下一个元素
                onPath[j] = false;                   //恢复现场,否则只能生成一个排列
            }
        }
    }
}

2、子集(LC 78)

(1)分析

基于回溯算法的"选或不选"思想解决。对于数组中的每一个元素,都存在加入当前子集和不加入当前子集两种选择,长度为n的数组最终会生成2ⁿ个子集。

递归函数以 i 作为当前处理的数组元素下标,当 i 等于数组长度时,代表所有元素的选择决策已完成,将当前子集复制后存入结果集。递归过程分为两个分支:不选择当前元素时,直接递归处理下一个元素;选择当前元素时,将元素添加到子集后递归处理下一个元素,递归返回后移除最后添加的元素,完成回溯恢复现场,确保两个选择分支互不干扰,最终生成数组的全部子集。

时间复杂度:O(n * )

空间复杂度:空间复杂度:O(n)。返回值的空间不计入。

(2)解答
java 复制代码
class Solution {
    public List<List<Integer>> subsets(int[] nums) {
        //初始化
        List<List<Integer>> ans = new ArrayList<>();  //存放所有子集
        List<Integer> path = new ArrayList<>();       //存放一个子集
        dfs(0, nums, path, ans);
        return ans;
        
    }
    public void dfs(int i, int[] nums, List<Integer> path, List<List<Integer>> ans){
        if( i == nums.length){
            ans.add(new ArrayList<>(path));      //边界条件,i超出数字下标
            return;
        }

        //不选
        dfs(i+1, nums, path, ans);           //不选择当前元素,不加入子集中

        //选
        path.add(nums[i]);                  //选择当前元素,加入子集中
        dfs(i+1, nums, path, ans);

        path.removeLast();               //恢复现场
    }
}
相关推荐
JieE21221 小时前
LeetCode 56. 合并区间|超清晰 JS 图解思路,面试高频区间题
javascript·算法·面试
Jack201 天前
HarmonyOS开发中错误处理策略:网络异常统一处理
算法
小小杨树1 天前
读懂色彩:拍照调色不再难
算法·计算机视觉·配色
JieE2122 天前
LeetCode 226. 翻转二叉树|JS 递归超详细拆解,二叉树入门经典题
javascript·算法
JieE2122 天前
LeetCode 104. 二叉树的最大深度|递归思路超详细拆解
javascript·算法
vivo互联网技术2 天前
CVPR 2026 | 全新强化学习框架 BeautyGRPO:重塑真实人像
算法·大模型·cvpr·影像
Darling噜啦啦2 天前
列表转树算法深度解析:从 Map 到 Reduce 的两种实现,面试高频考点
数据结构·算法·面试
用户497863050732 天前
(一)小红的数组操作
算法·编程语言
怕浪猫2 天前
Electron 系列文章封面图
算法·架构·前端框架