Backtracking: a prior sequence search algorithm similar number traversal (left, right) of

Backtracking to solve a problem, in fact, a decision tree traversal . You only need to think about three questions:

1, path : that is, has made a choice.

2. Select a list : that is, you can make the current selection.

3, the end condition : that is, to reach the bottom of the decision tree, do not choose the conditions.

If you do not understand the explanation of these three words, it does not matter, as we will use "full array" and "N Queens' two classic problem of backtracking algorithm to help you understand what these words mean, now you first keep impression.

Code side, backtracking algorithm framework:

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

    for 选择 in 选择列表:
        做选择
        backtrack(路径, 选择列表)
        撤销选择

Its core is for recursive loop inside, before the recursive call to "make choices", "deselection" after the recursive call , especially simple.

What is the selection and deselection of it, what is the underlying principle of this framework is it? Here we have "full array" puzzled before this issue be solved through a detailed inquiry about the secret!

First, the full array problem

We did it in high school math problems aligned portfolio, we also know that na number of non-repetition, the whole arrangement of a total of n! Th.

PS: For the sake of simplicity and clarity, all the problems we arranged this discussion does not contain duplicate numbers .

We were so full array of how exhaustive it? Let's say for three numbers [1,2,3], you certainly will not exhaustive chaos irregularly, usually something like this:

The first fixed first bit is 1, then the second can be 2, then the third is only 3; then be turned into 3 second place, third place would only be 2 up; then it can only change ...... after the first two, it becomes 2, and then an exhaustive

As long as the tree traversal, digital recording on the path from the root, in fact, all of the whole arrangement. We wish to have the tree backtracking algorithm called "decision tree」 .

Now, do you understand this core framework backtracking algorithm?

for 选择 in 选择列表:
    # 做选择
    将该选择从选择列表移除
    路径.add(选择)
    backtrack(路径, 选择列表)
    # 撤销选择
    路径.remove(选择)
    将该选择再加入选择列表

 

As long as we make a choice before the recursive, withdrawal just after the recursive option , you can get to choose the right path and each node of the list.

Next, the whole arrangement directly see the code:

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();
    }
}

Two, N queens problem

This is a very classic, simple to explain: to give you a N × N chessboard, let you place N queens so that they can not attack each other.

PS: Queen can attack the same row, the same column, in arbitrary units lower left four directions of the upper left upper right lower right.

The nature of the problem with almost full permutation problem, each layer of the tree represents each row on the board; selection may be made for each node is placed in a queen any one of the rows.

Apply directly to the frame:

vector<vector<string>> res;

/* 输入棋盘边长 n,返回所有合法的放置 */
vector<vector<string>> solveNQueens(int n) {
    // '.' 表示空,'Q' 表示皇后,初始化空棋盘。
    vector<string> board(n, string(n, '.'));
    backtrack(board, 0);
    return res;
}

// 路径:board 中小于 row 的那些行都已经成功放置了皇后
// 选择列表:第 row 行的所有列都是放置皇后的选择
// 结束条件:row 超过 board 的最后一行
void backtrack(vector<string>& board, int row) {
    // 触发结束条件
    if (row == board.size()) {
        res.push_back(board);
        return;
    }

    int n = board[row].size();
    for (int col = 0; col < n; col++) {
        // 排除不合法选择
        if (!isValid(board, row, col)) 
            continue;
        // 做选择
        board[row][col] = 'Q';
        // 进入下一行决策
        backtrack(board, row + 1);
        // 撤销选择
        board[row][col] = '.';
    }
}

This part of the main code, in fact, the problem with the whole arrangement similar isValid function to achieve is very simple:



/* 是否可以在 board[row][col] 放置皇后? */
bool isValid(vector<string>& board, int row, int col) {
    int n = board.size();
    // 检查列是否有皇后互相冲突
    for (int i = 0; i < n; i++) {
        if (board[i][col] == 'Q')
            return false;
    }
    // 检查右上方是否有皇后互相冲突
    for (int i = row - 1, j = col + 1; 
            i >= 0 && j < n; i--, j++) {
        if (board[i][j] == 'Q')
            return false;
    }
    // 检查左上方是否有皇后互相冲突
    for (int i = row - 1, j = col - 1;
            i >= 0 && j >= 0; i--, j--) {
        if (board[i][j] == 'Q')
            return false;
    }
    return true;
}

 

Published 159 original articles · won praise 75 · views 190 000 +

Guess you like

Origin blog.csdn.net/xuehuagongzi000/article/details/104299224