回溯法解决排列/组合/子集问题

回溯法

代码方面,回溯算法的框架:

result = []
def backtrack(路径, 选择列表):
    if 满足结束条件:
        result.add(路径)
        return

    for 选择 in 选择列表:
        [if 其他选择:行为]
        做选择
        backtrack(路径, 选择列表)
        撤销选择
排列问题

问题描述:输入一个不包含重复数字的数组 nums,返回这些数字的全部排列。

这里根据公式
A n m = A n n = n ( n − 1 ) A^nm = A ^n n=n(n-1) Anm=Ann=n(n1)
可以算出有n(n-1)种可能。

在这里插入图片描述

package BackTrace;

import java.util.ArrayList;
import java.util.List;

/**
 * @author junfeng.lin
 * @date 2021/3/31 17:07
 */
public class AllRank {
    //结果集合
    public static List<List<Integer>> lists = new ArrayList<>();
    //使用数组来判断元素是否被访问过
    public static boolean[] isVisit;
    public static void getResult(int[] nums) {
        isVisit = new boolean[nums.length];
        dfs(nums ,new ArrayList<>());
    }

    public static void dfs(int[] nums ,List<Integer> cur) {
        if (cur.size() == nums.length) {
            //此处要新建集合,cur的值会在后续被改变,结果也被改变了
            lists.add(new ArrayList<>(cur));
            return;
        }
        //每次都从0开始遍历
        for (int i = 0;i < nums.length;i++) {
            if (isVisit[i]) continue;
            isVisit[i] = true;
            cur.add(nums[i]);
            dfs(nums ,cur);
            cur.remove(cur.size() - 1);
            isVisit[i] = false;
        }
    }

    public static void main(String[] args) {
        getResult(new int[]{1,2,3});
        System.out.println(lists);
    }
}

输出:

[[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]
子集问题

问题描述:输入一个不包含重复数字的数组nums,返回这些数字的所有子集

在这里插入图片描述

package BackTrace;

import java.util.ArrayList;
import java.util.List;

/**
 * @author junfeng.lin
 * @date 2021/3/31 19:54
 */
public class PowerSet {
    //结果集合
    public static List<List<Integer>> lists;
    //使用数组来判断元素是否被访问过
    public static boolean[] isVisit;
    public static void getPowerSet(int[] nums) {
        lists = new ArrayList<>();
        isVisit = new boolean[nums.length];
        dfs(nums ,new ArrayList<>() ,0);
    }

    /**
     * 回溯法
     * @param nums
     * @param cur
     * @param index
     */
    public static void dfs(int[] nums ,List<Integer> cur ,int index) {
        //走到了叶子结点,添加到结果中
        if (index == nums.length) {
            lists.add(new ArrayList<>(cur));
            return;
        }
        for (int i = index;i < nums.length;i++) {
            if (isVisit[i]) continue;

            //不加入这个数
            isVisit[i] = true;
            dfs(nums ,cur ,index + 1);
            isVisit[i] = false;

            //不加入这个数
            isVisit[i] = true;
            cur.add(nums[i]);
            dfs(nums ,cur ,index + 1);
            cur.remove(cur.size() - 1);
            isVisit[i] = false;
        }
    }

    public static void main(String[] args) {
        int[] nums = new int[]{1,2,3};
        getPowerSet(nums);
        System.out.println(lists);
    }
}

输出:

[[], [3], [2], [2, 3], [1], [1, 3], [1, 2], [1, 2, 3]]
组合问题

问题描述:从m个球里(编号为1,2,3…,m)一次取n个球,其中m>n,记录取出球的编号,枚举所有的可能性。

假设n为4,k为2,则根据公式
C n m = C 2 4 = 6 C^nm = C ^2 4=6 Cnm=C24=6
可以算出有6种可能。

在这里插入图片描述

package BackTrace;

import java.util.ArrayList;
import java.util.List;

/**
 * @author junfeng.lin
 * @date 2021/3/31 22:12
 */
public class Combination {
    public static List<List<Integer>> lists;
    //使用数组来判断元素是否被访问过
    public static boolean[] isVisit;
    public static void getCombination(int n ,int k) {
        lists = new ArrayList<>();
        isVisit = new boolean[n + 1];
        dfs(n ,k ,new ArrayList<>() ,1);
    }

    public static void dfs(int n ,int k ,List<Integer> cur ,int start) {
        //当集合中有k个元素时,说明出现了一种情况
        if (k == cur.size()) {
            lists.add(new ArrayList<>(cur));
            return;
        }
        for (int i = start;i <= n;i++) {
            cur.add(i);
            //注意从i + 1 开始,而不是从start开始向下递归
            dfs(n ,k ,cur ,i + 1);
            cur.remove(cur.size() - 1);
        }
    }

    public static void main(String[] args) {
        getCombination(4 ,2);
        System.out.println(lists);
    }
}

输出:

[[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
回溯法是一种常用于解决组合问题的算法,可以用来解决子集和全排列问题。 对于子集问题,可以使用回溯法来求解。具体步骤如下: 1. 定义一个组来存储当前的子集,初始为空集。 2. 从原始组的第一个元素开始,依次将元素加入到当前的子集中,并计算子集的和。 3. 如果子集的和等于目标和,则将当前的子集加入到结果集中。 4. 如果子集的和小于目标和,则继续向下搜索,将下一个元素加入到子集中。 5. 如果子集的和大于目标和,则回溯到上一个元素,将其从子集中删除,并继续向下搜索。 6. 如果已经搜索到原始组的最后一个元素,且子集的和不等于目标和,则回溯到上一个元素,将其从子集中删除,并继续向下搜索。 7. 重复步骤2到步骤6,直到搜索完所有的子集。 对于全排列问题,也可以使用回溯法来求解。具体步骤如下: 1. 定义一个组来存储当前的排列,初始为空。 2. 从原始组的第一个元素开始,依次将元素加入到当前的排列中。 3. 如果当前的排列长度等于原始组的长度,则将当前的排列加入到结果集中。 4. 如果当前的排列长度小于原始组的长度,则继续向下搜索,将下一个元素加入到排列中。 5. 如果当前的排列中已经包含了某个元素,则回溯到上一个元素,将其从排列中删除,并继续向下搜索。 6. 如果已经搜索到原始组的最后一个元素,且当前排列长度不等于原始组的长度,则回溯到上一个元素,将其从排列中删除,并继续向下搜索。 7. 重复步骤2到步骤6,直到搜索完所有的排列。 需要注意的是,在回溯过程中,需要维护一个访问组,用来记录哪些元素已经被访问过,避免重复访问。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值