回溯系列-全排列问题

一、题目描述

求1~n的全排列。

示例1:

输入:3
输出:
    (1,2,3)
    (1,3,2)
    (2,1,3)
    (2,3,1)
    (3,1,2)
    (3,2,1)

二、解题思路

​ 回溯方法的思想及模板参考回溯系列-算法思想与模板,根据此算法的模板我们就可以解决此题。

​ 对{1,…,n}的全排列个数进行计数是生成这些置换的必要前提.对于置换(a1,a2,…,an)的第一个元素值来说,有n种不同的选项.一旦定下了a1,由于我们除了a1之外其他任意值都可用(全排列中不允许重复),所以对第二个位置来说还剩n-1个候选者.重复这个论证方法可获得所有互不相同的置换,共计n!种.

​ 这种计数论证方案提出了一个合适的表达方式.我们建立一个具有n个单元的数组a作为解向量A.部分解中前i-1个元素对应着置换中的前i-1个元素,而第i个位置的候选者集合将会是没有出现在这i-1个元素中那些值.
​ 在前述一般性回溯算法方案中,可设:Ck为第k个位置候选集合,有:

C k = { 1 , . . . , n } − ⋃ i = 1 k − 1 { a i } C_k = \lbrace 1,...,n \rbrace - \bigcup_{i=1}^{k-1}\lbrace a_i \rbrace Ck={1,...,n}i=1k1{ai}

只要k=n时A(k)就是一个解.

三、代码实现

package com.design.backtrack;

/**
 * 全排列问题
 *
 *  @author hh
 *  @date 2021-5-30 19:58
 */
public class FullPermutation {

    /**
     * 最大候选者数量
     */
    private int maxCandidates;

    /**
     * 回溯主方法
     *
     * @param a 解向量
     * @param k a中当前元素位置k
     * @param input 输入数据
     */
    public void backtrack(int[] a,int k,int input){
        //保存下一位置候选集合
        int[] c = new int[input];
        //下一位位置候选者数目
        int nCandidates;
        //计算器
        int i;
        if(isSolution(a,k,input)){
            processSolution(a,k,input);
        }else{
            k = k + 1;
            nCandidates = constructCandidates(a,k,input,c);
            for(i = 0; i < nCandidates; i++){
                a[k-1] = c[i];
                makeMove(a,k,input);
                backtrack(a,k,input);
                unmakeMove(a,k,input);
            }
        }

    }

    /**
     * 判断向量a是否是一解
     *
     * @param a 向量a是否构成一解向量
     * @param k a中当前元素位置k
     * @param input 输入数据
     * @return 布尔变量
     */
    private boolean isSolution(int[] a,int k,int input){
        return k == input;
    }

    /**
     * 处理解向量a,打印或保存操作
     *
     * @param a 解向量
     * @param k a中当前元素位置k
     * @param input 输入数据
     */
    private void processSolution(int[] a,int k,int input){
        StringBuilder sb = new StringBuilder();
        sb.append("(");
        for(int i = 0; i < k; i++){
            sb.append(a[i]).append(",");
        }
        sb.deleteCharAt(sb.length()-1);
        sb.append(")");
        System.out.println(sb);
    }

    /**
     * 根据当前解向量a及输入数据构建候选集合
     *
     * @param a 解向量
     * @param k a中当前元素位置k
     * @param input 输入数据
     * @param c 输出变量,下一位置候选集合
     * @return 候选集数量
     */
    private int constructCandidates(int[] a,int k,int input,int[] c){
        boolean[] flag = new boolean[input + 1];
        int i;
        for(i = 0; i < k-1; i++){
            flag[a[i]] = true;
        }
        int j = 0;
        for(i = 1; i <= input; i++){
            if(!flag[i]){
                c[j] = i;
                j++;
            }
        }
        return j;
    }

    /**
     * 进行标记或其他处理
     *
     * @param a 解向量
     * @param k a中当前元素位置k
     * @param input 输入数据
     */
    private void makeMove(int[] a,int k,int input){

    }

    /**
     * 进行清除标记或其他处理
     *
     * @param a 解向量
     * @param k a中当前元素位置k
     * @param input 输入数据
     */
    private void unmakeMove(int[] a,int k,int input){

    }

    public static void main(String[] args){
        FullPermutation fullPermutation = new FullPermutation();
        int input = 3;
        int[] a = new int[input];
        fullPermutation.backtrack(a,0,input);
    }

}

四、执行结果

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我可以为您讲解一下 Java 如何使用回溯法解决全排列问题回溯法是一种暴力枚举的搜索方法,通常用于在所有可能的情况中寻找解决方案。在全排列问题中,我们需要找出一组数的所有排列,即所有数的不同顺序。下面是使用回溯法解决全排列问题Java 代码: ```java public class Permutations { public static void main(String[] args) { int[] nums = {1, 2, 3}; List<List<Integer>> result = permute(nums); System.out.println(result); } public static List<List<Integer>> permute(int[] nums) { List<List<Integer>> result = new ArrayList<>(); List<Integer> list = new ArrayList<>(); boolean[] used = new boolean[nums.length]; backtrack(result, list, nums, used); return result; } public static void backtrack(List<List<Integer>> result, List<Integer> list, int[] nums, boolean[] used) { if (list.size() == nums.length) { result.add(new ArrayList<>(list)); } else { for (int i = 0; i < nums.length; i++) { if (used[i]) continue; list.add(nums[i]); used[i] = true; backtrack(result, list, nums, used); used[i] = false; list.remove(list.size() - 1); } } } } ``` 在这个代码中,我们定义了一个 `permute` 方法,它接收一个整数数组 `nums`,并返回所有可能的排列。在方法中,我们定义了一个 `result` 变量保存所有排列的结果,一个 `list` 变量表示当前排列,以及一个 `used` 数组表示哪些数字已经被使用过了。 我们使用 `backtrack` 方法来递归地搜索所有可能的排列。如果当前排列的长度等于数组的长度,说明找到了一个排列,将其加入到 `result` 中。否则,我们遍历数组中所有未被使用过的数字,将其添加到当前排列中,并将其标记为已使用。接着,我们递归搜索下一个数字。搜索完成后,我们需要将当前数字从排列中移除,并将其标记为未使用,以便进行下一轮搜索。 这就是使用回溯法解决全排列问题Java 代码。希望对您有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值