• 递归算法 -- 解决全排列问题


    递归算法 – 解决全排列问题

    首先我们来看题目

    给定一个不含重复数字的整数数组 nums ,返回其 所有可能的全排列 。可以 按任意顺序 返回答案。

    示例 1:

    输入:nums = [1,2,3]
    输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
    

    让后我们来看给的代码样列

    class Solution {
        public List> permute(int[] nums) {
    
        }
    }
    

    分析

    从代码的样例我们可以看到最终我们是要返回一个嵌套集合 , 也就说一个大的集合包裹多个小的集合

    如何迭代?

    在这里插入图片描述

    1. 初始化
    • 成员变量:
      • List> res:用于存储所有可能的排列。
      • ArrayList solu:用于存储当前正在构建的排列。
      • boolean[] bool:一个布尔数组,用于跟踪哪些数字已经被使用(在原始代码中,这个变量被错误地声明了两次,一次在类级别,一次在permute方法内部,应只保留在方法内部的声明)。
    • 方法:
      • List> permute(int[] nums):主方法,接受一个整数数组nums,返回其所有排列的列表。
      • void dfs(int index, int[] nums):深度优先搜索(DFS)的递归函数,用于生成排列。index表示当前处理到nums数组的哪个位置。
    2. DFS逻辑
    • 基准情况:

      • index等于nums的长度时,表示已经成功构建了一个排列,将其添加到结果集res中,并返回。
    • 递归步骤:

      • 遍历

        nums
        

        数组中的每个元素(索引为

        i
        

        • 如果当前元素未被使用(

          !bool[i]
          

          ),则进行以下操作:

          • 将当前元素添加到solu中,表示选中了这个元素作为当前排列的一部分。
          • bool[i]设置为true,表示该元素已被使用。
          • 递归调用dfs(index + 1, nums),注意这里index递增,表示处理下一个位置。
          • 回溯:在递归返回后,将solu中的最后一个元素移除,并将bool[i]重置为false,以便尝试其他可能的排列。
    public class PaiLieShu {
        // 定义结果集合
        List> res = new ArrayList<>();
        //定义每一种结果
        ArrayList solu = new ArrayList<>();
        boolean[] bool;
    
        public List> permute(int[] nums) {
    
    
            // 定义判断数组--> 判断该数字是否以及被使用
            boolean[] bool = new boolean[nums.length];
            // 定义初始位置
            int index = 0;
            // 计算nums长度
            int len = nums.length;
            dfs(index , nums);
    
            return res;
        }
    
        // 定义迭代函数
        public void dfs(int index , int[] nums){
            /*
                这里的index代表着nums数组的索引 , 我们每加入一个数字index就会加一
                当index=索引长度时表示找到了一种结果
             */
            if ( index == nums.length){
                // 将结果加入到集合中
                res.add(new ArrayList(solu));
                return;
            }
    
            // 这里进行三次循环相当于创建树的过程
            for (int i = 0; i < nums.length; i++) {
                // 判断该位置的数字是否被使用过, 如果没有则循环
                if (!bool[i]){
                    // 假如当前i=0 , 在第一次循环中没被使用
                    // 那么接下来需要对nums[i]进行储存
                    solu.add(nums[i]);
                    // 将索引为i的位置设置为使用过了
                    bool[i] = true;
    
                    // 递归调用
                    dfs(index + 1 , nums);
    
                    // 回溯 , 删除最后一个数
                    solu.remove(solu.size() - 1);
                    bool[i] = false;
    
                }
            }
    
    
        }
    }
    

    为什么要回溯?

    看下图 , 我们来像一个问题, 如果不回溯会产生什么样的问题?

    很显然不回溯的情况下回答之我们定义的bool判断数组中全为true , 那么就算进入下一次迭代 , 由于全为true并不会执行实际的代码 , 从而导致最终结果只有[[1,2,3]]

    而进行回溯可以巧妙的解决这个问题使得每一次迭代都从树的根节点开始

    在这里插入图片描述

  • 相关阅读:
    Spring官宣网传大漏洞,并提供解决方案
    COCI 2021-2022 #1 - Volontiranje 题解
    volatile-日常使用场景
    C#学习 - 操作符
    如何将CAD导入GIS并且找到正确的投影坐标
    MySQL 定时备份的几种方式(非常全面)
    数仓工具—Hive语法之join 扩展(23)
    MySQL数据库管理
    DIcom调试Planar configuration
    外汇天眼:一步错步步错,投资者表示真后悔遇到DIFX杀猪盘
  • 原文地址:https://blog.csdn.net/xiugtt6141121/article/details/141093625