Algorithm and Data Structure Interview Guide - Detailed Explanation of the Classic N Queen Problem

N queen problem

!!! question

根据国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。给定 $n$ 个皇后和一个 $n \times n$ 大小的棋盘,寻找使得所有皇后之间无法相互攻击的摆放方案。

As shown in the figure below, when n = 4 n = 4n=4 , a total of two solutions can be found. From the perspective of the backtracking algorithm,n × nn \times nn×A chessboard of size n has a total of n 2 n^2n2 grids, giving all choiceschoices. In the process of placing queens one by one, the chessboard state is constantly changing, and the chessboard at each moment is the statestate.

Insert image description here

The figure below shows the three constraints of this question: multiple queens cannot be in the same row, the same column, or the same diagonal . It is worth noting that diagonals are divided into two types: main diagonals \and sub-diagonals /.

Insert image description here

Row-by-row placement strategy

The number of queens and the number of rows on the chessboard are both nn , so we can easily get an inference:each row of the chessboard allows and only allows one queen to be placed.

That is, we can adopt a row-by-row placement strategy: starting from the first row, placing a queen in each row until the end of the last row.

As shown in the figure below, it is 4 4Row-by-row placement process for the 4- queen problem. Due to frame limitations, the figure below only expands one of the search branches in the first row, and prunes solutions that do not satisfy column constraints and diagonal constraints.

Insert image description here

Essentially, the row-by-row placement strategy acts as a pruner , which avoids all search branches where multiple queens appear in the same row.

Column and diagonal pruning

In order to satisfy the column constraints, we can use a length of nnBoolean array of ncols records whether each column has a queen. Before each placement decision, wecolsprune the columns that already have queens and dynamically updatecolsthe state during backtracking.

So, how to deal with diagonal constraints? Suppose the row and column index of a certain grid on the chessboard is (row, col) (row, col)(row,co l ) , ​​select a main diagonal in the matrix, we find that the row index minus column index of all cells on the diagonal are equal, that is, row − col row - col of all cells on the diagonalrowco l is a constant value.

That is to say, if the two grids satisfy row 1 − col 1 = row 2 − col 2 row_1 - col_1 = row_2 - col_2row1col1=row2col2, then they must be on the same main diagonal. Using this rule, we can use the array shown in the figure below diag1to record whether there is a queen on each main diagonal.

In the same way, row + col row + col of all grids on the sub-diagonalrow+col is a constant value . We can also use arraysdiag2to handle subdiagonal constraints.

Insert image description here

Code

Please note that nnrow − col row - col in n- dimensional square matrixrowThe range of co l is[ − n + 1 , n − 1 ] [-n + 1, n - 1][n+1,n1] r o w + c o l row + col row+The range of co l is[ 0 , 2 n − 2 ] [0, 2n − 2][0,2 n2 ] , so the number of main diagonals and sub-diagonals is2 n − 1 2n - 12 n1 , that is, the lengths of the arraysdiag1anddiag2are both2 n − 1 2n - 12 n1

=== “Python”

```python title="n_queens.py"
[class]{}-[func]{backtrack}

[class]{}-[func]{n_queens}
```

=== “C++”

```cpp title="n_queens.cpp"
[class]{}-[func]{backtrack}

[class]{}-[func]{nQueens}
```

=== “Java”

```java title="n_queens.java"
[class]{n_queens}-[func]{backtrack}

[class]{n_queens}-[func]{nQueens}
```

=== “C#”

```csharp title="n_queens.cs"
[class]{n_queens}-[func]{backtrack}

[class]{n_queens}-[func]{nQueens}
```

=== “Go”

```go title="n_queens.go"
[class]{}-[func]{backtrack}

[class]{}-[func]{nQueens}
```

=== “Swift”

```swift title="n_queens.swift"
[class]{}-[func]{backtrack}

[class]{}-[func]{nQueens}
```

=== “JS”

```javascript title="n_queens.js"
[class]{}-[func]{backtrack}

[class]{}-[func]{nQueens}
```

=== “TS”

```typescript title="n_queens.ts"
[class]{}-[func]{backtrack}

[class]{}-[func]{nQueens}
```

=== “Dart”

```dart title="n_queens.dart"
[class]{}-[func]{backtrack}

[class]{}-[func]{nQueens}
```

=== “Rust”

```rust title="n_queens.rs"
[class]{}-[func]{backtrack}

[class]{}-[func]{n_queens}
```

=== “C”

```c title="n_queens.c"
[class]{}-[func]{backtrack}

[class]{}-[func]{nQueens}
```

=== “Zig”

```zig title="n_queens.zig"
[class]{}-[func]{backtrack}

[class]{}-[func]{nQueens}
```

Place nn row by rown times, considering column constraints, there arennn n − 1 n-1 n1 … \dots 2 2 2 1 1 1 choice,so the time complexity is O ( n ! ) O(n!)O ( n !) . In fact, pruning based on diagonal constraints can also significantly reduce the search space, so the search efficiency is often better than the above time complexity.

Arrays stateuse O ( n 2 ) O(n^2)O ( n2 )Space, arraycols,diags1anddiags2all useO ( n ) O (n)O ( n ) space. The maximum recursion depth isnnn , useO ( n ) O(n)O ( n ) stack frame space. Therefore,the space complexity is O ( n 2 ) O(n^2)O ( n2)

Guess you like

Origin blog.csdn.net/zy_dreamer/article/details/132865440