回溯算法(Backtracking)说明与实例

定义

回溯算法(Backtracking)在很多场景中会使用,如N皇后,数迷,集合等,其是暴力求解的一种优化。参考https://en.wikipedia.org/wiki/Backtracking 中的说明,定义如下:

Backtracking is a general algorithm for finding all (or some) solutions to some computational problems, notably constraint satisfaction problems, that incrementally builds candidates to the solutions, and abandons each partial candidate c (“backtracks”) as soon as it determines that c cannot possibly be completed to a valid solution

从上文中可以得出, 核心的含义是 回溯算法是通过一步一步(通常是用递归)构建可能”解”,并且回溯不可能”解”来求所有或者部分解决方案的通用算法。其中“回溯”的具体意思就是将不可能解或者部分解的候选尽早的舍弃掉,“解”需要满足一定的限制条件(constraint satisfaction)

通用算法

这里介绍一下回溯算法的通用思想,一般来讲,会设置一个递归函数,函数的参数会携带一些当前的可能解的信息,根据这些参数得出可能解或者不可能而回溯。
参考 http://web.cse.ohio-state.edu/~gurari/course/cis680/cis680Ch19.html 这里面有一个通用的算法

ALGORITHM try(v1,...,vi)  // 这里的V1.....V2携带的参数说明 “可能解”  
   // 入口处验证是否是全局解,如果是,直接返回。 
   // 实际编程中也需要查看是否是无效解,如果是,也是直接返回
   IF (v1,...,vi) is a solution THEN RETURN (v1,...,vi)  
   FOR each v DO  // 对于每一个可能的解,进行查看
      // 下面的含义是形成一个可能解 进行递归
      IF (v1,...,vi,v) is acceptable vector  THEN 
        sol = try(v1,...,vi,v) 
        IF sol != () THEN RETURN sol 
        // 这个地方其实需要增加“回溯” 处理,实际编程中通常是函数参数的变化
      END 
   END 
   RETURN () 

经典算法

  • N皇后问题

这个是一个比较经典的问题, 意思是将N个“皇后”放在N*N的棋盘上,每个皇后不能再同一列,同一行,和斜对角 (这个就是限制条件constraint satisfaction)。 下面是回溯算法的需求所有的可能解。
算法基本的步骤思想为:
1)从第一行开始
2 )如果所有的皇后已经放置完成, 生成解,并且返回true
3)尝试当前行的所有列,如果当前行与列是合法的
3.1 修改棋盘让其成为部分解,
3.2 然后递归查看(主要是2, 3,4)该解是否合法
3.3 Backtrack 棋盘进行回溯
4) 如果上述所有的组合都为非法,返回false

    // 寻找N皇后问题的可能解, 我们用 '.' 表示不放置皇后,用'Q'表示放置皇后
    // 利用vector<string> 表示一个解决方案, vector<vector<string>> 表示                                 
    // 所有的解决方案。
    // 在递归初,我们可以生成一个待解的棋盘
    vector<vector<string>> solveNQueens(int n) {
        string tmp (n, '.');
        //生成一个N*N待解的棋盘,没有任何皇后
        vector<string> broad (n, tmp);
        nQueue = n;
        vector<vector<string>>
  • 7
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
回溯算法是一种常用的解决问题的方法,主要用于在一个问题的解空间中进行搜索和遍历,以找到问题的最优解或所有解。下面是一个基本的回溯算法的Java实现: ```java public class Backtracking { public static void main(String[] args) { int[] nums = {1, 2, 3}; boolean[] used = new boolean[nums.length]; List<List<Integer>> result = new ArrayList<>(); backtrack(result, new ArrayList<>(), nums, used); System.out.println(result); } private static void backtrack(List<List<Integer>> result, List<Integer> tempList, int[] nums, boolean[] used) { if (tempList.size() == nums.length) { result.add(new ArrayList<>(tempList)); } else { for (int i = 0; i < nums.length; i++) { if (used[i] || (i > 0 && nums[i] == nums[i - 1] && !used[i - 1])) { continue; } used[i] = true; tempList.add(nums[i]); backtrack(result, tempList, nums, used); used[i] = false; tempList.remove(tempList.size() - 1); } } } } ``` 这个例子中,我们要找到给定数组 `nums` 的所有排列。我们使用了一个 `used` 数组来记录哪些数字已经被使用过,用一个 `tempList` 来记录当前的排列,当 `tempList` 的大小等于 `nums` 的长度时,说明我们已经找到了一个排列,将它加入结果集 `result` 中。在递归的过程中,我们遍历数组,如果当前数字已经被使用过或者与前一个数字相同但前一个数字没有被使用过,则跳过该数字。否则,我们将该数字加入 `tempList` 中,并递归调用 `backtrack` 方法,递归结束后,我们需要将该数字从 `tempList` 中移除,并将其对应的 `used` 标记为未使用。最后,我们输出结果集 `result` 即可。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值