算法题(回溯)

一、题目

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();               //恢复现场
    }
}
相关推荐
可编程芯片开发9 小时前
基于PI控制算法的pwm直流电机控制系统Simulink建模与仿真
算法
怕浪猫9 小时前
2840亿参数只卖白菜价:DeepSeek V4 Flash 正式版上线,Agent 能力暴涨6倍
人工智能·算法
让学习成为一种生活方式9 小时前
苄基异喹啉生物碱糖基转移酶UGT74AN1晶体--Journal of Agricultural and Food Chemistry
人工智能·算法
alphaTao9 小时前
LeetCode 每日一题 2026/7/27-2026/8/2
python·算法·leetcode
2501_9269783310 小时前
提示工程的实战报告(二):模型的失败模式与边界行为
人工智能·深度学习·算法
小小晓.10 小时前
C++记:函数
开发语言·c++·算法
casual~10 小时前
模逆元计算方法详解:扩展欧几里得算法与费马小定理
学习·算法·逆元
脚踏实地皮皮晨11 小时前
002002002_DepandencyObject类2
开发语言·windows·算法·c#·visual studio
冻柠檬飞冰走茶12 小时前
PTA基础编程题目集 7-27 冒泡法排序(C语言实现)
c语言·开发语言·数据结构·算法
大鱼>13 小时前
因子挖掘+回测+RL交易:量化金融完整系统
人工智能·深度学习·算法·金融