Given a positive integer N, determine if it's possible to arrange N queens on an NxN board such that no two queens attack the same squares.
Return the board if it's possible. Otherwise return null.
Input: 5 Output: 1 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 1 0 Note: 0 = an empty square 1 = a square with a queen
The space complexity depends on the board representation used. We could use an array with N x N elements. However, since we only need 1s and 0s for each square of the board, we can also use a single n2-bit number to represent the board.
The number of bits in an n2-bit number is log n2 = 2 log n
Space complexity of board representations:
Solution | Time | Space |
---|---|---|
Brute force | O(n2 * nn) | O(log n) or O(n2) |
Backtracking | O(n!) | O(log n) or O(n2) |
Closed-form solution | O(n) | O(log n) or O(n2) |
def n_queens(n) board = Array.new(n) { Array.new(n, 0) } directions = [ [0, 1], [1, 1], [1, 0], [1, -1], [0, -1], [-1, -1], [-1, 0], [-1, -1] ] check_board = lambda do n_queens = 0 (0 .. n-1).each do |row| (0 .. n-1).each do |col| next if board[row][col] == 0 directions.each do |dir| i, j = row + dir[0], col + dir[1] while i >= 0 and i < n and j >= 0 and j < n return false if board[i][j] == 1 i, j = i + dir[0], j + dir[1] end end n_queens += 1 end end n_queens == n end n_sq = n**2 positions = (0 .. n-1).to_a positions.each do |j| board[j / n][j % n] = 1 end end_positions = (n_sq-n .. n_sq-1).to_a while !check_board.call i = n - 1 positions[i] += 1 board = Array.new(n) { Array.new(n, 0) } positions.each do |j| board[j / n][j % n] = 1 end while positions[i] == end_positions[i] i -= 1 positions[i] += 1 return false if positions[i] == n_sq positions[i + 1] = positions[i] end end board end
Time | O(n2 * nn) | |
Space | O(log n) or O(n2) |
def n_queens(n) board = Array.new(n) { Array.new(n, 0) } diagonals = [[-1, -1], [1, -1], [1, 1], [-1, 1]] can_place_queen = lambda do |row, column| (0 .. n-1).each do |i| return false if board[row][i] == 1 return false if board[i][column] == 1 end diagonals.each do |diagonal| i, j = row + diagonal[0], column + diagonal[1] while i >= 0 and i < n and j >= 0 and j < n return false if board[i][j] == 1 i, j = i + diagonal[0], j + diagonal[1] end end true end try_row = lambda do |row| return true if row == n (0 .. n-1).each do |column| next unless can_place_queen.call(row, column) board[row][column] = 1 if try_row.call(row + 1) return board else board[row][column] = 0 end end false end try_row.call(0) end
Time | O(n!) | |
Space | O(log n) or O(n2) |
def n_queens(n) return false if n == 2 || n == 3 board = Array.new(n+1) { Array.new(n+1, 0) } if n % 2 == 1 board[n][n] = 1 n -= 1 end if n % 6 != 0 (1 .. n/2).each do |i| j = (2*i + n/2 - 3) % n board[i][1 + j] = 1 board[n + 1 - i][n - j] = 1 end elsif (n - 2) % 6 != 0 (1 .. n/2).each do |i| board[i][2*i] = 1 board[n/2 + i][2*i - 1] = 1 end end board[1 .. -1].map {|row| row[1 .. -1] } end
Time | O(n) | |
Space | O(log n) or O(n2) |