LeetCode - N Queues

6 minute read

Problem description

description

The n-queens puzzle is the problem of placing n queens on an n×n chessboard such that no two queens attack each other.

Given an integer n, return all distinct solutions to the n-queens puzzle.

Each solution contains a distinct board configuration of the n-queens’ placement, where ‘Q’ and ‘.’ both indicate a queen and an empty space respectively.

Example:

1
2
3
4
5
6
7
8
9
10
11
12
13
Input: 4
Output: [
 [".Q..",  // Solution 1
  "...Q",
  "Q...",
  "..Q."],

 ["..Q.",  // Solution 2
  "Q...",
  "...Q",
  ".Q.."]
]
Explanation: There exist two distinct solutions to the 4-queens puzzle as shown above.

Analysis

Basically we can use backtrack to track all of the possible position and find a solution.

Here’s the code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class nQueens {
  public List<List<String>> solveNQueens(int n) {
    List<List<String>> res = new ArrayList<>();

    char[][] matrix = new char[n][n];
    boolean[] rows = new boolean[n];
    boolean[] cols = new boolean[n];
    boolean[] lefts = new boolean[2 * n - 1];
    boolean[] rights = new boolean[2 * n - 1];

    for (int i = 0; i < n; i++) {
      Arrays.fill(matrix[i], '.');
    }

    backtrack(res, matrix, lefts, rights, rows, cols, 0, n);

    return res;
  }

  void backtrack(
      List<List<String>> res,
      char[][] matrix,
      boolean[] lefts,
      boolean[] rights,
      boolean[] rows,
      boolean[] cols,
      int start,
      int n) {
    boolean meet = true;
    for (boolean j : rows) {
      meet = meet && j;
    }

    for (boolean j : cols) {
      meet = meet && j;
    }

    if (meet) {
      List<String> list = new ArrayList<>();
      for (int j = 0; j < n; j++) {
        list.add(new String(matrix[j]));
      }

      res.add(list);
      return;
    }

    for (int i = start; i < n * n; i++) {
      int row = i / n;
      int col = i % n;

      int left = getLeft(row, col, n);
      int right = getRight(row, col, n);
      if (rows[row] || cols[col] || lefts[left] || rights[right]) {
        continue;
      }

      matrix[row][col] = 'Q';
      rows[row] = true;
      cols[col] = true;
      lefts[left] = true;
      rights[right] = true;

      if (start != 0){
        i = (row + 1) * n - 1;
      }
      backtrack(res, matrix, lefts, rights, rows, cols, i + 1, n);

      matrix[row][col] = '.';
      rows[row] = false;
      cols[col] = false;
      lefts[left] = false;
      rights[right] = false;
    }
  }

  int getLeft(int i, int j, int n) {
    return n - 1 - i + j;
  }

  int getRight(int i, int j, int n) {
    return 2 * n - 2 - i - j;
  }
}

But there’s a more efficient one which backtrack the rows only but not all of the cells.

Here’s the code, and explanation:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
class Solution {
  int rows[];
  // "hill" diagonals
  int hills[];
  // "dale" diagonals
  int dales[];
  int n;
  // output
  List<List<String>> output = new ArrayList();
  // queens positions
  int queens[];

  public boolean isNotUnderAttack(int row, int col) {
    int res = rows[col] + hills[row - col + 2 * n] + dales[row + col];
    return (res == 0) ? true : false;
  }

  public void placeQueen(int row, int col) {
    queens[row] = col;
    rows[col] = 1;
    hills[row - col + 2 * n] = 1;  // "hill" diagonals
    dales[row + col] = 1;   //"dale" diagonals
  }

  public void removeQueen(int row, int col) {
    queens[row] = 0;
    rows[col] = 0;
    hills[row - col + 2 * n] = 0;
    dales[row + col] = 0;
  }

  public void addSolution() {
    List<String> solution = new ArrayList<String>();
    for (int i = 0; i < n; ++i) {
      int col = queens[i];
      StringBuilder sb = new StringBuilder();
      for(int j = 0; j < col; ++j) sb.append(".");
      sb.append("Q");
      for(int j = 0; j < n - col - 1; ++j) sb.append(".");
      solution.add(sb.toString());
    }
    output.add(solution);
  }

  public void backtrack(int row) {
    for (int col = 0; col < n; col++) {
      if (isNotUnderAttack(row, col)) {
        placeQueen(row, col);
        // if n queens are already placed
        if (row + 1 == n) addSolution();
          // if not proceed to place the rest
        else backtrack(row + 1);
        // backtrack
        removeQueen(row, col);
      }
    }
  }

  public List<List<String>> solveNQueens(int n) {
    this.n = n;
    rows = new int[n];
    hills = new int[4 * n - 1];
    dales = new int[2 * n - 1];
    queens = new int[n];

    backtrack(0);
    return output;
  }
}

And here’s my version of code after using rows to backtrack.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
class Solution {
    public List<List<String>> solveNQueens(int n) {
    List<List<String>> res = new ArrayList<>();

    char[][] matrix = new char[n][n];
    boolean[] rows = new boolean[n];
    boolean[] cols = new boolean[n];
    boolean[] lefts = new boolean[2 * n - 1];
    boolean[] rights = new boolean[2 * n - 1];

    for (int i = 0; i < n; i++) {
      Arrays.fill(matrix[i], '.');
    }

    backtrack(res, matrix, lefts, rights, rows, cols, 0, n);

    return res;
  }

  void backtrack(
      List<List<String>> res,
      char[][] matrix,
      boolean[] lefts,
      boolean[] rights,
      boolean[] rows,
      boolean[] cols,
      int start,
      int n) {
    boolean meet = true;
    for (boolean j : rows) {
      meet = meet && j;
    }

    for (boolean j : cols) {
      meet = meet && j;
    }

    if (meet) {
      List<String> list = new ArrayList<>();
      for (int j = 0; j < n; j++) {
        list.add(new String(matrix[j]));
      }

      res.add(list);
      return;
    }

    for (int i = 0; i < n; i++) {
      int row = start;
      int col = i;

      int left = getLeft(row, col, n);
      int right = getRight(row, col, n);
      if (rows[row] || cols[col] || lefts[left] || rights[right]) {
        continue;
      }

      matrix[row][col] = 'Q';
      rows[row] = true;
      cols[col] = true;
      lefts[left] = true;
      rights[right] = true;
        
      backtrack(res, matrix, lefts, rights, rows, cols, start + 1, n);
      
      matrix[row][col] = '.';
      rows[row] = false;
      cols[col] = false;
      lefts[left] = false;
      rights[right] = false;
    }
  }

  int getLeft(int i, int j, int n) {
    return n - 1 - i + j;
  }

  int getRight(int i, int j, int n) {
    return 2 * n - 2 - i - j;
  }
}

What to improve

  • more efficient backtrack