模板代码
result = []
def backtrack(路径, 选择列表):
if 满足结束条件:
result.add(路径)
return
for 选择 in 选择列表:
做选择
backtrack(路径, 选择列表)
撤销选择
回溯算法可以理解为同层次状态执行完之后进行状态的复原。由于一个循环递归到最后状态才会执行下一个循环,并且复原了原来所有数据所以路径可以写成全局的 。
一、全排列问题
为了简单清晰起见,我们这次讨论的全排列问题不包含重复的数字,那么我们当时是怎么穷举全排列的呢?比方说给三个数[1,2,3]
全排列代码
List<List<Integer>> res = new LinkedList<>();
/* 主函数,输入一组不重复的数字,返回它们的全排列 */
List<List<Integer>> permute(int[] nums) {
// 记录「路径」
LinkedList<Integer> track = new LinkedList<>();
backtrack(nums, track);
return res;
}
// 路径:记录在 track 中
// 选择列表:nums 中不存在于 track 的那些元素
// 结束条件:nums 中的元素全都在 track 中出现
void backtrack(int[] nums, LinkedList<Integer> track) {
// 触发结束条件
if (track.size() == nums.length) {
res.add(new LinkedList(track));
return;
}
for (int i = 0; i < nums.length; i++) {
// 排除不合法的选择
if (track.contains(nums[i]))
continue;
// 做选择
track.add(nums[i]);
// 进入下一层决策树
backtrack(nums, track);
// 取消选择
track.removeLast();
}
二、N 皇后问题
给你一个 N×N 的棋盘,让你放置 N 个皇后,使得它们不能互相攻击。
PS:皇后可以攻击同一行、同一列、左上左下右上右下四个方向的任意单位。
这是 N = 8 的一种放置方法:
这个问题本质上跟全排列问题差不多,决策树的每一层表示棋盘上的每一行;每个节点可以做出的选择是,在该行的任意一列放置一个皇后。
public class 八皇后回溯解法 {
private static int count = 8;
private static List<String[][]> queenList = new ArrayList<String[][]>();
private static String[][] queen = new String[count][count];
private static void init() {
for (int i = 0; i < queen.length; i++) {
for (int j = 0; j < queen.length; j++) {
queen[i][j] = "*";
}
}
}
private static void nQueen(int row) {
if (row == count) {
String[][] newQueen = new String[count][count];
for (int i = 0; i < queen.length; i++) {
System.arraycopy(queen[i], 0, newQueen[i], 0, newQueen[i].length);
}
queenList.add(newQueen);
}
for (int col = 0; col < count; col++) {
if (!isValidte(row, col)) {
continue;
}
queen[row][col] = "Q";
nQueen(row + 1);
queen[row][col] = "*";
}
}
private static boolean isValidte(int row, int col) {
// TODO Auto-generated method stub
for (int i = 0; i < count; i++) {
if (queen[row][i] == "Q") {
return false;
}
}
检查右上方是否有皇后互相冲突
for (int i = row - 1; i > -1 && col + (row - i) < count; i--) {
if (queen[i][col + (row - i)] == "Q") {
return false;
}
}
检查左上方是否有皇后互相冲突
for (int i = row - 1; i < count && col - (row - i) > -1; i--) {
if (queen[i][col - (row - i)] == "Q") {
return false;
}
}
return true;
}
public static void main(String[] args) {
init();
nQueen(0);
}
}
附录
八皇后问题的一维数组普通递归解法,时间复杂度一样,空间复杂度更省
/**
*
* 用一维数组来表示整个棋盘(相比于用二维数组实现更加简单)
*
* 1随意在第一行的任意一列上放上皇后
* 2在第二行上的的任意一列上放上皇后检查是否和前面的行上的皇后冲突,冲突的话结束,不冲突继续放第三行
* 3递归执行上述检测直到放满八个皇后
* @author lzhcode
*
*/
public class 八皇后普通递归解法 {
static int iCount = 0; // 全局变量
static int[] WeiZhi = new int[8]; // 数组下标代表行数,值表示列数
static void Output() // 输出解
{
int i, j, flag = 1;
System.out.printf("第%2d种方案(★表示皇后):\n", ++iCount);// 输出序号。
System.out.printf(" ");
for (i = 1; i <= 8; i++) {
System.out.printf("▁");
}
System.out.printf("\n");
for (i = 0; i < 8; i++) {
System.out.printf("▕");
for (j = 0; j < 8; j++) {
//列的值从1开始计数所以要减去1
if (WeiZhi[i] - 1 == j) {
System.out.printf("★"); // 皇后的位置
} else {
if (flag < 0) {
System.out.printf(" "); // 棋格
} else {
System.out.printf("█"); // 棋格
}
}
flag = -1 * flag;
}
System.out.printf("▏\n");
flag = -1 * flag;
}
System.out.printf(" ");
for (i = 1; i <= 8; i++) {
System.out.printf("▔");
}
System.out.printf("\n");
}
static void EightQueen(int n) // 算法
{
int i, j;
int conflict; // 用于判断是否冲突
if (n == 8) // 若8个皇后已放置完成
{
Output(); // 输出求解的结果
return;
}
for (i = 1; i <= 8; i++) // 从第n行第i列逐个试探
{
WeiZhi[n] = i; // 在该行的第i列上放置
// 判断第n个皇后是否与前面皇后形成攻击
conflict = 0;
for (j = 0; j < n; j++) {
if (WeiZhi[j] == WeiZhi[n]) // 同列形成攻击
{
conflict = 1;
} else if (Math.abs(WeiZhi[j] - WeiZhi[n]) == (n - j))// 斜线形成攻击
{
conflict = 1;
} else {
}
}
if (conflict==0) // 没有冲突,就开始下一列的试探,否则否认当前的放法
EightQueen(n + 1); // 递归调用
}
}
public static void main(String[] args) {
System.out.printf("八皇后问题求解!\n");
System.out.printf("八皇后排列方案:\n");
EightQueen(0); // 求解
}
}