回溯算法-全排列

Java实现全排列算法
该文章提供了一个Java程序,使用回溯法解决力扣上的全排列问题。程序通过递归遍历数组,避免重复,找到给定序列的所有可能排列,并将结果存储在列表中返回。

全排列

力扣题目链接(opens new window)

给定一个 没有重复 数字的序列,返回其所有可能的全排列。

示例:

  • 输入: [1,2,3]
  • 输出: [ [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1] ]
package com.company;

import java.util.*;

class Solution {
    private List<Integer> path = new ArrayList<>(); // 创建一个空的列表来存储当前子序列
    private List<List<Integer>> res = new ArrayList<>(); // 创建一个空的列表来存储所有递增子序列

    public List<List<Integer>> findSubsequences(int num[]) { // 寻找递增子序列的主函数,参数为整型数组
        int used[] = new int[10000]; // 创建一个数组来标记元素是否被使用过
        backtracking(num, used); // 调用回溯函数
        return res; // 返回所有递增子序列的列表
    }

    private void backtracking(int[] num, int used[]) { // 回溯函数,参数为整型数组和标记数组
        if (path.size() >= num.length) { // 如果当前子序列长度已经等于数组长度,则将该子序列加入结果列表中,并返回
            res.add(new ArrayList<>(path));
            return;
        }

        for (int i = 0; i < num.length; i++) { // 遍历整个数组
            if (used[i] == 1) continue; // 如果该元素已经被使用过,则跳过该元素
            used[i] = 1; // 标记该元素已被使用过
            path.add(num[i]); // 将该元素加入当前子序列中
            backtracking(num, used); // 递归调用回溯函数,继续向下寻找
            path.remove(path.size() - 1); // 回溯,将该元素从当前子序列中移除
            used[i] = 0; // 将该元素标记为未使用过
        }
    }

    public static void main(String[] args) { // 主函数
        Solution solution = new Solution(); // 创建Solution对象
        int[] nums = {1,2,3}; // 初始化整型数组
        List<List<Integer>> result = solution.findSubsequences(nums); // 调用寻找递增子序列的函数,并将结果赋值给result
        System.out.println(result); // 输出结果
    }
}

全排列问题是回溯算法的经典应用场景之一。该问题要求生成一个给定数组的所有可能排列组合。实现的核心思想是通过递归尝试所有可能的选择,并在发现当前路径无法形成有效解时进行“回退”,即回溯到上一步重新选择。 ### 全排列回溯实现步骤 全排列回溯算法通常包括以下几个关键步骤: - **初始化**:定义一个结果数组用于存储最终的排列组合,同时定义一个递归函数来执行回溯逻辑。 - **递归终止条件**:当当前路径(path)的长度等于原始数组(nums)的长度时,说明已经完成了一个完整的排列,此时将该排列加入结果数组。 - **遍历与选择**:在每一层递归中,都需要遍历原始数组中的所有元素。对于每一个元素,如果它尚未被选中,则将其标记为已选中,并将其添加到当前路径中。 - **回溯与释放选择**:递归调用完成后,需要将当前元素标记为未选中,以便在下一次循环中使用该元素进行其他排列的构造。 ### 去重机制 由于全排列不允许重复元素出现在同一个排列中,因此需要引入额外的状态数组或哈希结构来记录哪些元素已经被选中[^2]。这样可以确保在构建排列的过程中不会出现重复元素的情况。 ### 示例代码 以下是一个使用 JavaScript 实现的全排列算法示例,展示了如何通过回溯法解决全排列问题: ```javascript var permute = function(nums) { let res = []; const dfs = (path, used) => { if (path.length === nums.length) { res.push([...path]); return; } for (let i = 0; i < nums.length; i++) { if (!used[i]) { used[i] = true; path.push(nums[i]); dfs(path, used); path.pop(); used[i] = false; } } }; dfs([], new Array(nums.length).fill(false)); return res; }; ``` 上述代码中,`used` 数组用于记录每个元素是否已被选中,从而避免在同一排列中重复使用相同的元素。每次递归调用 `dfs` 函数时,都会检查当前元素是否可用,并在递归返回后恢复其状态,以允许后续排列使用该元素。 ### 算法优化策略 为了提高回溯算法的效率,可以在递归过程中加入剪枝操作,例如提前判断某些不可能产生有效解的分支并跳过这些分支的搜索。此外,还可以结合其他算法思想,如贪心策略或动态规划,进一步提升算法性能。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值