算法题(回溯)

一、题目

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();               //恢复现场
    }
}
相关推荐
一去不复返的通信er4 小时前
力扣-图论-岛屿数量
算法·leetcode·图论
程序 代码狂人7 小时前
切片表与分区表的区别(数据开发面试版)
java·大数据·开发语言·数据仓库·算法
HDU-Dade7 小时前
【校招笔试真题】简要介绍 Self-Attention 的前沿算法
人工智能·算法
小郎君6589 小时前
Vue keep-alive 切标签后滚动位置回顶?因为 onDeactivated 里读到的 scrollTop 是 0
算法
邵奈一10 小时前
GPT-6.1 Terra速度是Sol的3倍,价格减半
算法
RaaS10010 小时前
Node.js Brotli RCE 漏洞自查指南:fetch、undici、axios 都在影响面,三分钟确认你的版本
人工智能·深度学习·算法
shehuiyuelaiyuehao10 小时前
算法56,重排链表
数据结构·算法·链表
qq_4523962311 小时前
第四篇:《索引原理与实战:B-tree、Hash、GIN、GiST、BRIN》
算法·哈希算法·gin
西索斯coding11 小时前
Qwen3-Max 与 Qwen-Plus:60 个真实修复任务评测方案
大数据·网络·算法·ai