回溯算法 详解

本文深入探讨了回溯算法的原理,将其比喻为多叉树的遍历过程,并提供了全排列和N皇后问题的解题思路及Java和JavaScript代码实现。通过回溯法,可以有效地找到所有可能的解决方案,同时避免无效的路径探索。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  1. 回溯算法是一种暴力穷举算法。
  2. 穷举的过程就是遍历一棵多叉树的过程。
  3. 回溯算法 的代码框架和 多叉树遍历 的代码框架类似:
// 回溯算法框架
List<Value> result;
void backtrack(路径,选择列表){
	if(满足结束条件){
		result.add(路径)
		return;
	}
	for(选择:选择列表){
		做选择;
		backtrack(路径,选择列表);
		撤销选择;
	}
}
// 多叉树遍历框架
void traverse(TreeNode root){
	if(root == null){
		return;
	}
	for(TreeNode child : root.children){
		traverse(child);
	}
}

练习

全排列

/**
 * @param {number[]} nums
 * @return {number[][]}
 */

var permute = function(nums) {
    let res = [],ans = [];
    backTrack(nums,res,ans);
    return res;
};

function backTrack (nums, result, track) {
    if (track.length === nums.length) {
        // 更改引用类型的指针。目的;防止回溯行为影响到当前数组状态。
        result.push([...track])
    }
    for (let i = 0; i < nums.length; i++) {
        if (track.includes(nums[i])) {
            continue
        }
        track.push(nums[i])
        backTrack(nums, result, track)
        track.pop()
    }
}

N皇后

java版本:

class Solution {
    // 符合条件的单个结果(路径)
    private List<String> path = new LinkedList<>();
    // 符合条件的结果集合
    private List<List<String>> ans = new ArrayList<>();
    public List<List<String>> solveNQueens(int n) {
        // 新建字符棋盘,相比操作字符串,更方便
        char[][] board = new char[n][n];
        // 初始化棋盘
        for (int i=0; i<board.length; ++i) {
            Arrays.fill(board[i], '.');
        }
        // 从 row=0 第一行开始遍历
        backtracking(n, 0, board);
        return ans;
    }
    private void backtracking(int n, int row, char[][] board) {
        // 结束条件:所有行都遍历完成,注意是n不是n-1
        if (row == n) {
            path = array2List(board);
            ans.add(path);
            return;
        }
        // 遍历选择列表,这里的一个选择就是 row+col
        for (int col=0; col<n; ++col) {
            if (!isValid(n, row, col, board)) {
                // 排除不合法的选择(皇后存在冲突)
                continue;
            }
            // 做选择
            board[row][col] = 'Q';
            // 递归调用,进入下一行的决策
            backtracking(n, row+1, board);
            // 撤销选择
            board[row][col] = '.';
        }
    }
    /**
     * 将符合条件的某个棋盘数组转换成字符串列表
     */
    private List<String> array2List(char[][] board) {
        List<String> ans = new ArrayList<>();
        for (int i=0; i<board.length; ++i) {
            ans.add(String.valueOf(board[i]));
        }
        return ans;
    }
    /**
     * 判断是否可以在当前棋盘 board[row][col] 这个位置放置皇后Q
     * n是棋盘的大小,避免重复计算,所以作为参数传入
     */
    private boolean isValid(int n, int row, int col, char[][] board) {
        // 检查board[row][col]这个位置所在这一列正上方中,看是否已经存在 Q,存在说明列存在冲突,不能再放置皇后Q
        for (int i=0; i<row; ++i) {
            if (board[i][col] == 'Q') {
                return false;
            }
        }
        // 检查board[row][col]这个位置左上方对角线是否已经存在皇后,左下方不用检查,因为backtracking函数是一行一行遍历的,下方的还没遍历到呢
        for (int i=row-1,j=col-1; i>=0 && j>=0; --i,--j) {
            if (board[i][j] == 'Q') {
                return false;
            }
        }
        // 检查board[row][col]这个位置右上方对角线是否已经存在皇后,右下方不用检查,因为 backtracking函数是一行一行遍历的,下方的还没遍历到呢
        for (int i=row-1,j=col+1; i>=0 && j<n; --i,++j) {
            if (board[i][j] == 'Q') {
                return false;
            }
        }
        return true;
    }
}

js版本:

var solveNQueens = function (n) {
  const board = new Array(n).fill(0).map(() => new Array(n).fill('.'));
  const result = [];
  backTracking(n, 0, board, result);
  return result;
};

var judge = function (i, j, board, n) {
  // 检查列
  for (let x = 0; x < i; x++) {
    if (board[x][j] === 'Q') {
      return false;
    }
  }
  // 对角线
  for (let x = i - 1, y = j - 1; x >= 0 && y >= 0; x--, y--) {
    if (board[x][y] === 'Q') {
      return false;
    }
  }
  // 反对角线
  for (let x = i - 1, y = j + 1; x >= 0 && y < n; x--, y++) {
    if (board[x][y] === 'Q') {
      return false;
    }
  }
  return true;
}

var backTracking = function (n, row, board, result) {
  if (row == n) {
    result.push(board.map(item => item.join('')));
    return;
  }
  for (let col = 0; col < n; col++) {
    if (judge(row, col, board,n)) { // 验证合法就可以放
      board[row][col] = 'Q'; // 放置皇后
      backTracking(n, row + 1, board, result);
      board[row][col] = '.'; // 回溯,撤销皇后
    }
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

酱子姐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值