recursion - leanminmachine/cs-practice GitHub Wiki
public boolean groupSum(int start, int[] nums, int target) {
// Base case: if there are no numbers left, then there is a
// solution only if target is 0.
if (start >= nums.length) return (target == 0);
// Key idea: nums[start] is chosen or it is not.
// Deal with nums[start], letting recursion
// deal with all the rest of the array.
// Recursive call trying the case that nums[start] is chosen --
// subtract it from target in the call.
if (groupSum(start + 1, nums, target - nums[start])) return true;
// Recursive call trying the case that nums[start] is not chosen.
if (groupSum(start + 1, nums, target)) return true;
// If neither of the above worked, it's not possible.
return false;
}
Backtracking
Given a maze, NxN matrix. A rat has to find a path from source to destination. maze[0][0] (left top corner)is the source and maze[N-1][N-1](right bottom corner) is destination. There are few cells which are blocked, means rat cannot enter into those cells. Rat can move in any direction ( left, right, up and down).
Input: A 2D-matrix with 0’s and 1’s fill in it. 0 means that cell is blocked and 1 means rat can move to that cell.
Approach:
Create a solution matrix of the same structure as maze. Whenever rat moves to cell in a maze, mark that particular cell in solution matrix. At the end print the solution matrix, follow that 1’s from the top left corner, it will be that path for the rat. Algorithm:
If rat reaches the destination print the solution matrix. Else Mark the current cell in solution matrix as 1 If previous step is not in vertical direction (upwards) then move forward in the vertical direction(downwards) and recursively check if this movement leads to solution. If movement in above step doesn’t lead to the solution and If previous step is not in horizontal direction (towards left) then move forward in the horizontal direction(towards right) and recursively check if this movement leads to solution. If movement in above step doesn’t lead to the solution and If previous step is not in vertical direction (downwards) then move forward in the horizontal direction(upwards) and recursively check if this movement leads to solution. If movement in above step doesn’t lead to the solution and If previous step is not in horizontal direction (towards right) then move forward in the horizontal direction(towards left) and recursively check if this movement leads to solution. If none of the above solution works then BACKTRACK and mark the current cell as 0. NOTE: It is important to check the previous direction in which the rat has moved because if rat will move in the opposite direction w.r.t its previous direction then rat might end up in infinite loop. Example: if rat has moved to its left in the previous direction then if in next moves to right then moving left option will be available again then rat will move to left again , then again right and so on
public class RatInMaze {
public int[][] solution;
//initialize the solution matrix in constructor.
public RatInMaze(int N) {
solution = new int[N][N];
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
solution[i][j] = 0;
}
}
}
public void solveMaze(int[][] maze, int N) {
if (findPath(maze, 0, 0, N, "down")) {
print(solution, N);
}else{
System.out.println("NO PATH FOUND");
}
}
public boolean findPath(int[][] maze, int x, int y, int N, String direction) {
// check if maze[x][y] is feasible to move
if(x==N-1 && y==N-1){//we have reached
solution[x][y] = 1;
return true;
}
if (isSafeToGo(maze, x, y, N)) {
// move to maze[x][y]
solution[x][y] = 1;
// now rat has four options, either go right OR go down or left or go up
if(direction!="up" && findPath(maze, x+1, y, N, "down")){ //go down
return true;
}
//else go down
if(direction!="left" && findPath(maze, x, y+1, N,"right")){ //go right
return true;
}
if(direction!="down" && findPath(maze, x-1, y, N, "up")){ //go up
return true;
}
if(direction!="right" && findPath(maze, x, y-1, N, "left")){ //go left
return true;
}
//if none of the options work out BACKTRACK undo the move
solution[x][y] = 0;
return false;
}
return false;
}
// this function will check if mouse can move to this cell
public boolean isSafeToGo(int[][] maze, int x, int y, int N) {
// check if x and y are in limits and cell is not blocked
if (x >= 0 && y >= 0 && x < N && y < N && maze[x][y] != 0) {
return true;
}
return false;
}
public void print(int [][] solution, int N){
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
System.out.print(" " + solution[i][j]);
}
System.out.println();
}
}
public static void main(String[] args) {
int N = 5;
int[][] maze = { { 1, 0, 1, 1,1 }, { 1, 1, 1, 0,1 }, { 0, 0,0, 1, 1 },
{ 0, 0, 0, 1,0 },{ 0, 0,0, 1, 1 } };
RatInMaze r = new RatInMaze(N);
r.solveMaze(maze, N);
}
}
N-Queens
public class NQueens {
static int[] result; // this array will store the result
// result[i]=j; means queen at i-th row is placed at j-th column.
// Queens placed at (x1, y1) and (x2,y2)
// x1==x2 means same rows, y1==y2 means same columns, |x2-x1|==|y2-y1| means
// they are placed in diagonals.
public boolean canPlace(int x2, int y2) {
// This function will check if queen can be placed (x2,y2), or we can
// say that Can queen at x2 row is placed at y2 column.
// for finding the column for x2 row, we will check all the columns for
// all the rows till x2-1.
for (int i = 0; i < x2; i++) {
//result[i] == y2 => columns are same
//|i - x2| == |result[i] - y2| => in diagonals.
if ((result[i] == y2)
|| (Math.abs(i - x2) == Math.abs(result[i] - y2))) {
return false;
}
}
return true;
}
public void placeQueens(int x, int size) {
for (int i = 0; i < size; i++) {
//check if queen at xth row can be placed at i-th column.
if (canPlace(x, i)) {
result[x] = i; // place the queen at this position.
if (x == size - 1) {
System.out.println("Order of " + size + " queens"
+ Arrays.toString(result));
}
placeQueens(x + 1, size);
}
}
}
public static void main(String[] args) {
int n = 6;
result = new int[n];
NQueens i = new NQueens();
i.placeQueens(0, n);
}
}
SUDUKO SOLVER
Find empty cell, find int row, col number
If there are no empty cells, return true, problem solved.
For number from 1 to 9
a) If there if is safe for digit at cell[row][col]
fill the cell[row][col]=number and recursively try fill in rest of grid
b) If recursion successful, return true
c) Else, undo the selection, cell[row][col]=0 and try another
If all numbers are tried and solution not found, return false
public class SUDOKU {
public static int[][] grid;
public boolean solveSUDOKU() {
int row;
int col;
int[] blankCell = findBlankLocation();
row = blankCell[0];
col = blankCell[1];
if (row == -1) {
// means will have filled the grid, return;
return true;
}
// we need to fill grid[row][col] cell
for (int i = 1; i <= 9; i++) {
// check if number i is safe for grid[row][col] cell
if (isSafe(row, col, i)) {
// means its safe to fill the number
grid[row][col] = i;
// fill the rest of the grid
if (solveSUDOKU()) {
return true;
}
// if we are here that means current selection of number didnt
// work, revert back the changes
grid[row][col] = 0;
}
}
return false; // This will cause the backtracking
}
public boolean isSafe(int row, int col, int n) {
// we need to check row contains number n OR
// Column contains number n OR
// Block in which cell appears contains number n
// If Any of the above statement is true, return false
if (!UsedInRow(row, n) && !UsedInColumn(col, n)
&& !UsedInBox(row - row % 3, col - col % 3, n)) {
return true;
}
return false;
}
// check if n not in particular row
public boolean UsedInRow(int row, int n) {
for (int i = 0; i < 9; i++) {
if (grid[row][i] == n) {
return true;
}
}
return false;
}
// check if n not in particular column
public boolean UsedInColumn(int col, int n) {
for (int i = 0; i < 9; i++) {
if (grid[i][col] == n) {
return true;
}
}
return false;
}
// check if n not in particular box
public boolean UsedInBox(int boxStartRow, int boxStartCol, int n) {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (grid[i + boxStartRow][j + boxStartCol] == n) {
return true;
}
}
}
return false;
}
public int[] findBlankLocation() {
int[] cell = new int[2]; // cell[0]-row cell[1] -column
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
if (grid[i][j] == 0) {
cell[0] = i;
cell[1] = j;
return cell;
}
}
}
cell[0] = -1;
cell[1] = -1;
return cell; // means grid is full
}
public void print() {
for (int row = 0; row < 9; row++) {
if (row % 3 == 0) {
System.out.println(); // for more readability
}
for (int col = 0; col < 9; col++) {
if (col % 3 == 0) {
System.out.print(" "); // for more readability
}
System.out.print(grid[row][col] + " ");
}
System.out.println();
}
}
public static void main(String[] args) {
grid = new int[][] { { 5, 3, 0, 0, 7, 0, 0, 0, 0 },
{ 6, 0, 0, 1, 9, 5, 0, 0, 0 }, { 0, 9, 8, 0, 0, 0, 0, 6, 0 },
{ 8, 0, 0, 0, 6, 0, 0, 0, 3 }, { 4, 0, 0, 8, 0, 3, 0, 0, 1 },
{ 7, 0, 0, 0, 2, 0, 0, 0, 6 }, { 0, 6, 0, 0, 0, 0, 2, 8, 0 },
{ 0, 0, 0, 4, 1, 9, 0, 0, 5 }, { 0, 0, 0, 0, 8, 0, 0, 7, 9 } };
SUDOKU s = new SUDOKU();
if (s.solveSUDOKU()) {
s.print();
} else {
System.out.println("NO SOLUTION");
}
}
}
Search word in a matrix
Create a solution matrix of the same structure as Matrix.
Try each cell a starting point.
Check current cell is not already used and character in it matches with the character in the word at index (starts will 0).
Check if index = length of the word, means we have found the word. return true and print the solution matrix.
If above criteria matches, mark that cell with a number Whenever any cell matches with the criteria, put a number corresponding to it in solution matrix. (start with 0 and keep incrementing it, it will show us the path for the word).
Now try to solve rest of the problem recursively by making index +1. Check all 8 directions ( up, down, left right, diagonally up-left, diagonally up-right, diagonally down-left, diagonally down-right). Check the boundary conditions as well
If none of the 8 recursive calls return true, BACKTRACK and undo the changes ( put 0 to corresponding cell in solution matrix) and return false.
Choose different starting point.
If all the starting points tried and solution not found, return false
See the code for better understanding.
public class WordMatrix {
public int[][] solution;
int path = 1;
// initialize the solution matrix in constructor.
public WordMatrix(int N) {
solution = new int[N][N];
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
solution[i][j] = 0;
}
}
}
public boolean searchWord(char[][] matrix, String word) {
int N = matrix.length;
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
if (search(matrix, word, i, j, 0, N)) {
return true;
}
}
}
return false;
}
public boolean search(char[][] matrix, String word, int row, int col,
int index, int N) {
// check if current cell not already used or character in it is not not
if (solution[row][col] != 0 || word.charAt(index) != matrix[row][col]) {
return false;
}
if (index == word.length() - 1) {
// word is found, return true
solution[row][col] = path++;
return true;
}
// mark the current cell as 1
solution[row][col] = path++;
// check if cell is already used
if (row + 1 < N && search(matrix, word, row + 1, col, index + 1, N)) { // go
// down
return true;
}
if (row - 1 >= 0 && search(matrix, word, row - 1, col, index + 1, N)) { // go
// up
return true;
}
if (col + 1 < N && search(matrix, word, row, col + 1, index + 1, N)) { // go
// right
return true;
}
if (col - 1 >= 0 && search(matrix, word, row, col - 1, index + 1, N)) { // go
// left
return true;
}
if (row - 1 >= 0 && col + 1 < N
&& search(matrix, word, row - 1, col + 1, index + 1, N)) {
// go diagonally up right
return true;
}
if (row - 1 >= 0 && col - 1 >= 0
&& search(matrix, word, row - 1, col - 1, index + 1, N)) {
// go diagonally up left
return true;
}
if (row + 1 < N && col - 1 >= 0
&& search(matrix, word, row + 1, col - 1, index + 1, N)) {
// go diagonally down left
return true;
}
if (row + 1 < N && col + 1 < N
&& search(matrix, word, row + 1, col + 1, index + 1, N)) {
// go diagonally down right
return true;
}
// if none of the option works out, BACKTRACK and return false
solution[row][col] = 0;
path--;
return false;
}
public void print() {
for (int i = 0; i < solution.length; i++) {
for (int j = 0; j < solution.length; j++) {
System.out.print(" " + solution[i][j]);
}
System.out.println();
}
}
public static void main(String[] args) {
char[][] matrix = { { 't', 'z', 'x', 'c', 'd' },
{ 'a', 'h', 'n', 'z', 'x' }, { 'h', 'w', 'o', 'i', 'o' },
{ 'o', 'r', 'n', 'r', 'n' }, { 'a', 'b', 'r', 'i', 'n' } };
WordMatrix w = new WordMatrix(matrix.length);
if (w.searchWord(matrix, "horizon")) {
w.print();
} else {
System.out.println("NO PATH FOUND");
}
}
}
Knight's Tour
public class KnightTour {
int[][] solution;
int path = 0;
public KnightTour(int N) {
solution = new int[N][N];
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
solution[i][j] = 0;
}
}
}
public void solve() {
if (findPath(0, 0, 0, solution.length)) {
print();
} else {
System.out.println("NO PATH FOUND");
}
}
public boolean findPath(int row, int column, int index, int N) {
// check if current is not used already
if (solution[row][column] != 0) {
return false;
}
// mark the current cell is as used
solution[row][column] = path++;
// if (index == 50) {
if (index == N * N - 1) {
// if we are here means we have solved the problem
return true;
}
// try to solve the rest of the problem recursively
// go down and right
if (canMove(row + 2, column + 1, N)
&& findPath(row + 2, column + 1, index + 1, N)) {
return true;
}
// go right and down
if (canMove(row + 1, column + 2, N)
&& findPath(row + 1, column + 2, index + 1, N)) {
return true;
}
// go right and up
if (canMove(row - 1, column + 2, N)
&& findPath(row - 1, column + 2, index + 1, N)) {
return true;
}
// go up and right
if (canMove(row - 2, column + 1, N)
&& findPath(row - 2, column + 1, index + 1, N)) {
return true;
}
// go up and left
if (canMove(row - 2, column - 1, N)
&& findPath(row - 2, column - 1, index + 1, N)) {
return true;
}
// go left and up
if (canMove(row - 1, column - 2, N)
&& findPath(row - 1, column - 2, index + 1, N)) {
return true;
}
// go left and down
if (canMove(row + 1, column - 2, N)
&& findPath(row + 1, column - 2, index + 1, N)) {
return true;
}
// go down and left
if (canMove(row + 2, column - 1, N)
&& findPath(row + 2, column - 1, index + 1, N)) {
return true;
}
// if we are here means nothing has worked , backtrack
solution[row][column] = 0;
path--;
return false;
}
public boolean canMove(int row, int col, int N) {
if (row >= 0 && col >= 0 && row < N && col < N) {
return true;
}
return false;
}
public void print() {
DecimalFormat twodigits = new DecimalFormat("00");
for (int i = 0; i < solution.length; i++) {
for (int j = 0; j < solution.length; j++) {
System.out.print(" " + twodigits.format(solution[i][j]));
}
System.out.println();
}
}
public static void main(String[] args) {
int N = 8;
KnightTour i = new KnightTour(N);
i.solve();
}
}