算法题(回溯)

一、题目

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();               //恢复现场
    }
}
相关推荐
-To be number.wan21 小时前
算法日记 | STL-MAP
c++·算法
cjp56021 小时前
015. UG 二次开发,拉伸草图生成实体类,高级草图类封装
算法
Eric 辰东21 小时前
【C 语言程序的编译和链接】详解编译链接过程
c语言·笔记·算法·学习方法
迈巴赫车主21 小时前
蓝桥杯21247弹跳鞋java
java·开发语言·数据结构·算法·职场和发展·蓝桥杯
jghhh0121 小时前
基于 Weiler-Atherton 算法的多边形裁剪程序实现
算法
不爱吃糖の糖糖21 小时前
RAG 04:向量数据库与索引算法
数据库·算法
MegaDataFlowers21 小时前
226.翻转二叉树
算法
alphaTao1 天前
LeetCode 每日一题 2026/5/25-2026/5/31
算法·leetcode
菜菜的顾清寒1 天前
力扣HOT100(41)动态规划-杨辉三角
算法·leetcode·动态规划
Cthy_hy1 天前
Python算法竞赛:集合去重+字典映射 核心用法一站式整理
数据结构·python·算法