五大常用算法--回溯
概念
回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径。
关键词汇
解空间树、决策树、DFS
伪代码
result = [] def backtrack(路径, 选择列表): if 满足结束条件 result.add(路径) return for选择 in 选择列表 做选择 // 相当于二叉树的前序遍历 backtrack(路径, 选择列表) 撤销选择 // 相当于二叉树的后序遍历
树遍历 void traverse(TreeNode root) { for (TreeNode child : root.childern) // 前序遍历需要的操作 traverse(child); // 后序遍历需要的操作 }
经典例子
全排列
package backtracing; import java.util.ArrayList; import java.util.List; // 求n的全排列 public class PermutePrac { private List<List<Integer>> listList = new ArrayList<>(); public List<List<Integer>> permute(int[] nums) { int len = nums.length; backTrack(new ArrayList<>(), len, nums); return listList; } public void backTrack(List<Integer> list, int n, int[] nums) { // 判断回溯值 if (list.size() == n) { listList.add(new ArrayList<>(list)); } // 遍历 for (int i = 0; i < n; i++) { // 约束条件 if (list.contains(nums[i])) { continue; } list.add(nums[i]); backTrack(list, n, nums); // 回溯 list.remove(list.size() - 1); } } }
八皇后
package backtracing.nqueen; import java.util.ArrayList; import java.util.List; // leetcode 51 // // 给定一个整数 n,返回所有不同的 n 皇后问题的解决方案。 // // 每一种解法包含一个明确的 n 皇后问题的棋子放置方案,该方案中 'Q' 和 '.' 分别代表了皇后和空位。 // // 示例: // // 输入: 4 // 输出: [ // [".Q..", // 解法 1 // "...Q", // "Q...", // "..Q."], // // ["..Q.", // 解法 2 // "Q...", // "...Q", // ".Q.."] // ] public class Solution { List<List<String>> listlist = new ArrayList<>(); public List<List<String>> solveNQueens(int n) { solveNQueenUtil(0, n, new ArrayList<>()); return listlist; } private void solveNQueenUtil(int row, int n, List<String> list) { if (row == n) { listlist.add(new ArrayList<>(list)); return; } for (int col = 0; col < n; col++) { list.add(generateColn(col, n)); if (isSafe(list, n)) { solveNQueenUtil(row + 1, n, list); } list.remove(list.size() - 1); } } private String generateColn(int col, int n) { StringBuffer sb = new StringBuffer(); for (int i = 0; i < n; i++) { if (i == col) { sb.append("Q"); } else { sb.append("."); } } return sb.toString(); } boolean isSafe(List<String> list, int n) { return isColSafe(list, n) && isLeftDiagnoseSafe(list, n) && isRightDiagnoseSafe(list, n); } boolean isColSafe(List<String> list, int n) { for (int col = 0; col < n; col++) { int count = 0; for (int row = 0; row < list.size(); row++) { if (list.get(row).charAt(col) == 'Q') { count++; } } if (count > 1) { return false; } } return true; } boolean isLeftDiagnoseSafe(List<String> list, int n) { for (int col = 0; col < n; col++) { int nextRow = 0; int nextCol = col; int count = 0; if (leftDianoseUtil(list, nextRow, nextCol, count)) return false; } for (int row = 0; row < list.size(); row++) { int nextRow = row; int nextCol = n - 1; int count = 0; if (leftDianoseUtil(list, nextRow, nextCol, count)) return false; } return true; } private boolean leftDianoseUtil(List<String> list, int nextRow, int nextCol, int count) { while (nextRow < list.size() && nextCol >= 0) { if (list.get(nextRow).charAt(nextCol) == 'Q') { count++; } nextCol--; nextRow++; } if (count > 1) { return true; } return false; } boolean isRightDiagnoseSafe(List<String> list, int n) { for (int col = n - 1; col >= 0; col--) { int nextRow = 0; int nextCol = col; int count = 0; if (rightDianoseUtil(list, n, nextRow, nextCol, count)) return false; } for (int row = 0; row < list.size(); row++) { int nextRow = row; int nextCol = 0; int count = 0; if (rightDianoseUtil(list, n, nextRow, nextCol, count)) return false; } return true; } private boolean rightDianoseUtil(List<String> list, int n, int nextRow, int nextCol, int count) { while (nextRow < list.size() && nextCol < n) { if (list.get(nextRow).charAt(nextCol) == 'Q') { count++; } nextCol++; nextRow++; } if (count > 1) { return true; } return false; } public static void main(String[] args) { Solution solution = new Solution(); solution.solveNQueens(4); System.out.println(solution.listlist); } }
参考资料:
1. https://leetcode-cn.com/problems/n-queens/solution/hui-su-suan-fa-xiang-jie-by-labuladong/ (主要参考)
2. https://leetcode-cn.com/problems/n-queens/solution/nhuang-hou-by-leetcode/