diff --git a/N queens.py b/N queens.py new file mode 100644 index 00000000..42782318 --- /dev/null +++ b/N queens.py @@ -0,0 +1,75 @@ +''' +Solution : DFS with backtracking + - For each row, we try to add a queen on each column, one at a time. If + that is a valid move, then we recurse on the next row. + - Once we are able to recurse on all rows, we have a valid board for placing the queens + - Use a boolean board grid, when we place the queen at a cell, we mark it True. + - after recursion function returns at function call, we backtrack and mark it False. + - When we have a valid board + - We need to save it as a List[]. + - StringBuilder (Java) => list([char]) & "".join(list) in Python +Time Complexity: O(N!) (One can say, O(N^N) but not exactly) + - First row -> N options + - Second row -> N-2 options + - Third row -> N-4 options + - This comes to (N)*(N-2)*(N-4)... == N*(N-1)*(N-2)*(N-3)... +Space Complexity: O(N^2) + O(N) + - board + Recursive stack +''' +class Solution: + def solveNQueens(self, n: int) -> List[List[str]]: + valid_boards = [] + board = [[False]*n for i in range(n)] + + self.helper(n,0,board,valid_boards) # board_dimension,row, current_board, answer list + + return valid_boards + + def helper(self,n,row,board,valid_boards): + # base + if row==n: # completed placing queen in all rows of board. + new_valid_board = [] + for i in range(n): + row_char = [] + for j in range(n): + if board[i][j]==True: + row_char.append('Q') + else: + row_char.append('.') + row_string = "".join(row_char) # make each row elements as one string + new_valid_board.append(row_string) + + valid_boards.append(new_valid_board) # add to the answer list + return + + # logic + for col in range(n): + board[row][col] = True # action + if self.isValidMove(board,n,row,col): + self.helper(n,row+1,board,valid_boards) + board[row][col] = False # backtrack + + + def isValidMove(self,board,n,row,col): + # check column + for i in range(row-1,-1,-1): + if board[i][col]: + return False + + # check left diagonal + diag_col_check = col + for i in range(row-1,-1,-1): + diag_col_check-=1 + if diag_col_check>=0: + if board[i][diag_col_check]: + return False + + # check right diagonal + diag_col_check = col + for i in range(row-1,-1,-1): + diag_col_check+=1 + if diag_col_check<=n-1: + if board[i][diag_col_check]: + return False + + return True diff --git a/word search.py b/word search.py new file mode 100644 index 00000000..388c37b6 --- /dev/null +++ b/word search.py @@ -0,0 +1,44 @@ +''' +Solution : DFS with backtracking + - At each cell, we explore 4 options. + - Base is when we end up finding all characters of the target word, we return True +Time Complexity: O(m*n*4^L), L = length of word, m = rows, n = cols + - from each cell we try finding word. O(m*n) + - at each cell we'll do 4^L recursions +Space Complexity: O(L) - Height of Tree/Recursive stack +''' +class Solution: + def exist(self, board: List[List[str]], word: str) -> bool: + self.rows = len(board) + self.cols = len(board[0]) + + for i in range(self.rows): + for j in range(self.cols): + if self.helper(i,j,word,board): + return True + + return False + + + def helper(self,row,col,remaining_word,board): + # base + if remaining_word == "": + return True + + if row<0 or row>=self.rows or col<0 or col>=self.cols or board[row][col]!=remaining_word[0]: + return False + + + # logic + board[row][col] = "#" # mark visited - action + + found_remaining_word = False + for (row_offset, col_offset) in [[0,-1],[0,1],[-1,0],[1,0]]: + found_remaining_word = self.helper(row+row_offset,col+col_offset,remaining_word[1:], board) + if found_remaining_word: + break + + + board[row][col] = remaining_word[0] # backtrack + + return found_remaining_word