0% found this document useful (0 votes)
22 views56 pages

DSA Most Asked Interview Questions

Uploaded by

maajinbuu132
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
22 views56 pages

DSA Most Asked Interview Questions

Uploaded by

maajinbuu132
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 56

Recursion and Backtracking

Question 1

Implement the myAtoi(string s) function, which converts a string to a 32-bit signed integer.

The algorithm for myAtoi(string s) is as follows:

Whitespace: Ignore any leading whitespace (" "). Signedness: Determine the sign by checking if the next character is '-' or '+',
assuming positivity is neither present. Conversion: Read the integer by skipping leading zeros until a non-digit character is
encountered or the end of the string is reached. If no digits were read, then the result is 0. Rounding: If the integer is out of the
32-bit signed integer range [-231, 231 - 1], then round the integer to remain in the range. Specifically, integers less than -231
should be rounded to -231, and integers greater than 231 - 1 should be rounded to 231 - 1. Return the integer as the final result.

Brute Force Approach: Iterate through each character in the string and manually perform each step of the algorithm, making
decisions based on the current character. This approach is straightforward but inefficient due to the multiple loops and
conditions.

Optimal Approach: Use regular expressions to parse the string efficiently in one pass. Extract the relevant parts (sign, integer,
and trailing non-digits) and handle rounding within the regex pattern.

Question 2

Given a double x and integer n, calculate x raised to power n. Basically Implement pow(x, n).

Brute Force: Repeatedly multiply x by itself n times.

Optimal Approach: Using binary exponentiation, repeatedly cut n in half, and only multiply x by itself when n is odd. This
approach significantly reduces the number of multiplications, making it much more efficient for large values of n.

Question 3

A digit string is good if the digits (0-indexed) at even indices are even and the digits at odd indices are prime (2, 3, 5, or 7).

For example, "2582" is good because the digits (2 and 8) at even positions are even and the digits (5 and 2) at odd positions are
prime. However, "3245" is not good because 3 is at an even index but is not even. Given an integer n, return the total number of
good digit strings of length n. Since the answer may be large, return it modulo 109 + 7.

A digit string is a string consisting of digits 0 through 9 that may contain leading zeros.

Brute force: Iterate over all possible digit strings of length n and count the number of good strings. This approach has a time
complexity of O(10^n) and is not feasible for large n.

Optimal approach: Use dynamic programming to store the number of good digit strings of length i and use this to calculate the
number of good digit strings of length i+1. This approach has a time complexity of O(n) and is much more efficient than the
brute force approach.

Question 4

Given a stack, the task is to sort it such that the top of the stack has the greatest element.

Brute Force Approach: Repeatedly pop the top element from the stack and insert it into its correct position in a sorted
temporary array. Once the stack is empty, push all the elements from the temporary array back into the stack.

Optimal Approach (Recursion with Temporary Stack): Create a temporary stack. While the original stack is not empty, pop
the top element and recursively place it in the correct position in the temporary stack while maintaining the sorted order. Pop
all the elements from the temporary stack and push them back into the original stack.

Question 5

You are given a stack St. You have to reverse the stack using recursion.

Brute Force Approach: In the brute force approach, we first transfer all the elements from the stack to a temporary stack using
recursion. Then, we transfer all the elements back to the original stack, again using recursion.
Optimal Approach: The optimal approach is to use two recursive functions. The first function removes the top element from the
stack and calls the second function to reverse the remaining elements. The second function then inserts the removed element at
the bottom of the reversed stack. This approach is more efficient than the brute force approach as it minimizes the number of
stack operations.

Question 6

Given an integer, K. Generate all binary strings of size k without consecutive 1’s.

Brute Force Approach: Generate all binary strings of size K and for each string check if it has consecutive 1's. If it does,
then discard the string. Time complexity: O(2^k).

Optimal Approach: Use dynamic programming to keep track of the number of valid strings of length i ending with 0 and 1.
Time complexity: O(k).

Question 7

Given n pairs of parentheses, write a function to generate all combinations of well-formed parentheses.

Brute Force Approach: Generate all possible strings of n pairs of parentheses and check if each string is well-formed. This
approach has a time complexity of O(4^n), as there are 4 possible characters (2 parentheses in each direction) for each position
in the string.

Optimal Approach (Dynamic Programming): Use a dynamic programming table to store the number of well-formed
parentheses strings for each number of pairs of parentheses. The table can be populated starting from 0 pairs of parentheses
and gradually increasing the number of pairs. This approach has a time complexity of O(n^2).

Question 8

Given an integer array nums of unique elements, return all possible subsets (the power set).

The solution set must not contain duplicate subsets. Return the solution in any order.

Brute force approach:

Iterate through the array and for each element, create a new subset by including or excluding the element. Recursively apply
this process to the remaining elements of the array. Time complexity: O(n * 2^n) where n is the length of the input array.

Optimal approach (backtrack):

Start with an empty subset. For each element in the array, recursively create a new subset by including or excluding the
element. Backtrack to remove the element and explore other possibilities. Time complexity: O(n * 2^n) where n is the length of
the input array.

Question 9

Given a pair of strings of equal lengths, Geek wants to find the better string. The better string is the string having more number
of distinct subsequences. If both the strings have equal count of distinct subsequence then return str1.

Brute Force: For each string, generate all possible subsequences and store them in a set to count the number of distinct
subsequences. The string with a higher distinct subsequence count is the better string.

Optimal Approach: Use dynamic programming to count the number of distinct subsequences for each string. Define dp[i][j] as
the count of distinct subsequences for string s1 up to index i and string s2 up to index j. Compute dp[i][j] based on transitions: 1.
if s1[i] == s2[j], dp[i][j] = dp[i-1][j-1] + dp[i-1][j] 2. if s1[i] != s2[j], dp[i][j] = dp[i-1][j]
The string with a higher dp[n][n] value is the better string.

Question 10

Given an array arr of size n of non-negative integers and an integer sum, the task is to count all subsets of the given array with a
sum equal to a given sum.

Note: Answer can be very large, so, output answer modulo 109+7.

Brute force approach:

1. Generate all possible subsets of the array.


2. Check the sum of each subset.
3. Count the number of subsets with the desired sum.

Time complexity: O(2^n), where n is the size of the array.

Optimal approach:

Use dynamic programming to store the number of subsets with a given sum for each prefix of the array.

Time complexity: O(n * sum), where n is the size of the array and sum is the desired sum.

Question 11

You are given an array 'A' of 'N' integers. You have to return true if there exists a subset of elements of 'A' that sums up to 'K'.
Otherwise, return false.

Brute Force Approach: This approach generates all the subsets of the array 'A' and checks if the sum of any subset is equal to
'K'. The complexity of this algorithm is O(2^N), where 'N' is the size of the array.

Optimal Approach (Dynamic Programming): This approach creates a 2D array 'dp' of size (N+1)x(K+1), where 'dp[i][j]' stores
whether there exists a subset of elements from the first 'i' elements of the array that sums up to 'j'. The complexity of this
algorithm is O(N*K).

Question 12

Given an array of distinct integers and a target, you have to return the list of all unique combinations where the chosen
numbers sum to target. You may return the combinations in any order.

The same number may be chosen from the given array an unlimited number of times. Two combinations are unique if the
frequency of at least one of the chosen numbers is different.

It is guaranteed that the number of unique combinations that sum up to target is less than 150 combinations for the given input.

Brute-force approach:

The brute-force algorithm loops through all possible subsets of the input array and checks if the sum of the subset is equal to the
target. If the sum is equal to the target, the subset is added to the list of unique combinations. The time complexity of the brute-
force algorithm is O(2^n), where n is the number of elements in the input array.

Optimal approach:

The optimal approach uses a dynamic programming algorithm to compute the list of unique combinations that sum up to the
target. The dynamic programming algorithm starts by creating a 2D array, where the rows represent the target values and the
columns represent the elements in the input array. The cell at row i and column j contains the list of unique combinations that
sum up to i using the elements in the input array up to and including element j. The dynamic programming algorithm fills in the
cells of the 2D array starting from the top-left corner (i = 0, j = 0) and moving right and down until the bottom-right corner (i =
target, j = n - 1) is reached. The time complexity of the dynamic programming algorithm is O(target * n), where n is the number
of elements in the input array.

Question 13

Given a collection of candidate numbers (candidates) and a target number (target), find all unique combinations in candidates
where the candidate numbers sum to target. Each number in candidates may only be used once in the combination.

Brute Force: Iterate over the candidates in a nested loop. For each pair of candidates, create a new combination by adding them
to the current combination. If the sum of the new combination is equal to the target, store it in the result list.

Optimal Approach (Depth-First Search): Start with an empty combination. Iterate over the candidates. For each candidate,
create a new combination by adding it to the current combination. If the sum of the new combination is less than the target,
recursively call the function with the new combination. If the sum of the new combination is equal to the target, store it in the
result list. If the sum of the new combination is greater than the target, discard it.

Question 14

Given an array print all the sum of the subset generated from it, in the increasing order.

Brute Force: This approach generates all the subsets of the array and calculates the sum of each subset. The sum of all the
generated subsets is stored in an array and then sorted in increasing order. The time complexity of this approach is O(2^n),
where n is the number of elements in the array.

Optimal Approach: A more efficient approach is to use dynamic programming. We start by creating a 2D array dp where dp[i]
[j] represents the sum of the subset of the array from index i to j. We can fill the dp array in O(n^2) time and then calculate the
sum of all subsets in O(n) time. The total time complexity of this approach is O(n^2), which is significantly better than the brute-
force approach for large arrays

Question 15

Given an array of integers that may contain duplicates the task is to return all possible subsets. Return only unique subsets and
they can be in any order.

Brute Force Approach:

The idea is to generate all possible combinations of elements in the array and then filter out duplicate combinations. The time
complexity of this approach is O(N * 2^N) where N is the number of elements in the array. This is because there are O(2^N)
subsets and each subset can be generated in O(N) time.

Optimal Approach:

A better approach is to use a recursive algorithm to generate all possible subsets. In the recursive step, each element in the array
is either included in the current subset or not. The time complexity of this approach is also O(N * 2^N), but it is more efficient
than the brute force approach because it avoids generating duplicate combinations.

Question 16

Find all valid combinations of k numbers that sum up to n such that the following conditions are true:

Only numbers 1 through 9 are used. Each number is used at most once. Return a list of all possible valid combinations. The list
must not contain the same combination twice, and the combinations may be returned in any order.

Brute Force Approach: This approach systematically generates all possible combinations of k numbers from 1 to 9 and checks if
each combination sums up to n. If a valid combination is found, it is added to the result list. The complexity of this approach is
O(9^k), which can be high for large values of k.

Optimal Approach: The optimal approach uses dynamic programming to efficiently find all valid combinations. It starts with a
2D table dp where dp[i][j] stores the number of ways to sum up to j using only the first i numbers. The table is then filled in
bottom-up fashion, starting with dp[1][j] = 1 for j = 1 to 9, and dp[i][j] = dp[i-1][j] + dp[i-1][j-i] for j >= i. Finally, the result is
obtained by backtracking through the table to find all valid combinations. The complexity of this approach is O(k * 9^k), which is
significantly better than the brute force approach for large values of k.

Question 17

Given a string containing digits from 2-9 inclusive, return all possible letter combinations that the number could represent.
Return the answer in any order.

A mapping of digits to letters (just like on the telephone buttons) is given below. Note that 1 does not map to any letters.

Brute Force Approach: Iterate over each digit in the string and append all possible letter combinations for that digit to a list.
Repeat this process for all digits, constructing all possible combinations of letters.

Optimal Approach: Use recursion to traverse the string. For each digit, append all possible letter combinations to the current
partial combination. Then, recursively call the function with the remaining digits and the updated partial combination. This
approach efficiently explores all possible combinations without redundant computations.

Question 18

You are given a string s, partition it in such a way that every substring is a palindrome. Return all such palindromic partitions of
s.

Brute Force Approach:

The brute-force approach is to generate all possible partitions of the string and then check if each substring is a palindrome. If
all substrings are palindromes, then the partition is added to the result set. Here we can use recursion to achieve this.

Optimal Approach (Dynamic Programming):


The optimal approach uses dynamic programming to determine if a string can be partitioned into palindromes. It creates a table
where each cell stores whether the substring starting at that index can be partitioned into palindromes. The table is filled in
bottom-up fashion, and the final answer is obtained from the last cell.

Question 19

Given an m x n grid of characters board and a string word, return true if the word exists in the grid. The word can be
constructed from letters of sequentially adjacent cells, where adjacent cells are horizontally or vertically neighboring. The same
letter cell may not be used more than once.

Brute Force: Iterate over each cell in the grid and perform a depth-first search, checking if the current cell matches the first
character of the word. If it does, recursively explore the adjacent cells to find the remaining characters in the word. Time
complexity: O(mn * 4^L), where L is the length of the word.

Optimal (Trie and Depth-First Search): Build a Trie data structure from the word. Start from each cell in the grid and perform
a depth-first search. For each cell, check if there is a path in the Trie starting from the root and ending at the cell's character.
Time complexity: O(mn * L), where L is the length of the word.

Question 20

The n-queens is the problem of placing n queens on n × n chessboard such that no two queens can attack each other. Given an
integer n, return all distinct solutions to the n -queens puzzle. Each solution contains a distinct boards configuration of the
queen's placement, where ‘Q’ and ‘.’ indicate queen and empty space respectively.

Brute Force Approach: Try all possible board configurations and check if they meet the n-queens requirements. For each board
configuration, place a queen at each row and check if it's safe. If safe, recurse for the next row. Time complexity is O(n^n), as
there are n^n possible board configurations and O(n^2) time to check each configuration.

Optimal Approach: Use backtracking with optimization. Start by placing a queen in the first row. Then recurse for the next row.
For each recursive call, consider all possible columns that are safe to place a queen. Use a bitmap or an array to track columns
and diagonals that are already used by queens. This optimization reduces the time complexity to O(n!).

Question 21

Consider a rat placed at (0, 0) in a square matrix of order N * N. It has to reach the destination at (N - 1, N - 1). Find all possible
paths that the rat can take to reach from source to destination. The directions in which the rat can move are 'U'(up), 'D'(down), 'L'
(left), 'R' (right). Value 0 at a cell in the matrix represents that it is blocked and the rat cannot move to it while value 1 at a cell in
the matrix represents that rat can travel through it.

Note: In a path, no cell can be visited more than one time.

Print the answer in lexicographical(sorted) order

Brute Force Approach:

A naive solution involves exploring all possible paths by backtracking. Starting at the source, the rat can move in four directions:
up, down, left, or right. It recursively explores each path until it reaches the destination or backtracks if it encounters a blocked
cell or revisits a previously visited cell.

Optimal Approach:

A more efficient approach is dynamic programming, storing the possible paths from different cells to the destination. This saves
recomputation of overlapping subproblems. The solution initializes a matrix of size N*N with all values set to -1, indicating no
path is known yet. It starts by filling the destination cell with 1 (denoting a valid path). For each cell, it checks the valid paths
from adjacent cells and updates its value accordingly. Finally, it prints all paths that have a non-zero value in the matrix.

Question 22

Given an undirected graph and a number m, determine if the graph can be colored with at most m colors such that no two
adjacent vertices of the graph are colored with the same color.

Brute Force Approach: Iterate over all possible combinations of colors for the vertices, and for each combination, check if the
adjacent vertices are colored with different colors. The time complexity of this approach is O(m^n), where m is the number of
colors and n is the number of vertices.

Optimal Approach: Use a greedy algorithm to color the vertices one by one. Start with any vertex and assign it an arbitrary
color. For each subsequent vertex, consider the colors of its adjacent vertices and assign it the first available color that is not
used by any of its adjacent vertices. If no such color is available, then the graph cannot be colored with m colors. The time
complexity of this approach is O(n + m), where n is the number of vertices and m is the number of colors.

Question 23

Given a 9x9 incomplete sudoku, solve it such that it becomes valid sudoku. Valid sudoku has the following properties.

1. All the rows should be filled with numbers(1 - 9) exactly once.

2. All the columns should be filled with numbers(1 - 9) exactly once.

3. Each 3x3 submatrix should be filled with numbers(1 - 9) exactly once.

Brute Force Approach The brute-force approach involves trying out all possible assignments of values to the empty cells until
the grid is complete and valid. This approach is straightforward to implement but has an exponential time complexity, making it
unsuitable for large Sudoku puzzles.

Optimal Approach A more efficient approach is to use recursive backtracking. This algorithm starts by assigning a value to an
empty cell. If the assigned value results in a valid Sudoku grid, the algorithm proceeds to the next empty cell. If not, it backtracks
and attempts a different value for the current cell. This approach has a time complexity of O(10^n), where n is the number of
empty cells, significantly faster than the brute-force approach.

Question 24

Given a string s and a dictionary of strings wordDict, return true if s can be segmented into a space-separated sequence of one or
more dictionary words.

Note that the same word in the dictionary may be reused multiple times in the segmentation.

Brute Force Approach:

Recursive approach: Start from the beginning of the string and check if it matches any word in the dictionary. If it does, try
to match the remaining string recursively. Continue until the entire string is matched or no more matches are found. The
time complexity is O(2^n), where n is the length of the string.

Optimal Approach:

Dynamic Programming (DP) approach: Create a DP table of size n+1, where n is the length of the string. For each substring
of length i, check if it is in the dictionary. If it is, set the DP table entry at i to True. Then, for each substring of length i,
check if there is a previous substring of length j (j < i) such that the substring from j to i is in the dictionary and the DP table
entry at j is True. If so, set the DP table entry at i to True. The time complexity is O(n^2), where n is the length of the string.

Question 25

Given a string num that contains only digits and an integer target, return all possibilities to insert the binary operators '+', '-',
and/or '*' between the digits of num so that the resultant expression evaluates to the target value.

Note that operands in the returned expressions should not contain leading zeros.

Brute Force Approach: Enumerate all possible ways to insert operators and recursively evaluate the expression to check if it
equals the target. The time complexity is O(4^n), where n is the length of the string.

Optimal Approach (Dynamic Programming): Use dynamic programming to store intermediate results. The states are defined
as dp[i][j][res], where i is the index of the current digit, j is the index of the previous operator, and res is the current result. The
state dp[i][j][res] represents if it is possible to reach the result res by inserting operators up to index i and using the operator at
index j as the previous operator. The time complexity is O(n^3), where n is the length of the string.

Dynamic Programming
Question 1

There is a frog on the '1st' step of an 'N' stairs long staircase. The frog wants to reach the 'Nth' stair. 'HEIGHT[i]' is the height of
the '(i+1)th' stair.If Frog jumps from 'ith' to 'jth' stair, the energy lost in the jump is given by absolute value of ( HEIGHT[i-1] -
HEIGHT[j-1] ). If the Frog is on 'ith' staircase, he can jump either to '(i+1)th' stair or to '(i+2)th' stair. Your task is to find the
minimum total energy used by the frog to reach from '1st' stair to 'Nth' stair.
Dynamic Programming Approach:

This problem can be efficiently solved using dynamic programming. We define a DP array dp where dp[i] represents the
minimum energy required to reach the ith stair. We initialize dp[1] with 0.

For each stair i, we iterate over all possible next stairs j = i+1, i+2. We calculate the energy cost cost of jumping from ith stair
to jth stair as the absolute difference between their heights. We then update dp[j] with the minimum of its current value and
dp[i] + cost.

By iterating through all stairs and considering all possible jumps, we ensure that dp[N] holds the minimum total energy required
to reach the Nth stair.

Explanation:

The problem exhibits optimal substructure, meaning the optimal solution to the problem for a given stair relies on the optimal
solution to smaller subproblems (i.e., reaching the previous stairs). Additionally, there is overlap in subproblems, as multiple
jumps may share a common starting or ending stair.

Dynamic programming efficiently solves such problems by storing the optimal solutions to subproblems in the dp array. This
approach eliminates redundant computations and ensures that the optimal solution is derived in a time-efficient manner.

Question 2

Mr. X is a professional robber planning to rob houses along a street. Each house has a certain amount of money hidden. All
houses along this street are arranged in a circle. That means the first house is the neighbour of the last one. Meanwhile, adjacent
houses have a security system connected, and it will automatically contact the police if two adjacent houses are broken into on
the same night. You are given an array/list of non-negative integers 'ARR' representing the amount of money of each house. Your
task is to return the maximum amount of money Mr. X can rob tonight without alerting the police.

Dynamic Programming Approach:

Dynamic programming involves breaking down a complex problem into a collection of simpler subproblems, storing their
solutions, and reusing them to solve larger subproblems.

In this problem, the subproblems are the maximum amount of money that can be robbed from a particular starting house
without alerting the police. We store the solutions to these subproblems in a table.

The recursive formula for the subproblems is:

rob(i, n) = max(arr[i] + rob(i+2, n), rob(i+1, n))

where:

rob(i, n) is the maximum amount of money that can be robbed from the i-th house onwards (inclusive) out of a total of n
houses
arr[i] is the amount of money hidden in the i-th house

The base cases are:

rob(i, n) = 0 for i >= n


rob(n-1, n) = arr[n-1]
rob(n, n) = arr[n]

We start from the last house and work our way backwards, filling in the table with the solutions to the subproblems. Finally, the
maximum amount of money that Mr. X can rob is stored in rob(0, n).

Question 3

Ninja is planing this ‘N’ days-long training schedule. Each day, he can perform any one of these three activities. (Running,
Fighting Practice or Learning New Moves). Each activity has some merit points on each day. As Ninja has to improve all his skills,
he can’t do the same activity in two consecutive days. Can you help Ninja find out the maximum merit points Ninja can earn?

You are given a 2D array of size N*3 ‘POINTS’ with the points corresponding to each day and activity. Your task is to
calculate the maximum number of merit points that Ninja can earn.

Optimal Approach using Dynamic Programming:

To solve this problem using dynamic programming, we can define a 2D array dp where dp[i][j] represents the maximum
number of merit points that Ninja can earn if he is on day i and his last activity was j.

We can populate the dp array recursively as follows:

dp[i][0] = max(dp[i-1][1], dp[i-1][2]) + POINTS[i][0] dp[i][1] = max(dp[i-1][0], dp[i-1][2]) + POINTS[i][1] dp[i][2] =


max(dp[i-1][0], dp[i-1][1]) + POINTS[i][2]

This recurrence relation ensures that Ninja cannot perform the same activity on consecutive days.

The maximum number of merit points Ninja can earn is given by max(dp[N-1][0], dp[N-1][1], dp[N-1][2]).

Explanation:

Dynamic programming is used in this problem to break down the problem into smaller subproblems, which are represented by
the states dp[i][j]. By solving these subproblems recursively, we can build up the solution to the original problem. The key
insight is that the maximum number of merit points at any state dp[i][j] depends only on the maximum number of merit points
in the previous states dp[i-1][0], dp[i-1][1], and dp[i-1][2].

Question 4

You are present at point ‘A’ which is the top-left cell of an M X N matrix, your destination is point ‘B’, which is the
bottom-right cell of the same matrix. Your task is to find the total number of unique paths from point ‘A’ to point
‘B’.In other words, you will be given the dimensions of the matrix as integers ‘M’ and ‘N’, your task is to find
the total number of unique paths from the cell MATRIX[0][0] to MATRIX['M' - 1]['N' - 1].

To traverse in the matrix, you can either move Right or Down at each step. For example in a given point MATRIX[i] [j], you can
move to either MATRIX[i + 1][j] or MATRIX[i][j + 1].

Dynamic Programming Approach:

Dynamic programming can be used to solve this problem by computing the number of paths for each cell in the matrix. We can
initialize the top and left edges of the matrix with 1, as there is only one way to reach any cell in these rows or columns.

For all other cells, we can compute the number of paths as the sum of paths from the cell above and to the left. This is because, at
each step, we can either move right or down.

Therefore, the number of paths for cell MATRIX[i][j] is given by:

MATRIX[i][j] = MATRIX[i - 1][j] + MATRIX[i][j - 1]

where MATRIX[i - 1][j] is the number of paths from the cell above and MATRIX[i][j - 1] is the number of paths from the cell to the
left.

Using dynamic programming, we can compute the number of paths for each cell in O(M * N) time, where M and N are the
dimensions of the matrix.

How Dynamic Programming Helps:

Dynamic programming breaks down the problem into smaller subproblems, which are represented by the number of paths for
each cell. By solving these smaller problems and storing their results, we can efficiently compute the overall solution without
having to explore every path in the matrix.

Question 5

Ninjaland is a country in the shape of a 2-Dimensional grid 'GRID', with 'N' rows and 'M' columns. Each point in the grid has
some cost associated with it. Find a path from top left i.e. (0, 0) to the bottom right i.e. ('N' - 1, 'M' - 1) which minimizes the sum of
the cost of all the numbers along the path. You need to tell the minimum sum of that path.

Optimal Approach:

Dynamic programming can be used to solve this problem efficiently. We initialize a 2D array 'dp' of size 'N' x 'M', where 'dp[i][j]'
represents the minimum cost of the path from (0, 0) to (i, j). We build the 'dp' array column by column, starting from the leftmost
column. For each cell 'dp[i][j]', we consider all possible paths to reach (i, j) from the cell above it and the cell to its left. We choose
the path with the minimum cost and add the cost of the current cell to it.

Time Complexity: O(NM) Space Complexity: O(NM)

Question 6
You have been given an N*M matrix filled with integer numbers, find the maximum sum that can be obtained from a path
starting from any cell in the first row to any cell in the last row.

From a cell in a row, you can move to another cell directly below that row, or diagonally below left or right. So from a particular
cell (row, col), we can move in three directions i.e.

Down: (row+1,col) Down left diagonal: (row+1,col-1) Down right diagonal: (row+1, col+1)

Dynamic Programming Approach:

1. Initialize: Create a 2D array dp of size N x M to store the maximum sum achievable from each cell. Initialize the first row of
dp with the values from the given matrix.
2. Iterate: Traverse the matrix row by row, starting from the second row. For each cell (row, col), calculate the maximum
sum dp[row][col] considering all three possible moves:
3. Down: dp[row-1][col]
4. Down left diagonal: dp[row-1][col-1]
5. Down right diagonal: dp[row-1][col+1]
6. Choose the maximum of these three sums and add it to the current matrix value to get dp[row][col].
7. Result: After completing the iteration, the maximum sum achievable will be the maximum value in the last row of dp,
which represents the sum of the path with the highest score.

Dynamic Programming Explanation:

This approach uses dynamic programming by breaking down the problem into smaller subproblems (individual cells) and
solving them iteratively. The dp array stores the optimal solutions for these subproblems, and the final solution is built
incrementally by combining the optimal solutions for each step. This avoids the need to explore all possible paths, significantly
reducing the time and space complexity of the algorithm.

Question 7

You are given an array/list ‘ARR’ of ‘N’ positive integers and an integer ‘K’. Your task is to check if there exists
a subset in ‘ARR’ with a sum equal to ‘K’.

Note: Return true if there exists a subset with sum equal to ‘K’. Otherwise, return false.

Optimal Approach:

This problem can be efficiently solved using Dynamic Programming. Let's define a 2D array DP, where DP[i][j] represents
whether there exists a subset of the first i elements of ARR that sums up to j. Initialize DP[0][0] to true (empty subset has a sum of
0). For each subsequent element ARR[i], we can consider two cases:

1. Include ARR[i]: If DP[i-1][j-ARR[i]] is true, then we can add ARR[i] to a subset that sums up to j-ARR[i]. Thus, DP[i][j] is
set to true.
2. Exclude ARR[i]: If we don't include ARR[i], then DP[i][j] is the same as DP[i-1][j].

After filling the DP array, the answer is stored in DP[N][K].

Time Complexity: O(N * K), where N is the size of ARR and K is the target sum. Space Complexity: O(N * K)

Question 8

A thief is robbing a store and can carry a maximal weight of W into his knapsack. There are N items and the ith item weighs wi
and is of value vi. Considering the constraints of the maximum weight that a knapsack can carry, you have to find and return
the maximum value that a thief can generate by stealing items.

Optimal Approach:

To solve this problem optimally, we can use dynamic programming. Let's define a 2D array dp where dp[i][j] represents the
maximum value that can be achieved using the first i items while staying within the weight limit of j.

We can initialize dp[0][j] to 0 for all j, representing the value achieved with no items used. For i from 1 to N, we can iterate over
all possible weights j and determine the maximum value achievable using either the i-th item or not.

If j - wi >= 0, then we can add the value of the i-th item to the maximum value achieved without it (dp[i - 1][j - wi]), giving
us dp[i][j] = dp[i - 1][j - wi] + vi. Otherwise, we use the previous maximum value: dp[i][j] = dp[i - 1][j].

By filling the dp array iteratively, we can obtain the maximum value in the last cell dp[N][W], representing the optimal solution.
Explanation of Dynamic Programming:

Dynamic programming is a technique that breaks down a problem into smaller subproblems, stores the solutions to these
subproblems, and combines them to solve the original problem. In this case, we store the maximum values for different
combinations of items and weights in the dp array. This allows us to avoid re-calculating solutions for overlapping subproblems,
resulting in an efficient solution.

Question 9

demYou are given an infinite supply of coins of each of denominations D = {D0, D1, D2, D3, ...... Dn-1}. You need to figure out the
total number of ways W, in which you can make a change for value V using coins of denominations from D. Print 0, if a change
isn't possible.o9

Optimal Approach:

The optimal approach to count the number of ways to make a change for value V using coins from D is based on dynamic
programming.

Dynamic Programming Approach:

1. Define Subproblems: For each coin denomination Di and change value V, define W(V, i) as the number of ways to make a
change for V using coins from D0 to Di.
2. Recurrence Relation:
3. If V < Di: W(V, i) = W(V, i-1) (cannot use Di)
4. If V = Di: W(V, i) = W(V, i-1) + 1 (use Di once)
5. If V > Di: W(V, i) = W(V-Di, i) + W(V, i-1) (use Di multiple times or not at all)
6. Base Case: W(0, i) = 1 (empty set of coins can make change for 0)

Algorithm:

1. Initialize W(0, i) = 1 for all i.


2. For each value V from 1 to n:
3. For each denomination Di from 0 to i-1:
Compute W(V, i) using the recurrence relation.

Time Complexity: O(V * n), where V is the change value and n is the number of coin denominations.

By filling up the dynamic programming table W, we can efficiently determine W(V, n), which represents the total number of
ways to make a change for V using coins from D.

Question 10

Given a rod of length ‘N’ units. The rod can be cut into different sizes and each size has a cost associated with it. Determine
the maximum cost obtained by cutting the rod and selling its pieces.

Optimal Approach:

The optimal approach for this problem is to use dynamic programming. We can define a table dp[i], where dp[i] represents the
maximum cost obtainable by cutting the rod into segments of length i.

Dynamic Programming Approach:

1. Initialize dp[0] = 0.
2. For each rod length i from 1 to N:
3. Iterate through all possible cut lengths j such that 1 <= j < i.
4. Calculate the cost of cutting the rod at length j: cost = dp[j] + dp[i - j] + price[i].
5. Update dp[i] with the maximum of the current value of dp[i] and the calculated cost.
6. Return dp[N].

How Dynamic Programming is Used:

Dynamic programming breaks the problem into smaller subproblems and stores the solutions to these subproblems in a table.
By iterating through the subproblems and using the stored solutions, the optimal solution for the original problem can be
computed efficiently. In this case, the table dp stores the maximum cost for each rod length, and the final solution is found by
returning dp[N].
Question 11

Given two strings, 'S' and 'T' with lengths 'M' and 'N', find the length of the 'Longest Common Subsequence'.

For a string 'str'(per se) of length K, the subsequences are the strings containing characters in the same relative order as they are
present in 'str,' but not necessarily contiguous. Subsequences contain all the strings of length varying from 0 to K.

Optimal Approach:

The optimal approach to finding the Longest Common Subsequence (LCS) between two strings is using dynamic programming.
This technique involves breaking the problem into smaller subproblems that can be solved independently.

A dynamic programming algorithm typically uses a table or matrix to store the solutions to these subproblems. In the case of
LCS, the table is 2-dimensional, with rows representing the characters of the first string ('S') and columns representing the
characters of the second string ('T'). The cell at the intersection of a row and column contains the length of the LCS between the
substrings of 'S' and 'T' up to those positions.

The algorithm fills the table from left to right and top to bottom, starting with an empty LCS (length 0) for empty substrings. If
the characters at the current positions of 'S' and 'T' are the same, the LCS is extended by one character, and the cell value is
incremented by 1. Otherwise, the cell value is set to the maximum of the values in the cells above and to the left, indicating the
LCS of the previous substrings.

Once the table is filled, the length of the LCS is given by the value in the last cell. This approach efficiently computes the LCS by
reusing results from previously solved subproblems, reducing the exponential complexity of a brute-force approach to O(M * N),
where M and N are the lengths of 'S' and 'T', respectively.

Question 12

Given two strings, ‘A’ and ‘B’. Return the shortest supersequence string ‘S’, containing both ‘A’ and
‘B’ as its subsequences. If there are multiple answers, return any of them. Note: A string 's' is a subsequence of string 't' if
deleting some number of characters from 't' (possibly 0) results in the string 's'.

Optimal Approach:

Dynamic programming offers an optimal approach to solve this problem. It involves building a 2D table, where the rows and
columns represent characters from string 'A' and 'B', respectively. Each cell of the table stores the length of the shortest
supersequence of the substrings of 'A' and 'B' ending at the corresponding characters.

The table is filled row-wise, starting with the empty string. For each cell, we consider two options:

1. Include the current character from 'A': If the current character in 'A' is not already included in the supersequence, we
add it and update the supersequence length.
2. Include the current character from 'B': If the current character in 'B' is not already included in the supersequence, we
add it and update the supersequence length.

The shortest supersequence length is found in the last cell of the table, and the actual supersequence can be constructed by
backtracking the table to find the characters that were added.

This approach solves the problem in O(mn) time complexity, where m and n are the lengths of strings 'A' and 'B', respectively. It
efficiently utilizes dynamic programming to find the shortest supersequence without considering redundant computations.

Question 13

A Subsequence of a string is the string that is obtained by deleting 0 or more letters from the string and keeping the rest of the
letters in the same order. We are given two strings, 'str' and 'sub'. Find the number of subsequences of 'str' which are equal to
'sub'. Since the answer can be very large, print it modulo 10 ^ 9 + 7.

Optimal Approach using Dynamic Programming:

We can construct a 2D matrix dp of size (m + 1) x (n + 1), where m is the length of str and n is the length of sub.

Dynamic Programming Recurrence Relation:

dp[i][j] represents the number of subsequences of str[1:i] that are equal to sub[1:j]. The recurrence relation is defined as
follows:

If str[i] == sub[j], then dp[i][j] = (dp[i-1][j] + dp[i-1][j-1]) % MOD. This means we can either skip or include str[i] to
form a subsequence that matches sub[1:j].
If str[i] != sub[j], then dp[i][j] = dp[i-1][j]. This means we skip str[i] since it cannot contribute to a matching
subsequence.

Base Cases:

dp[0][0] = 1, since the empty string is a subsequence of any string.


dp[i][0] = 1, for all i > 0, since the empty string is a subsequence of str[1:i].
dp[0][j] = 0, for all j > 0, since sub[1:j] cannot be a subsequence of the empty string.

Optimal Solution:

We fill the dp matrix using the recurrence relation and base cases. Finally, the result is dp[m][n], which represents the number of
subsequences of str that are equal to sub.

Question 14

You are given two strings 'S' and 'T' of lengths 'N' and 'M' respectively. Find the "Edit Distance" between the strings.

Edit Distance of two strings is the minimum number of steps required to make one string equal to the other. In order to do so,
you can perform the following three operations:

1. Delete a character
2. Replace a character with another one
3. Insert a character

Optimal Approach:

We can use Dynamic Programming to solve this problem efficiently. Create a 2D table 'dp' of size (N+1) x (M+1), where 'N' and 'M'
are the lengths of strings 'S' and 'T' respectively.

Dynamic Programming Steps:

1. Initialize 'dp[i][0]' to 'i' for 0 <= i <= N and 'dp[0][j]' to 'j' for 0 <= j <= M (representing the cost of transforming an empty
string to 'S' or 'T').

2. For each cell 'dp[i][j]' (where 1 <= i <= N and 1 <= j <= M):

If 'S[i]' equals 'T[j]', then 'dp[i][j] = dp[i-1][j-1]' (no operation needed).


Otherwise, consider the following three cases:
Deletion: 'dp[i][j] = dp[i-1][j] + 1'
Replacement: 'dp[i][j] = dp[i-1][j-1] + 1'
Insertion: 'dp[i][j] = dp[i][j-1] + 1'
Choose the case with the minimum cost.

3. The final result is stored in 'dp[N][M]', which represents the minimum number of operations (edit distance) required to
transform string 'S' to 'T'.

Explanation:

This approach leverages the overlapping subproblems and optimal substructure properties of the problem. By systematically
building up the 'dp' table, we can efficiently determine the edit distance between the two strings. Each cell in the table stores the
minimum cost of transforming a substring of 'S' to a substring of 'T' up to that point. By considering the possible operations and
choosing the one with the minimum cost, we gradually construct the solution for the entire strings.

Question 15

Given a text and a wildcard pattern of size N and M respectively, implement a wildcard pattern matching algorithm that finds if
the wildcard pattern is matched with the text. The matching should cover the entire text not partial text.

The wildcard pattern can include the characters ‘?’ and ‘*’

‘?’ – matches any single character ‘*’ – Matches any sequence of characters(sequence can be of length 0 or
more)

Optimal Approach:

Wildcard pattern matching can be efficiently solved using dynamic programming. We can create a 2D table to store whether the
wildcard pattern matches the text up to the corresponding indices.

Dynamic Programming Algorithm:

1. Initialize the table with dp[i][j] = false for all i and j, where i represents the index in the text and j represents the index
in the wildcard pattern.

2. If the first character in the text and the wildcard pattern match (or the wildcard pattern is '?'), set dp[0][0] = true.

3. If the first character in the wildcard pattern is '*', set dp[0][1] = true.

4. For each subsequent index i in the text and j in the wildcard pattern:

5. If the characters match (or the wildcard pattern is '?'), dp[i][j] = dp[i-1][j-1].

6. If the wildcard pattern is '*', dp[i][j] = dp[i-1][j] || dp[i][j-1].

7. After filling the table, check if dp[n-1][m-1] is true, where n is the length of the text and m is the length of the wildcard
pattern. If true, the pattern matches the text; otherwise, it doesn't.

Explanation of Dynamic Programming:

Dynamic programming breaks the problem down into smaller subproblems, which are represented by the entries in the table.
By solving these subproblems in a recursive manner, the algorithm builds up the solution to the original problem. The table
stores the results of the subproblems to avoid redundant calculations.

The time complexity of this algorithm is O(n * m), where n is the length of the text and m is the length of the wildcard pattern.
The space complexity is O(n * m) for the dynamic programming table.

Question 16

You are given an array/list 'prices' where the elements of the array represent the prices of the stock as they were yesterday and
indices of the array represent minutes. Your task is to find and return the maximum profit you can make by buying and selling
the stock. You can buy and sell the stock only once.

Note:

You can’t sell without buying first.

Optimal Approach:

Dynamic programming, a technique for solving complex problems by breaking them down into smaller subproblems with
overlapping solutions, can be effectively applied to solve this problem. We create a table 'profit' of size 'n', where 'n' is the length
of the input array 'prices'.

Dynamic Programming Table:

profit[i]: Represents the maximum profit achievable by selling the stock up to the 'i'th minute.

Transition Function:

For each minute 'i' (starting from the first minute), we compute the maximum profit as:

profit[i] = max({profit[i-1], prices[i] - min_price})

where 'min_price' is the minimum price encountered so far. This formula captures the choice of either continuing to hold the
stock or selling it for the maximum profit at minute 'i'.

Initialization:

profit[0] = 0
min_price = prices[0]

Time Complexity: O(n), where n is the length of the input array.

Space Complexity: O(n), for the profit table.

Question 17

You have been given stock values/prices for N number of days. Every i-th day signifies the price of a stock on that day. Your task
is to find the maximum profit which you can make by buying and selling the stocks.

Note : You may make as many transactions as you want but can not have more than one transaction at a time i.e, if you have the
stock, you need to sell it first, and then only you can buy it again.

This problem can be solved efficiently using dynamic programming. We define a 2D array dp where dp[i][0] represents the
maximum profit if we have not bought any stock on i-th day and dp[i][1] represents the maximum profit if we have bought a
stock on i-th day. We initialize dp[0][0] = 0 and dp[0][1] = -prices[0] since we can't buy a stock on the first day.

For the i-th day, if we haven't bought any stock, we have two options: either we buy the stock on the i-th day or we don't. If we
buy the stock, the profit would be -prices[i] since we are buying the stock at price prices[i]. If we don't buy the stock, the profit
would be the same as the previous day, i.e., dp[i-1][0].

If we have bought a stock on the i-th day, we have two options: either we sell the stock on the i-th day or we don't. If we sell the
stock, the profit would be prices[i] - dp[i-1][1] since we are selling the stock at price prices[i] and we bought it at price dp[i-1][1].
If we don't sell the stock, the profit would be the same as the previous day, i.e., dp[i-1][1].

Therefore, the recurrence relations for dp[i][0] and dp[i][1] are:

dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i]) dp[i][1] = max(dp[i-1][1], -prices[i])

We can compute the values of dp[i][0] and dp[i][1] for all i = 1, 2, ..., N. The maximum profit would be dp[N][0].

Question 18

You are Harshad Mehta’s friend. He told you the price of a particular stock for the next ‘n’ days. You are given an array
‘prices’ which such that ‘prices[i]’ denotes the price of the stock on the ith day. You don't want to do more than 2
transactions. Find the maximum profit that you can earn from these transactions.

Note 1. Buying a stock and then selling it is called one transaction. 2. You are not allowed to do multiple transactions at the same
time. This means you have to sell the stock before buying it again.

Dynamic Programming Approach:

Dynamic programming is an algorithm design paradigm that solves a complex problem by breaking it down into smaller,
simpler subproblems that can be solved recursively. In this case, the subproblems represent different states of the two
transactions that can be made.

The optimal substructure property of the problem allows us to solve each subproblem independently and combine the solutions
to obtain the overall optimal solution. Specifically, we can define a 2D array dp where dp[i][j] represents the maximum profit
that can be earned from the first i days of the stock's price considering j transactions.

The recurrence relation for dp is: dp[i][j] = max(dp[i-1][j], dp[i-1][j-1] + prices[i] - prices[k]) where k is the day on which
the stock was bought in the previous transaction.

The base cases are: dp[0][0] = 0 dp[i][0] = 0

By solving these subproblems in a bottom-up manner, we can construct the dp table and find the maximum profit in dp[n-1][2].
This approach ensures that we consider all possible combinations of buying and selling the stock, leading to an optimal solution.

Question 19

For a given array with N elements, you need to find the length of the longest subsequence from the array such that all the
elements of the subsequence are sorted in strictly increasing order. Strictly Increasing Sequence is when each term in the
sequence is larger than the preceding term

Optimal Approach:

A dynamic programming approach can be utilized to solve this problem. A dynamic programming array, dp, is initialized with a
length equal to the number of elements in the input array. Each element in the array represents the length of the longest strictly
increasing subsequence ending at that corresponding index in the input array.

The algorithm iterates through the input array and updates the dp array as follows: 1. For each element in the input array,
initialize its corresponding dp value to 1 (representing a subsequence of length 1). 2. For each subsequent element, iterate
through the elements before it. If the current element is greater than a previous element, and the length of the subsequence
ending at the previous element plus 1 is greater than the current dp value for the current element, update the dp value
accordingly.
The maximum value in the dp array represents the length of the longest strictly increasing subsequence in the input array.

This dynamic programming approach efficiently solves the problem by leveraging previously computed results to determine the
length of the longest strictly increasing subsequence. The complexity of the algorithm is O(N^2), where N is the number of
elements in the input array.

Question 20

Given an integer array ‘arr’ of length ‘n’, return the number of longest increasing subsequences in it. The longest
increasing subsequence(LIS) is the longest subsequence of the given sequence such that all subsequent elements are in strictly
increasing order.

Dynamic Programming Approach:

The dynamic programming approach calculates the LIS length for every element and stores it in a table. Initially, the LIS length
for each element is set to 1. Then, for each element, we iterate through all previous elements and check if the current element is
greater than the previous element and if it is, we update the LIS length for the current element to be the maximum of its current
length and the LIS length of the previous element + 1. Finally, we return the maximum LIS length among all elements.

This approach takes O(n^2) time, where n is the length of the array. It uses dynamic programming by storing and updating the
LIS length for each element based on the LIS lengths of previous elements, allowing us to calculate the longest increasing
subsequence and its count efficiently.

Question 21

Given a chain of matrices A1, A2, A3,.....An. Your task is to find out the minimum cost to multiply these matrices. The cost of
matrix multiplication is defined as the number of scalar multiplications. A Chain of matrices A1, A2, A3,.....An is represented by a
sequence of numbers in an array ‘arr’ where the dimension of 1st matrix is equal to arr[0] * arr[1] , 2nd matrix is arr[1] *
arr[2], and so on.

For example: For arr[ ] = { 10, 20, 30, 40}, matrix A1 = [10 * 20], A2 = [20 * 30], A3 = [30 * 40]

Scalar multiplication of matrix with dimension 10 * 20 is equal to 200.

Minimum Matrix Multiplication Cost

The problem of finding the minimum cost of multiplying a chain of matrices can be solved efficiently using dynamic
programming. The key observation is that the solution to the subproblems is reusable. The cost of multiplying a subchain of
matrices from i to j can be computed regardless of how the matrices before i and after j are multiplied.

Dynamic Programming Approach:

1. Create a table dp[i][j] to store the minimum cost of multiplying the subchain from matrix i to j.
2. Initialize the diagonal elements of dp to 0 (cost of multiplying a single matrix is 0).
3. For each subchain of length greater than 1:
4. For each split point k between i and j:
Compute the cost of multiplying the submatrices i to k and k+1 to j.
Add this cost to the cost of multiplying the split submatrices optimally.
Choose the minimum cost among all possible split points.
5. Store the minimum cost for the subchain in dp[i][j].

The final result is stored in dp[1][n], where n is the total number of matrices.

Advantages of Dynamic Programming:

Dynamic programming avoids recomputing overlapping subproblems by storing intermediate results in a table. This
significantly reduces the runtime complexity of the algorithm, making it suitable for large matrix chains.

Question 22

There are ‘N’ diamonds in a mine. The size of each diamond is given in the form of integer array ‘A’. If the miner
mines a diamond, then he gets 'size of previous unmined diamond * size of currently mined diamond * size of next unmined
diamond' number of coins. If there isn’t any next or previous unmined diamond then their size is replaced by 1 while
calculating the number of coins. Vladimir, a dumb miner was assigned the task to mine all diamonds. Since he is dumb he asks
for your help to determine the maximum number of coins that he can earn by mining the diamonds in an optimal order.
Let's define a dp array to store the maximum coins earned for each possible ending point in the mine. Here, dp[i] represents the
maximum coins earned by mining diamonds up to index 'i'.

We can use dynamic programming approach to compute the value of dp[i]. To calculate dp[i], we consider mining diamond at
position 'i'. If we decide to mine the diamond at position 'i', we get coins as a product of sizes of two adjacent diamonds (i.e., A[i-
1]A[i]A[i+1]). After including these coins, we move to the next unmined diamond (i.e., i+2) to maximize the earnings. This means
that dp[i] can be calculated as follows:

dp[i] = max(dp[i], dp[i-2] + (A[i-1]A[i]A[i+1]))

So the final result will be the maximum value among all dp values (i.e., result = max(dp[0],dp[1],...,dp[N-1])).

Question 23

You are given an expression 'exp' in the form of a string where operands will be : (TRUE or FALSE), and operators will be : (AND,
OR or XOR). Now you have to find the number of ways we can parenthesize the expression such that it will evaluate to TRUE. As
the answer can be very large, return the output modulo 1000000007.

We can use dynamic programming to solve this problem. We define a 2D array dp where dp[i][j] represents the number of ways
to parenthesize the substring from index i to index j such that it evaluates to TRUE. We can compute dp[i][j] using the following
recurrence relation:

dp[i][j] = sum(dp[i][k] * dp[k+1][j] for all k such that i <= k < j and the operator between the substrings at indices i and
k+1 is AND) + sum(dp[i][k] * dp[k+1][j] for all k such that i <= k < j and the operator between the substrings at indices i
and k+1 is OR) + sum(dp[i][k] * dp[k+1][j] for all k such that i <= k < j and the operator between the substrings at indices
i and k+1 is XOR)

We can compute the value of dp[i][j] for all i and j in O(n^3) time, where n is the length of the string.

The recurrence relation can be understood as follows. If the operator between the substrings at indices i and k+1 is AND, then
the substring from index i to index j will evaluate to TRUE if and only if both substrings from index i to index k and from index
k+1 to index j evaluate to TRUE. Similarly, if the operator between the substrings at indices i and k+1 is OR, then the substring
from index i to index j will evaluate to TRUE if at least one of the substrings from index i to index k and from index k+1 to index j
evaluates to TRUE. Finally, if the operator between the substrings at indices i and k+1 is XOR, then the substring from index i to
index j will evaluate to TRUE if exactly one of the substrings from index i to index k and from index k+1 to index j evaluate to
TRUE.

Question 24

A matrix 'arr' with 'n' rows and 'm' columns is given. Count the number of square submatrices in matrix ‘arr’ with all
elements equal to 1. A square matrix is a matrix with square dimensions.

1. Initialize a 2D dp array: Create a 2D array dp of size (n+1)x(m+1), where dp[i][j] stores the count of square submatrices
with all 1s ending at position (i-1, j-1) in the original matrix.

2. Populate the dp array: Iterate over the original matrix and for each cell (i, j):

3. If arr[i][j] == 0, then dp[i][j] = 0.

4. Else, dp[i][j] = min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) + 1.

5. Return the result: The total count of square submatrices with all 1s is given by the sum of all elements in the dp array,
which can be calculated as the sum of the last row and last column of dp.

Dynamic Programming Approach:

Dynamic programming is used in this algorithm by building up the solution incrementally. We start by finding the count of 1x1
square submatrices, then 2x2, and so on. The key observation is that the count of larger square submatrices depends on the
count of smaller square submatrices. By storing these counts in the dp array, we can efficiently compute the final result without
having to recompute the counts for smaller submatrices.

Graphs
Question 1

You are given a connected undirected graph. Perform a Depth First Traversal of the graph. Note: Use the recursive approach to
find the DFS traversal of the graph starting from the 0th vertex from left to right according to the graph.

To perform a Depth First Traversal (DFS) of a graph, we can use a recursive approach. We start by creating a visited array to
keep track of which vertices have already been visited. We then start a recursive function from the first vertex, and visit all its
adjacent vertices that have not yet been visited. We continue this process until all vertices in the graph have been visited.

The optimal approach for DFS is to use a stack data structure to keep track of the vertices that need to be visited. We start by
pushing the first vertex onto the stack, and then while the stack is not empty, we pop the top vertex off the stack and visit it. We
then push all of its adjacent vertices that have not yet been visited onto the stack, and repeat the process until the stack is empty.

Question 2

Rotten Oranges

Given a grid of dimension nxm where each cell in the grid can have values 0, 1 or 2 which has the following meaning: 0 : Empty
cell 1 : Cells have fresh oranges 2 : Cells have rotten oranges

We have to determine what is the earliest time after which all the oranges are rotten. A rotten orange at index [i,j] can rot other
fresh orange at indexes [i-1,j], [i+1,j], [i,j-1], [i,j+1] (up, down, left and right) in unit time.

We can apply the BFS approach to solve this problem. We can start by initializing a queue with all the cells that have rotten
oranges. In each iteration of BFS, we can visit all the neighbors of the current cell in the queue and check if they are fresh
oranges. If so, we can add them to the queue and mark them as rotten. We can also keep track of the maximum time taken to rot
all the oranges. Once the queue is empty or all the oranges are rotten, we can return the maximum time. The time complexity of
this approach is O(n * m), where n and m are the dimensions of the grid.

Question 3

Undirected Graph Cycle using BFS

Given an undirected graph with V vertices labelled from 0 to V-1 and E edges, check whether it contains any cycle or not. Graph
is in the form of adjacency list where adj[i] contains all the nodes ith node is having edge with.

We can use BFS to detect cycles in an undirected graph. The idea is to start from any vertex and mark it as visited. Then, for each
adjacent vertex of the current vertex, if it is not visited, we mark it as visited and enqueue it in a queue. If it is already visited,
then there is a cycle in the graph. We can repeat this process for all vertices in the graph. If we don't find any cycle after visiting
all vertices, then the graph is acyclic. The time complexity of this approach is O(V+E), where V is the number of vertices and E is
the number of edges in the graph.

Question 4

Undirected Graph Cycle Using DFS

Given an undirected graph with V vertices labelled from 0 to V-1 and E edges, check whether it contains any cycle or not. Graph
is in the form of adjacency list where adj[i] contains all the nodes ith node is having edge with.

To efficiently check for cycles in an undirected graph using DFS, we employ the disjoint set union (DSU) data structure. We
initialize a DSU with V nodes, where each node initially represents a separate component. As we perform DFS, we check if the
current node and its adjacent node belong to the same component. If they do, it indicates the presence of a cycle. Otherwise, we
merge the components of the current node and its adjacent node using the DSU. This ensures that all nodes in a connected
component are represented by the same component in the DSU. If, during the entire DFS traversal, no two nodes in the same
component are encountered, it means the graph contains no cycles.

Question 5

Number of Distinct Islands

Given a boolean 2D matrix grid of size n * m. You have to find the number of distinct islands where a group of connected 1s
(horizontally or vertically) forms an island. Two islands are considered to be distinct if and only if one island is not equal to
another (not rotated or reflected).

A DFS approach can be used to solve this problem. We start by iterating over the grid and whenever we find a 1, we start a DFS
to explore the island. During the DFS, we keep track of the shape of the island by recording the sequence of directions we take
(up, down, left, or right). Once we have explored the entire island, we add the shape to a set to keep track of the distinct islands.
We continue this process until we have explored all the islands in the grid.
This approach has a time complexity of O(n*m), where n and m are the number of rows and columns in the grid.

Question 6

Given an adjacency list of a graph adj of V no. of vertices having 0 based index. Check whether the graph is bipartite or not using
BFS.

To check if a graph is bipartite using BFS, we can assign a color (0 or 1) to each vertex and perform a BFS starting from any
vertex. While performing the BFS, we assign a color to the current vertex and check if the adjacent vertices have the same color.
If adjacent vertices have the same color, then the graph is not bipartite. We continue this process until all vertices are visited. If
no contradictions are found, then the graph is bipartite. This algorithm runs in O(V + E) time complexity, where V is the number
of vertices and E is the number of edges.

Question 7

Given an adjacency list of a graph adj of V no. of vertices having 0 based index. Check whether the graph is bipartite or not using
BFS.

Optimal Approach (BFS):

1. Initialize color array color with -1 for all vertices (not visited).
2. Use a queue to perform BFS.
3. While the queue is not empty:
4. Dequeue a vertex u and set its color to 1.
5. For each adjacent vertex v of u:
If v is not visited, enqueue v and set its color to 1 - color[u].
If v is already visited and color[v] == color[u], the graph is not bipartite.
6. If BFS completes without finding any color conflicts, the graph is bipartite.

Question 8

Using DFS Given a Directed Graph with V vertices (Numbered from 0 to V-1) and E edges, check whether it contains any cycle or
not.

Depth-First Search (DFS) can be used to detect cycles in a directed graph. Start from any vertex and perform DFS. While
performing DFS, if you encounter a vertex that has already been visited in the current DFS traversal, then there is a cycle in the
graph. Maintain a visited array to keep track of visited vertices. If no such vertex is encountered during DFS traversal, then the
graph doesn't contain any cycles.

Question 9

Using DFS Given an adjacency list for a Directed Acyclic Graph (DAG) where adj_list[i] contains a list of all vertices j such that
there is a directed edge from vertex i to vertex j, with V vertices and E edges, your task is to find any valid topological sorting of
the graph.

Topological Sort using DFS (Depth-First Search):

Perform a Depth-First Search (DFS) on the DAG, starting from any vertex. While traversing, maintain a stack. When a vertex is
visited, push it onto the stack. Once all its neighbors are visited, pop it from the stack. Repeat this process until all vertices have
been visited. The order in which the vertices are popped from the stack represents a valid topological sorting of the DAG.

Question 10

Kahns Algorithm Given an adjacency list for a Directed Acyclic Graph (DAG) where adj_list[i] contains a list of all vertices j such
that there is a directed edge from vertex i to vertex j, with V vertices and E edges, your task is to find any valid topological
sorting of the graph.

Kahn's algorithm is a topological sorting algorithm that works by iteratively removing vertices with no incoming edges from the
graph. It maintains a queue of vertices with no incoming edges, and at each step, it removes a vertex from the queue and adds it
to the topological ordering. If the queue becomes empty before all vertices have been added to the topological ordering, then the
graph contains a cycle and no topological ordering is possible. Kahn's algorithm has a time complexity of O(V+E), where V is the
number of vertices and E is the number of edges in the graph. It is an optimal algorithm for topological sorting of DAGs.

Question 11
Course Schedule

There are a total of N tasks, labeled from 0 to N-1. Some tasks may have prerequisites, for example to do task 0 you have to first
complete task 1, which is expressed as a pair: [0, 1] Given the total number of tasks N and a list of prerequisite pairs P, find if it is
possible to finish all tasks.

To determine if it is possible to finish all tasks, we can use a topological sort algorithm. This algorithm involves: 1. Creating a
directed graph where each task is a node and each prerequisite is an edge pointing from the prerequisite task to the dependent
task. 2. Performing a depth-first search (DFS) on the graph, visiting each node exactly once and assigning a temporary mark to
each node to indicate its status as unvisited, temporarily visited, or permanently visited. 3. If any node is permanently visited
more than once, a cycle exists in the graph and it is not possible to finish all tasks. 4. If all nodes are permanently visited exactly
once, there are no cycles in the graph and it is possible to finish all tasks.

Question 12

Prerequisite Tasks

There are a total of n tasks you have to pick, labelled from 0 to n-1. Some tasks may have prerequisite tasks, for example to pick
task 0 you have to first finish tasks 1, which is expressed as a pair: [0, 1] Given the total number of n tasks and a list of
prerequisite pairs of size m. Find a ordering of tasks you should pick to finish all tasks. Note: There may be multiple correct
orders, you just need to return any one of them. If it is impossible to finish all tasks, return an empty array. Driver code will
print "No Ordering Possible", on returning an empty array. Returning any correct order will give the output as 1, whereas any
invalid order will give the output 0.

Optimal Solution: Use Depth First Search (DFS) to traverse the graph of tasks. Start with each task and recursively visit all its
prerequisites before visiting itself. If a cycle is detected (i.e., a task is visited again after visiting all its prerequisites), it implies
that there is no valid ordering. Otherwise, keep track of the order in which the tasks are visited and return it as the ordering.
This approach ensures that all the prerequisites of a task are completed before the task itself.

Question 13

Alien Dictionary

Given a sorted dictionary of an alien language having N words and k starting alphabets of standard dictionary. Find the order of
characters in the alien language. Note: Many orders may be possible for a particular test case, thus you may return any valid
order and output will be 1 if the order of string returned by the function is correct else 0 denoting incorrect string returned.

Optimal Approach:

We can use Topological Sort to find the order of characters in an alien dictionary. Initially, we create a directed graph with
characters as nodes and edges between consecutive characters in each word. We then perform a topological sort on this graph,
which ensures that for any two adjacent characters in any word, the character with a higher lexicographical order comes after
the other in the sorted order. The result of the topological sort gives us the order of characters in the alien language.

Time Complexity: O(N + K), where N is the total number of characters in all words and K is the number of starting alphabets.

Question 14

Shortest path in Directed Acyclic Graph

Given a Directed Acyclic Graph of N vertices from 0 to N-1 and a 2D Integer array(or vector) edges[ ][ ] of length M, where there
is a directed edge from edge[i][0] to edge[i][1] with a distance of edge[i][2] for all i.

Find the shortest path from src(0) vertex to all the vertices and if it is impossible to reach any vertex, then return -1 for that
vertex.

Optimal Approach:

The optimal approach is to use Dynamic Programming with Topological Sorting. First, perform Topological Sorting to obtain a
linear ordering of the vertices. Then, traverse the graph in the topological order and update the shortest distances as follows: 1.
Initialize the distance of the source vertex to 0. 2. For each vertex v in topological order: - For each outgoing edge (v, u) with
weight w: - If the distance from the source to v plus w is less than the current distance to u: - Update the distance to u to be the
sum of the distance to v and w. 3. Return the distance array.

This approach has a time complexity of O(V + E), where V is the number of vertices and E is the number of edges, and a space
complexity of O(V).
Question 15

Dijkstra Algorithm

Given a weighted, undirected and connected graph of V vertices and an adjacency list adj where adj[i] is a list of lists containing
two integers where the first integer of each list j denotes there is edge between i and j , second integers corresponds to the
weight of that edge . You are given the source vertex S and You to Find the shortest distance of all the vertex's from the source
vertex S. You have to return a list of integers denoting shortest distance between each node and Source vertex S.

Note: The Graph doesn't contain any negative weight cycle.

Dijkstra's algorithm is a greedy algorithm that solves the single-source shortest path problem on a weighted, directed graph. The
algorithm starts at the source node and iteratively explores the graph, adding the shortest path to each node to a set of known
shortest paths. The algorithm terminates when all nodes have been added to the set of known shortest paths.

The time complexity of Dijkstra's algorithm is O(V^2), where V is the number of vertices in the graph. This is because the
algorithm iterates over all of the vertices in the graph, and for each vertex, it explores all of its neighbors.

Question 16

Distance from the Source (Bellman-Ford Algorithm)

Given a weighted and directed graph of V vertices and E edges, Find the shortest distance of all the vertex's from the source
vertex S. If a vertices can't be reach from the S then mark the distance as 10^8. Note: If the Graph contains a negative cycle then
return an array consisting of only -1.

The Bellman-Ford algorithm is used to calculate the shortest distance from a source vertex to all other vertices in a weighted and
directed graph. It is similar to Dijkstra's algorithm but can handle negative weight edges. The algorithm works by iteratively
relaxing each edge in the graph, updating the distance to each vertex as it goes. If the algorithm finds a negative-weight cycle, it
will return an array of -1s, indicating that the graph contains a negative-weight cycle. The time complexity of the Bellman-Ford
algorithm is O(VE), where V is the number of vertices and E is the number of edges in the graph.

Question 17

Floyd Warshall

The problem is to find the shortest distances between every pair of vertices in a given edge-weighted directed graph. The graph
is represented as an adjacency matrix of size n*n. Matrix[i][j] denotes the weight of the edge from i to j. If Matrix[i][j]=-1, it
means there is no edge from i to j. Note : Modify the distances for every pair in-place.

Floyd-Warshall is a dynamic programming algorithm that solves the all-pairs shortest path problem in a weighted directed
graph. It works by iteratively updating the distances between all pairs of vertices until the shortest distances are found. Initially,
the distances are set to the weights of the edges between the vertices. Then, for each intermediate vertex k, the algorithm checks
if the path from vertex i to vertex j through vertex k is shorter than the current shortest path. If it is, the distance from i to j is
updated to the shorter path. This process is repeated until the distances between all pairs of vertices are the shortest possible.
The time complexity of the Floyd-Warshall algorithm is O(V^3), where V is the number of vertices in the graph.

Question 18

Minimum Spanning Tree

Given a weighted, undirected, and connected graph with V vertices and E edges, your task is to find the sum of the weights of the
edges in the Minimum Spanning Tree (MST) of the graph. The graph is represented by an adjacency list, where each element
adj[i] is a vector containing pairs of integers. Each pair represents an edge, with the first integer denoting the endpoint of the
edge and the second integer denoting the weight of the edge

Optimal Approach:

Kruskal's algorithm is an optimal approach for finding the MST. It works by sorting all edges in ascending order of weight and
iteratively adding them to the MST if they do not create a cycle. To check for cycles, we use the Union-Find data structure. The
algorithm has a time complexity of O(E log V), where E is the number of edges and V is the number of vertices.

Question 19

Strongly Connected Components (Kosaraju's Algo)


Given a Directed Graph with V vertices (Numbered from 0 to V-1) and E edges, Find the number of strongly connected
components in the graph.

Kosaraju's algorithm can be used to find the number of strongly connected components in a directed graph. The algorithm
works in two phases. In the first phase, a depth-first search (DFS) is performed on the graph to compute the finishing times of all
the vertices. In the second phase, the graph is reversed, and another DFS is performed on the reversed graph starting from the
vertices with the highest finishing times. The number of strongly connected components is equal to the number of DFS trees
obtained in the second phase. The time complexity of Kosaraju's algorithm is O(V + E), where V is the number of vertices and E is
the number of edges in the graph.

Question 20

Critical Connections in a Network

There are n servers numbered from 0 to n - 1 connected by undirected server-to-server connections forming a network where
connections[i] = [ai, bi] represents a connection between servers ai and bi. Any server can reach other servers directly or
indirectly through the network.

A critical connection is a connection that, if removed, will make some servers unable to reach some other server.

Return all critical connections in the network in any order.

Optimal Approach:

Use Tarjan's algorithm to perform a depth-first search on the network, assigning each server a discovery time and a low time.
A discovery time represents the order in which the server was discovered during the search, while a low time represents the
lowest discovery time of any server reachable from the current server.

If a connection between two servers has a low time for one server greater than the discovery time of the other server, then it is a
critical connection because removing it would disconnect the two servers.

Time Complexity: O(E + V), where E is the number of connections and V is the number of servers.

Question 21

Articulation Point - I

Given an undirected connected graph with V vertices and adjacency list adj. You are required to find all the vertices removing
which (and edges through it) disconnects the graph into 2 or more components and return it in sorted manner. Note: Indexing is
zero-based i.e nodes numbering from (0 to V-1). There might be loops present in the graph.

To find articulation points in a graph, you can use Tarjan's algorithm. This algorithm uses depth-first search (DFS) to traverse the
graph and assign depth values to each vertex. It also keeps track of the lowest depth value that can be reached from each vertex.
If the lowest depth value for a vertex is greater than its depth value, then that vertex is an articulation point. The algorithm can
be implemented in linear time and space complexity.
Arrays
Question 1

Problem Statement : Given a matrix if an element in the matrix is 0 then you will have to set its entire column and row to 0 and
then return the matrix.

Brute Force Solution:

Iterate through each element in the matrix. If the element is 0, iterate through its row and column and set all elements in those
row and column to 0. Repeat this process for each element in the matrix.

Optimized Solution:

Use two arrays, one for rows and one for columns, to track which rows and columns contain a 0. Iterate through the matrix
once. If an element is 0, set the corresponding row and column in the tracking arrays to True. Iterate through the matrix again. If
a row or column is marked as True, set all elements in that row or column to 0. This approach avoids iterating through rows and
columns multiple times, improving efficiency.

Question 2

Problem Statement : This problem has 3 variations. They are stated below:

Variation 1: Given row number r and column number c. Print the element at position (r, c) in Pascal’s triangle.

Variation 2: Given the row number n. Print the n-th row of Pascal’s triangle.

Variation 3: Given the number of rows n. Print the first n rows of Pascal’s triangle.

Brute Force Solution:

For variation 1, calculate the value at (r, c) using the formula nCr = n! / (r! * (n-r)!). For variation 2, print each value in the row by
using the formula for variation 1. For variation 3, print each row by using the formula for variation 1.

Optimized Solution:

Instead of calculating each value in Pascal's triangle directly, use the following formula to calculate the value at (r, c): P(r, c) =
P(r-1, c-1) + P(r-1, c). This significantly reduces the number of calculations needed. To print the first n rows of Pascal's triangle,
initialize the first row with [1] and use the formula to calculate subsequent rows.

Question 3

Problem Statement : Given an array Arr[] of integers, rearrange the numbers of the given array into the lexicographically next
greater permutation of numbers.

If such an arrangement is not possible, it must rearrange to the lowest possible order (i.e., sorted in ascending order).

Brute Force Solution: A straightforward approach is to generate all possible permutations of the array and find the
lexicographically next greater permutation. Time complexity is O(n!*n), where n is the size of the array.

Optimized Solution: A more efficient approach is to use the following steps:

1. Find the longest non-increasing suffix from the end of the array.
2. Find the pivot element in this suffix that is smaller than the element on its right side.
3. Swap the pivot element with the smallest element on its right side in the suffix.
4. Reverse the suffix.

This approach has a time complexity of O(n).

Question 4

Problem Statement : Given an integer array arr, find the contiguous subarray (containing at least one number) which has the
largest sum and returns its sum and prints the subarray.

Brute Force:

The brute force approach involves finding all possible contiguous subarrays and calculating their sums. The subarray with the
largest sum is then returned. This approach has a time complexity of O(n^3) (worst case), as it considers all possible contiguous
subarrays.

Optimized Solution (Kadane's Algorithm):

Kadane's algorithm provides an efficient O(n) solution for finding the maximum sum contiguous subarray. It starts with an
empty subarray, and as it iterates through the array, it adds elements to the subarray as long as the sum is positive. If the sum
becomes negative, it discards the current subarray and starts a new one. At the end of the iteration, the algorithm returns the
maximum sum of all subarrays examined.

Question 5

Problem Statement : Given an array consisting of only 0s, 1s, and 2s. Write a program to in-place sort the array without using
inbuilt sort functions. ( Expected: Single pass-O(N) and constant space)

Brute Force Approach:

The brute force approach to sort an array consisting only of 0s, 1s, and 2s is to use two nested loops. The outer loop iterates over
the elements of the array, and the inner loop compares each element to the next element. If the order is incorrect, then the
elements are swapped. This approach is inefficient because it has a time complexity of O(n^2) and a space complexity of O(1).

Optimized Approach:

The optimized approach to sort an array consisting only of 0s, 1s, and 2s is to use the "Dutch national flag" partitioning
algorithm. This algorithm works by dividing the array into three sections: the left section contains all the 0s, the middle section
contains all the 1s, and the right section contains all the 2s. The algorithm iterates over the elements of the array and maintains
two pointers: a "left" pointer that points to the first element of the middle section, and a "right" pointer that points to the last
element of the right section. The algorithm then iterates over the elements of the array, and for each element, it checks if it is
equal to 0, 1, or 2. If the element is equal to 0, then it is swapped with the element at the left pointer, and the left pointer is
incremented. If the element is equal to 1, then it is left in its current position. If the element is equal to 2, then it is swapped with
the element at the right pointer, and the right pointer is decremented. The algorithm continues until the left pointer is greater
than or equal to the right pointer. This approach is efficient because it has a time complexity of O(n) and a space complexity of
O(1).

Question 6

Problem Statement : You are given an array of prices where prices[i] is the price of a given stock on an ith day.

You want to maximize your profit by choosing a single day to buy one stock and choosing a different day in the future to sell that
stock. Return the maximum profit you can achieve from this transaction. If you cannot achieve any profit, return 0

Brute Force Solution:

The brute force solution is to iterate over all possible pairs of days and compute the profit for each pair. The maximum profit
among all pairs is the answer. The time complexity of this approach is O(n^2), where n is the length of the array.

Optimized Solution:

The optimized solution is based on the observation that the maximum profit can be achieved by buying the stock on the day
with the lowest price and selling it on the day with the highest price. To find these days, we can maintain two variables,
min_price and max_profit, while iterating over the array. The min_price variable stores the minimum price seen so far, and the
max_profit variable stores the maximum profit that can be achieved by selling the stock on the current day.

The time complexity of this approach is O(n), where n is the length of the array.

Question 7

Problem Statement : Given a matrix, your task is to rotate the matrix 90 degrees clockwise.

Brute Force Approach:

The brute force approach to rotate a matrix involves creating a new matrix that is the transpose of the original matrix and then
reversing each row. Time Complexity - O((n^2)) Auxiliary Space - O((n^2))

Optimized Solution using Transpose and Reverse (or Cyclic Rotation):

Transpose the original matrix. Now, traverse the transpose and reverse all rows. Time Complexity - O((n^2)) Auxiliary Space -
O(1)
Question 8

Problem Statement : Given an array of intervals, merge all the overlapping intervals and return an array of non-overlapping
intervals.

Brute Force Solution: Iterate through the array of intervals and for each interval, check if it overlaps with any of the
subsequent intervals. If there is an overlap, merge them together by creating a new interval with the start time as the minimum
start time of the overlapping intervals and the end time as the maximum end time of the overlapping intervals. Repeat this
process until there are no more overlapping intervals.

Optimized Solution using Sorting: Sort the intervals based on their start time. This ensures that the intervals are in order and
makes it easier to merge overlapping intervals. Iterate through the sorted array of intervals and compare each interval with the
next interval. If the current interval overlaps with the next interval, merge them together. Continue this process until you reach
the end of the array.

Question 9

Problem Statement : Given two sorted arrays arr1[] and arr2[] of sizes n and m in non-decreasing order. Merge them in sorted
order. Modify arr1 so that it contains the first N elements and modify arr2 so that it contains the last M elements.

Brute Force Solution: The simplest approach is to create a new array of size n + m, copy elements of arr1[] into it, and then
copy elements of arr2[] into the same array. Sort the new array and then copy the first n elements into arr1[] and the last m
elements into arr2[]. This approach takes O(n + m) time.

Optimized Solution: This approach is based on the fact that the arrays are already sorted. We start by comparing the first
elements of the two arrays and copy the smaller element into the output array. We then move to the next element of the array
from which we copied the element. We repeat this process until we have copied all the elements from both the arrays. This
approach takes O(n + m) time.

Question 10

Problem Statement : Given an array of N + 1 size, where each element is between 1 and N. Assuming there is only one duplicate
number, your task is to find the duplicate number.

Brute Force (O(N^2)): This approach systematically compares each element with every other element in the array. For each
pair of elements, it checks if they are equal. If a duplicate is found, the algorithm terminates and returns the duplicate value.

Optimized Solution (O(N) and O(1) Space): The optimized solution exploits the fact that all elements in the array are between
1 and N. It uses the "tortoise and hare" algorithm. Two pointers, a slow pointer (tortoise) and a fast pointer (hare), start at the
first element of the array. The tortoise moves one step at a time, while the hare moves two steps at a time. If the hare and
tortoise meet at any point in the array, there is a loop, indicating a duplicate number. The algorithm then finds the duplicate by
starting the hare at the beginning of the array and moving it one step at a time until it meets the tortoise again. The meeting
point is the duplicate number.

Question 11

Problem Statement : You are given a read-only array of N integers with values also in the range [1, N] both inclusive. Each
integer appears exactly once except A which appears twice and B which is missing. The task is to find the repeating and missing
numbers A and B where A repeats twice and B is missing.

Brute Force Approach:

Iterate through the array, using a hash table to keep track of the counts of each element. If an element count reaches 2, then it is
the repeating element (A), and if an element count is 0, then it is the missing element (B). This approach has a time complexity of
O(N), where N is the size of the array.

Optimized Approach (Sum and XOR):

1. Find the sum of all elements in the array (S) and the sum of all elements from 1 to N (S1). The missing element B can be
found as B = S1 - S.
2. XOR all elements in the array (X) with the numbers from 1 to N (X1). The result X ^ X1 will be the repeating element A, as all
the unique elements will cancel each other out.
3. To confirm that A repeats twice and B is missing, check if X = -A and B = N + 1 - A.

This optimized approach has a time complexity of O(N) and a constant space complexity.

Question 12
Problem Statement : Given an array of N integers, count the inversion of the array (using merge-sort).

What is an inversion of an array? Definition: for all i & j < size of array, if i < j then you have to find pair (A[i],A[j]) such that A[j]
< A[i].

Brute Force Solution: Iterate through all pairs of elements in the array and count the number of inversions (pairs where the
second element is smaller than the first). Time complexity: O(N^2).

Optimized Solution (Merge Sort): Merge sort the array. While merging two sorted subarrays, count the number of inversions
that occur when elements from the first subarray are greater than elements from the second subarray. This requires keeping
track of the indices of elements in the subarrays being merged. Time complexity: O(N log N).

Question 13

Problem Statement : You have been given a 2-D array 'mat' of size 'N x M' where 'N' and 'M' denote the number of rows and
columns, respectively. The elements of each row are sorted in non-decreasing order. Moreover, the first element of a row is
greater than the last element of the previous row (if it exists). You are given an integer ‘target’, and your task is to find if it
exists in the given 'mat' or not.

Brute Force Approach:

This approach involves searching for the target in each row of the matrix sequentially. For each row, we can perform a linear
search to determine if the target is present in that row. If the target is found in any of the rows, the search is terminated, and we
return 'true'; otherwise, we return 'false.' The time complexity of this approach is O(N * M), where N represents the number of
rows and M represents the number of columns in the matrix.

Optimized Approach:

The optimized approach takes advantage of the sorted nature of the matrix. We start from the top-right corner of the matrix. If
the target is greater than the current element, we move one step down the matrix since the elements in each row are sorted in
non-decreasing order. If the target is smaller than the current element, we move one step left in the matrix since the first
element of each row is greater than the last element of the previous row. We continue this process until we find the target or
reach the end of the matrix. The time complexity of this optimized approach is O(N + M), which is significantly better than the
brute force approach, especially for large matrices.

Question 14

Problem Statement : Given a double x and integer n, calculate x raised to power n. Basically Implement pow(x, n).

Brute Force: The straightforward approach is to multiply x by itself n times. For example, to calculate 2^3, we multiply 2 by
itself 3 times (2 * 2 * 2 = 8). This approach has a time complexity of O(n), as the number of multiplications increases linearly with
n.

Optimized Solution: A more efficient solution is to use the divide-and-conquer algorithm. It recursively breaks down the
exponent n into smaller chunks and then combines the results. For example, to calculate 2^3, we can compute 2^2 (which is 4)
and then multiply the result by 2. This approach has a time complexity of O(log n), as the exponent n is repeatedly halved with
each recursive call.

Question 15

Problem Statement : Given an array of N integers, write a program to return an element that occurs more than N/2 times in the
given array. You may consider that such an element always exists in the array.

Brute Force Solution:

The brute force solution involves iterating over the array and checking the frequency of each element. The element with the
maximum frequency is the majority element. The time complexity of this approach is O(N^2), where N is the size of the array.

Optimized Solution (Boyer-Moore Majority Vote Algorithm):

The Boyer-Moore Majority Vote Algorithm takes advantage of the fact that the majority element appears more than N/2 times. It
works by maintaining two variables, candidate and count. The candidate variable stores the current candidate for the majority
element, and the count variable stores the number of times the candidate has been seen. The algorithm iterates over the array
and updates candidate and count based on the following rules:

If count is 0, then the current element becomes the candidate, and count is incremented to 1.
If the current element is the candidate, then count is incremented to 1.
If the current element is not the candidate, then count is decremented to 1.
At the end of the algorithm, the candidate is the majority element. The time complexity of this algorithm is O(N), and it has
constant space complexity. Therefore, the Boyer-Moore Majority Vote Algorithm is more efficient than the brute force solution.

Question 16

Problem Statement : Given an array of N integers. Find the elements that appear more than N/3 times in the array. If no such
element exists, return an empty vector.

Brute Force: One simple approach is to iterate over the array and count the occurrences of each element. If an element's count
exceeds N/3, add it to the result. However, this approach has a time complexity of O(N^2) and is not efficient for large arrays.

Optimized Solution (Moore's Voting Algorithm): This algorithm works in linear time (O(N)). It uses two variables, candidate
and count, initialized to -1 and 0, respectively. It iterates over the array and updates candidate and count as follows:

If count is 0, candidate is assigned the current element, and count is set to 1.


If candidate is equal to the current element, count is incremented.
Otherwise, count is decremented.

At the end of the first iteration, candidate will be the majority element (if it exists) or -1 otherwise. To verify if candidate is
indeed the majority element, a second iteration is performed. In this iteration, the count of candidate is verified to be greater
than N/3. If so, candidate is the majority element; otherwise, there is no majority element in the array.

Question 17

Problem Statement : Given a matrix m X n, count paths from left-top to the right bottom of a matrix with the constraints that
from each cell you can either only move to the rightward direction or the downward direction.

Brute Force Solution:

This solution relies on recursion and backtracking. Recursively explore all possible paths from the top-left corner to the bottom-
right corner, each step either moving right or down. Keep track of the number of paths to the current position. The total count of
paths is the sum of paths for all cells on the bottom row or rightmost column.

Optimized Solution (Dynamic Programming):

This solution utilizes dynamic programming to efficiently calculate the number of paths. Create a 2D array to store the number
of paths to each cell. Initialize the top-left cell to 1 (one path) and all other cells to 0 (no path). Iterate through each cell and
calculate the number of paths based on the sum of paths from the cells above and to its left. The number of paths to the bottom-
right corner represents the total number of paths in the matrix.

Question 18

Problem Statement : Given an array of numbers, you need to return the count of reverse pairs. Reverse Pairs are those pairs
where i2*arr[j].

Brute Force Solution : The brute force solution is to iterate over the entire array and find the count of reverse pairs by
comparing each pair of elements in the array. The time complexity of this solution is O(n^2), where n is the size of the array.

Optimized Solution : The optimized solution uses a merge sort algorithm to count the reverse pairs. The idea is to divide the
array into smaller subarrays, sort each subarray, and then merge the sorted subarrays while counting the reverse pairs. The
time complexity of this solution is O(nlogn), where n is the size of the array.

Question 19

Problem Statement : Given an array of integers arr[] and an integer target.

1st variant: Return YES if there exist two numbers such that their sum is equal to the target. Otherwise, return NO.

2nd variant: Return indices of the two numbers such that their sum is equal to the target. Otherwise, we will return {-1, -1}.

Note: You are not allowed to use the same element twice. Example: If the target is equal to 6 and num[1] = 3, then nums[1] +
nums[1] = target is not a solution.

Brute Force Approach:

Iterate over each element in the array and for each element, iterate over the remaining elements in the array. Check if the sum
of the current element and the element being iterated is equal to the target. If a pair is found, return YES or the indices of the
two elements.
Optimized Approach:

Use a hash table to store the elements of the array. For each element in the array, check if the complement (target - current
element) exists in the hash table. If it does, return YES or the indices of the two elements. Otherwise, insert the current element
into the hash table.

Question 20

Problem Statement : Given an array of N integers, your task is to find unique quads that add up to give a target value. In short,
you need to return an array of all the unique quadruplets [arr[a], arr[b], arr[c], arr[d]] such that their sum is equal to a given
target.

Brute Force Approach:

In brute force solution, we fix the first element and then we try to find the remaining three elements using three loops. For each
combination of four elements, we check whether their sum is equal to the target value. If it is, we add the combination to the
output array. The time complexity of the brute force approach is O(N^4), where N is the length of the array.

Optimized Approach:

We can sort the array in ascending order and then use two pointers to iterate through the array. For each element in the array,
we use two nested loops to iterate through the remaining elements and check whether their sum is equal to the target value. If it
is, we add the combination to the output array and skip the duplicate elements. The time complexity of the optimized approach
is O(N^3), where N is the length of the array.

Question 21

Problem Statement : You are given an array of ‘N’ integers. You need to find the length of the longest sequence which
contains the consecutive elements.

Brute Force Approach: Iterate through the array and for each element, check if the next element is consecutive. If it is,
increment the count by 1. If it's not, reset the count to 1. Keep track of the maximum count encountered.

Optimized Approach: Sort the array in ascending order. Initialize the count to 1 and the maximum count to 1. Iterate through
the array and for each element, check if the next element is consecutive. If it is, increment the count by 1. If it's not, reset the
count to 1 and check if the current count is greater than the maximum count. If it is, update the maximum count with the
current count. The maximum count after the iteration is the length of the longest consecutive sequence.

Question 22

Problem Statement : Given an array containing both positive and negative integers, we have to find the length of the longest
subarray with the sum of all elements equal to zero.

Brute Force Approach: The brute force approach is to generate all possible subarrays, find their sums, and check if the sum is
zero for any subarray. This approach requires O(n^3) time, where n is the number of elements in the array.

Optimized Approach: The optimized approach uses a hash table to store the cumulative sum of elements up to each index. For
each element, the current sum is calculated and checked if its negation exists in the hash table. If the negation is found, the
length of the subarray with zero sum is calculated. The hash table ensures constant-time lookup for the negation, reducing the
time complexity to O(n).

Question 23

Problem Statement : Subarray with given XOR

Brute Force: Iterate through all possible subarrays of the given array and calculate the XOR of each subarray. If the XOR of any
subarray matches the given target XOR, print the indices of that subarray. This solution has a time complexity of O(N^2), where
N is the number of elements in the array.

Optimized Solution: Use a Fenwick Tree to efficiently calculate the XOR of ranges of elements in the array. Initialize the
Fenwick Tree with the values of the array. For each query, use the Fenwick Tree to calculate the XOR of the range of elements
corresponding to the query. If the result matches the target XOR, print the indices of the query. This solution has a time
complexity of O(N log N) for preprocessing and O(log N) for each query.

Question 24

Problem Statement : Longest Substring Without Repeating Characters


Brute Force:

Iterate through the string, starting from each character. For each starting point, expand the window as far as possible while
ensuring that there are no repeating characters inside it. Keep track of the longest window encountered so far. Time Complexity:
O(n^3), where n is the length of the string.

Optimized:

Use a sliding window approach with two pointers (start and end) initialized to the beginning of the string. Increment the end
pointer while there are no repeating characters in the current window. If a repeating character is encountered, increment the
start pointer to remove the repeating character and slide the window forward. Keep track of the longest window encountered so
far. Time Complexity: O(n), where n is the length of the string.

Strings
Question 1

Problem Statement : Given an input string s, reverse the order of the words.

A word is defined as a sequence of non-space characters. The words in s will be separated by at least one space.

Return a string of the words in reverse order concatenated by a single space.

Note that s may contain leading or trailing spaces or multiple spaces between two words. The returned string should only have a
single space separating the words. Do not include any extra spaces.

Brute Force:

1. Split the input string into words using the split() method.
2. Reverse the order of the words in the list.
3. Join the words back into a string using the join() method.

Optimized:

1. Iterate over the input string from end to start.


2. Skip leading and trailing spaces.
3. Start appending non-space characters to a word string.
4. Append the word string to the result list when a space is encountered.
5. Reverse the order of words in the result list and join them with a space.

Question 2

Problem Statement : Given a string s, return the longest palindromic substring in s.

Brute Force Solution:

The brute force solution iterates through all possible substrings of the input string and checks if each substring is a palindrome.
To determine if a substring is a palindrome, the solution compares its characters from the start and end towards the middle. If
all characters match, the substring is a palindrome. The substring with the maximum length among all palindrome substrings is
returned as the result. The time complexity of this solution is O(n^3), where n is the length of the input string.

Optimized Solution:

The optimized solution makes use of dynamic programming to efficiently compute the longest palindromic substring. It
maintains a two-dimensional table where each cell contains the length of the longest palindromic substring within its
corresponding substring in the input string. The table is filled by iteratively checking all pairs of characters and extending the
substring when a palindrome is found. Once the table is filled, the cell with the maximum value represents the longest
palindromic substring in the input string. The time complexity of this solution is O(n^2), making it significantly faster than the
brute force approach.

Question 3

Problem Statement : Roman numerals are represented by seven different symbols: I, V, X, L, C, D and M.

Brute Force: Iterate over all possible combinations of Roman numerals and check if they match the input number. For each
combination, calculate the corresponding numeric value by adding the values of the individual symbols. If the calculated value
matches the input number, then the combination is a valid Roman numeral representation. This approach has a time complexity
of O(2^n), where n is the length of the input number. It is not an efficient approach for large input numbers.

Optimized Solution: Start with the largest Roman symbol, M, and check if the input number is greater than or equal to 1000. If
so, subtract 1000 from the input number and append 'M' to the Roman numeral representation. Repeat this process for the
remaining symbols (D, C, L, X, V, I) in descending order. This approach has a time complexity of O(n), where n is the length of the
input number, making it significantly more efficient than the brute force approach for large input numbers.

Question 4

Problem Statement : Implement the myAtoi(string s) function, which converts a string to a 32-bit signed integer.

The algorithm for myAtoi(string s) is as follows:

Whitespace: Ignore any leading whitespace (" "). Signedness: Determine the sign by checking if the next character is '-' or '+',
assuming positivity is neither present. Conversion: Read the integer by skipping leading zeros until a non-digit character is
encountered or the end of the string is reached. If no digits were read, then the result is 0. Rounding: If the integer is out of the
32-bit signed integer range [-231, 231 - 1], then round the integer to remain in the range. Specifically, integers less than -231
should be rounded to -231, and integers greater than 231 - 1 should be rounded to 231 - 1. Return the integer as the final result.

Brute Force Solution:

The brute force solution involves iterating through the input string character by character and manually parsing the integer by
keeping track of the sign, skipping leading zeros, and accumulating the digits until a non-digit character is encountered. The
time complexity of this solution is O(n), where n is the length of the input string.

Optimized Solution:

An optimized solution can be achieved by taking advantage of regular expressions to match the integer format and extract the
significant components (sign, leading zeros, digits). The time complexity of this solution is reduced to O(1) for valid integer
inputs and O(n) for invalid inputs. This improved solution provides faster processing times for valid integer inputs, while
handling edge cases efficiently.

Question 5

Problem Statement : Write a function to find the longest common prefix string amongst an array of strings.

If there is no common prefix, return an empty string "".

Brute Force Solution: The brute force approach involves iterating through each character of the shortest string in the array and
checking if it matches the corresponding character in all other strings. If a mismatch is found, the common prefix is truncated by
one character and the process is repeated. This solution has a time complexity of O(MN), where M is the length of the shortest
string and N is the number of strings in the array.

Optimized Solution: A more efficient approach is to use a divide-and-conquer strategy. The function first checks if the array of
strings is empty. If it is, an empty string is returned. If the array contains only one string, that string is returned as the common
prefix. Otherwise, the array is split into two halves, and the common prefix of each half is computed recursively. The common
prefix of the entire array is then computed by finding the common prefix of the two halves. This optimized solution has a time
complexity of O(N log N).

Question 6

Problem Statement : Given two strings a and b, return the minimum number of times you should repeat string a so that string
b is a substring of it. If it is impossible for b​​​​​​ to be a substring of a after repeating it, return -1.

Notice: string "abc" repeated 0 times is "", repeated 1 time is "abc" and repeated 2 times is "abcabc".

Brute Force Solution: This involves checking every possible value of the number of times to repeat string 'a'. This is done by
iterating through all integers from 1 to infinity and checking if the repeated string 'a' contains string 'b' as a substring. If it does,
then the number of repetitions is returned, otherwise, the process continues.

Optimized Solution: This approach involves using the KMP (Knuth-Morris-Pratt) algorithm to find the minimum repetitions of
'a' such that 'b' is a substring. The KMP algorithm preprocesses 'b' to create a failure function, which is used to skip unnecessary
comparisons while searching for 'b' in the repeated 'a'. This optimization significantly reduces the time complexity by avoiding
redundant comparisons and making the search more efficient.

Question 7

Problem Statement : Given two strings needle and haystack, return the index of the first occurrence of needle in haystack, or -1
if needle is not part of haystack.

Brute Force Solution: Iterate through each character in haystack and check if it matches the first character of needle. If it does,
continue iterating through subsequent characters in needle and compare them to corresponding characters in haystack. If all
characters in needle match, return the starting index in haystack. The time complexity is O(mn), where m is the length of needle
and n is the length of haystack.

Optimized Solution (KMP Algorithm): The Knuth-Morris-Pratt (KMP) algorithm preprocesses needle to create a failure function
that indicates the maximum length of the suffix that is also a prefix of needle. This allows skipping unnecessary comparisons,
reducing the time complexity to O(n) on average and O(mn) in the worst case, where m is the length of needle and n is the length
of haystack.

Question 8

Problem Statement : Given a string A. The only operation allowed is to insert characters at the beginning of the string. Find
how many minimum characters are needed to be inserted to make the string a palindrome string.

Brute Force Approach:

The brute force approach involves iterating through all possible prefixes of the string and checking if the resulting string is a
palindrome. The minimum number of characters required to be inserted is the difference between the length of the original
string and the length of the longest prefix that forms a palindrome. This approach has a time complexity of O(n^2), where n is
the length of the string.

Optimized Solution:

An optimized solution to this problem can be achieved using dynamic programming. We can create a table dp[i][j] that stores
the minimum number of characters that need to be inserted between characters i and j (inclusive) to make the substring a
palindrome. The table is filled in bottom-up manner, starting from substrings of length 1 and gradually expanding to larger
substrings. The optimized solution has a time complexity of O(n^2) and space complexity of O(n^2).

Question 9

Problem Statement : Given two strings s and t, return true if t is an anagram of s, and false otherwise.

An Anagram is a word or phrase formed by rearranging the letters of a different word or phrase, typically using all the original
letters exactly once.

Brute Force Solution: The brute force approach is a basic solution that iterates through both strings, s and t. It creates a hash
table to store the counts of characters in s. Then, it iterates through t, decrementing the count of each character in the hash table.
If a character in t is not found in the hash table or its count becomes negative, it indicates that t is not an anagram of s. The
running time of this approach is O((m + n) * min(m, n)), where m and n are the lengths of s and t, respectively.

Optimized Solution: The optimized solution reduces the complexity by utilizing the fact that two strings are anagrams if they
have the same character set (i.e., the same set of unique characters). This solution creates a set to store the unique characters
from s. Then, it iterates through t and checks if each character in t is present in the set. If any character in t is not found in the
set, it indicates that t is not an anagram of s. The running time of this optimized approach is O(m + n), where m and n are the
lengths of s and t, respectively.

Question 10

Problem Statement : The count-and-say sequence is a sequence of digit strings defined by the recursive formula:

countAndSay(1) = "1" countAndSay(n) is the run-length encoding of countAndSay(n - 1). Run-length encoding (RLE) is a string
compression method that works by replacing consecutive identical characters (repeated 2 or more times) with the concatenation
of the character and the number marking the count of the characters (length of the run). For example, to compress the string
"3322251" we replace "33" with "23", replace "222" with "32", replace "5" with "15" and replace "1" with "11". Thus the compressed
string becomes "23321511".

Given a positive integer n, return the nth element of the count-and-say sequence.

Brute Force Solution:

The brute force solution involves generating the first n elements of the count-and-say sequence and then returning the nth
element. This can be done by starting with the first element "1" and then repeatedly applying the count-and-say operation to
each element to generate the next element. The time complexity of this solution is O(n^2), where n is the given integer.

Optimized Solution
The Optimized solution uses dynamic programming to store the results of the count-and-say operation for each element. This
allows us to reuse the results of previous calculations, reducing the time complexity to O(n). The space complexity of this
solution is O(n).

Question 11

Problem Statement : Given two version strings, version1 and version2, compare them. A version string consists of revisions
separated by dots '.'. The value of the revision is its integer conversion ignoring leading zeros.

To compare version strings, compare their revision values in left-to-right order. If one of the version strings has fewer revisions,
treat the missing revision values as 0.

Return the following:

If version1 < version2, return -1. If version1 > version2, return 1. Otherwise, return 0.

Brute Force Solution:

The brute force solution is to convert both version strings to lists of integers, then compare the lists element by element. If the
first version string has fewer elements, pad it with zeros. This approach is simple but inefficient, especially for large version
strings.

Optimized Solution:

A more efficient solution is to use a recursive strategy. Split both version strings into two parts: the first revision and the
remaining revisions. Compare the first revisions; if they are equal, recurse on the remaining revisions. If the first revisions are
not equal, return the result of comparing them as integers. This solution is more efficient because it avoids unnecessary splitting
and conversions and has a time complexity of O(n), where n is the length of the longer version string.

Stack and Queue


Question 1

Problem Statement : Given an infix expression, Your task is to convert the given infix expression to a postfix expression.

Brute Force Approach:

This approach uses a stack and two passes. In the first pass, the input infix expression is scanned from left to right, and each
character is processed. If the character is an operand, it is simply appended to the output queue. If the character is an operator,
it is pushed onto the operator stack. If the scanned operator's precedence is less than or equal to the precedence of the operator
at the top of the stack, the operator at the top of the stack is popped and appended to the output queue. This process is repeated
until the end of the infix expression is reached, and any remaining operators in the stack are popped and appended to the
output queue.

Optimized Approach (Shunting-Yard Algorithm):

Improved version of the brute-force approach. It also uses a stack and two passes. The key difference is in how operators are
processed. In the optimized algorithm, the precedence of the current operator is compared to the precedence of the operator on
the top of the stack. If the current operator has higher precedence, it is pushed onto the stack. Otherwise, operators on the stack
with higher or equal precedence are popped and appended to the output queue until the current operator can be pushed onto
the stack. This algorithm ensures correct precedence without requiring multiple passes over the input.

Question 2

Problem Statement : You are given a string S of size N that represents the prefix form of a valid mathematical expression.
Convert it to its infix form.

Brute Force Solution: The straightforward approach involves iterating through the string from left to right and identifying
pairs of balanced parentheses. For each balanced pair, the substring within the parentheses is treated as another expression and
is recursively converted to its infix form. The resulting infix expressions are then concatenated to form the infix form of the
entire expression. While simple, this approach has a time complexity of O(2^N), where N is the length of the input string, due to
the exponential number of possible balanced parentheses combinations.

Optimized Solution: To improve efficiency, a stack-based algorithm can be employed. The string is processed from left to right,
and each character is placed on the stack if it is an opening parenthesis or a mathematical operator. If a closing parenthesis is
encountered, the characters between the corresponding opening parenthesis and the closing parenthesis are popped from the
stack and processed. They are concatenated to form a substring, which is then treated as a separate expression and converted to
infix form. The resulting infix expressions are concatenated with the rest of the characters on the stack. This approach has a
time complexity of O(N), providing a significant performance improvement over the brute force method.

Question 3

Problem Statement : You are given a string that represents the prefix form of a valid mathematical expression. Convert it to its
postfix form.

Brute Force Solution: The brute force approach is to recursively evaluate the expression tree represented by the prefix
expression. For each operator, evaluate its operands by recursively calling the function for the left and right subtrees. The result
of the operator is then returned as the value of the current node.

Optimized Solution: The optimized solution uses a stack to evaluate the prefix expression. For each token in the expression, if it
is an operand, push it onto the stack. If it is an operator, pop the top two operands from the stack, perform the operation, and
push the result back onto the stack. After processing all tokens, the top of the stack contains the result of the expression.

Question 4

Problem Statement : You are given a string that represents the postfix form of a valid mathematical expression. Convert it to its
prefix form.

Brute Force:

1. Create a stack to store operands.


2. Iterate over the postfix expression from left to right.
3. If the current character is an operand (digit), push it onto the stack.
4. If the current character is an operator, pop the top two operands from the stack, evaluate the expression, and push the
result back onto the stack.
5. Repeat steps 2-4 until the entire postfix expression has been processed.
6. The final element on the stack is the prefix form of the expression.

Optimized:

1. Create a stack to store operators.


2. Iterate over the postfix expression from right to left.
3. If the current character is an operand (digit), push it onto the stack.
4. If the current character is an operator, pop the top two elements from the stack, insert the current operator between them,
and push the modified expression back onto the stack.
5. Repeat steps 2-4 until the entire postfix expression has been processed.
6. The final element on the stack is the prefix form of the expression.

The optimized solution is more efficient because it reduces the number of stack operations required by reversing the order of
iteration.

Question 5

Problem Statement : You are given a string that represents the postfix form of a valid mathematical expression. Convert it to its
infix form.

Brute Force:

The brute force approach is to use a stack to evaluate the postfix expression. Start by iterating over the characters in the postfix
expression. If the character is an operand, push it onto the stack. If the character is an operator, pop the top two operands from
the stack, evaluate the expression, and push the result back onto the stack. Repeat until the entire expression is processed. The
final value on the stack is the infix form of the expression.

Optimized:

The optimized approach uses a stack and a dictionary to efficiently convert the postfix expression to infix form. The dictionary is
used to store the precedence of operators. Start by iterating over the characters in the postfix expression. If the character is an
operand, push it onto the stack. If the character is an operator, pop the top two operands from the stack, evaluate the expression,
and push the result back onto the stack. When evaluating an expression, check the precedence of the operator relative to the
operator at the top of the stack. If the precedence is higher, evaluate the expression immediately. If the precedence is lower,
push the expression onto a temporary stack until the precedence of the operator at the top of the stack is higher. This ensures
that expressions are evaluated in the correct order. Repeat until the entire expression is processed. The final value on the stack
is the infix form of the expression.
Question 6

Problem Statement : Given an infix expression, Your task is to convert the given infix expression to a prefix expression.

Brute Force Approach:

This approach involves converting the infix expression to a postfix expression first, using a stack to maintain operator
precedence. Once the postfix expression is obtained, it can be easily reversed to obtain the prefix expression.

Optimized Approach (Shunting-Yard Algorithm):

The Shunting-Yard algorithm is a more efficient approach for converting infix expressions to prefix expressions. It uses two
stacks: one for operators and one for operands. As the infix expression is parsed from left to right, operators are pushed onto the
operator stack based on their precedence, while operands are pushed onto the operand stack. When an operator is encountered,
it is popped from the operator stack and the operands it operates on are popped from the operand stack, applied to the operator,
and the result is pushed back onto the operand stack. This process continues until the end of the infix expression is reached. The
final expression in the operand stack is the prefix expression.

Question 7

Problem Statement : Given a circular integer array A, return the next greater element for every element in A. The next greater
element for an element x is the first element greater than x that we come across while traversing the array in a clockwise
manner. If it doesn't exist, return -1 for this element.

Brute force solution: Iterate over each element in the array and check if there is an element greater than it. If there is, the next
greater element is that element. Otherwise, the next greater element is -1.

Optimized solution: Create a stack and iterate over the array twice. The first iteration is to push elements onto the stack in
clockwise order. The second iteration is to pop elements off the stack and check if the next element in the array is greater than
the popped element. If it is, the next greater element for the popped element is that element. Otherwise, the next greater element
for the popped element is -1.

Question 8

Problem Statement : Given a circular integer array nums (i.e., the next element of nums[nums.length - 1] is nums[0]), return
the next greater number for every element in nums.

The next greater number of a number x is the first greater number to its traversing-order next in the array, which means you
could search circularly to find its next greater number. If it doesn't exist, return -1 for this number.

Brute Force:

The brute force solution is straightforward. For each element nums[i], we iterate through the array starting from i+1 to find the
next greater number. If no greater number is found, we assign -1 to nums[i]. This approach has a time complexity of O(n^2)
since it requires nested iterations.

Optimized Solution:

An optimized solution is based on the idea of using a stack. We iterate through the array once. For each element nums[i], we
push its index onto the stack if it is greater than the current stack top. If it is not, we pop elements from the stack until we find an
element that is smaller than nums[i]. If the stack is empty, it means there is no greater number for nums[i], and we assign -1 to
it. Otherwise, the index at the top of the stack is the index of the next greater number for nums[i]. This approach has a time
complexity of O(n).

Question 9

Problem Statement : Given an array, find the nearest smaller element G[i] for every element A[i] in the array such that the
element has an index smaller than i.

More formally,

G[i] for an element A[i] = an element A[j] such that


j is maximum possible AND
j < i AND
A[j] < A[i]

Elements for which no smaller element exist, consider next smaller element as -1.

Brute Force Solution: Iterate through the array from left to right. For each element A[i], traverse the array from index 0 to i-1 to
find the nearest smaller element G[i]. If no smaller element is found during the traversal, then G[i] is set to -1.

Optimized Solution: Use a stack to efficiently find the nearest smaller element for each element in the array. Iterate through the
array from left to right, pushing the indices of elements onto the stack in increasing order of values. For each element A[i], pop
elements from the stack until a smaller element is found. The element at the top of the stack is the nearest smaller element for
A[i]. If the stack is empty after popping all elements, then G[i] is set to -1.

Question 10

Problem Statement : Given an array of N integers and Q queries of indices. Return a list NGEs[] where NGEs[i] stores the count
of elements strictly greater than the current element (arr[indices[i]]) to the right of indices[i].

Brute Force Solution:

The brute force approach is to iterate over the array for each query and count the number of elements to the right of the index
that are strictly greater than the current element. Time Complexity: O(N^2), where N is the number of elements in the array.

Optimized Solution:

To optimize the solution, we can use a stack to keep track of the elements that have not yet been compared. For each query,
iterate over the stack, popping elements that are not strictly greater than the current element. The count of popped elements is
the answer for the current query. Time Complexity: O(N), where N is the number of elements in the array.

Question 11

Problem Statement : Given an array of non-negative integers representation elevation of ground. Your task is to find the water
that can be trapped after rain.

Brute Force Approach: This approach involves iterating through each position in the array and calculating the amount of water
that can be trapped at that position as the minimum of the maximum heights to the left and right of it minus the current height.
The time complexity of this approach is O(n^2), where n is the length of the array.

Optimized Approach (Dynamic Programming): This approach utilizes two arrays, left_max and right_max, to store the
maximum heights to the left and right of each position. These arrays can be computed in O(n) time. Then, the amount of water
trapped at each position can be calculated in O(1) time using the precomputed maximum heights. The overall time complexity of
this approach is O(n), which is significantly faster than the brute force approach.

Question 12

Problem Statement : Given an array of integers arr, find the sum of min(b), where b ranges over every (contiguous) subarray of
arr. Since the answer may be large, return the answer modulo 109 + 7.

Brute Force Solution: The brute force solution involves iterating over all possible subarrays of the given array, calculating the
minimum element in each subarray, and then summing up these minimum values. This approach has a time complexity of
O(n^3), where n is the length of the array.

Optimized Solution: To optimize the solution, we can use a technique called "prefix minimums". We compute the minimum
value for each prefix of the array, and store these values in a separate array. Then, for each subarray, we can quickly determine
the minimum value by comparing the prefix minimums at the start and end of the subarray. This approach has a time
complexity of O(n^2).

Question 13

Problem Statement : We are given an array asteroids of integers representing asteroids in a row.

For each asteroid, the absolute value represents its size, and the sign represents its direction (positive meaning right, negative
meaning left). Each asteroid moves at the same speed.

Find out the state of the asteroids after all collisions. If two asteroids meet, the smaller one will explode. If both are the same
size, both will explode. Two asteroids moving in the same direction will never meet.

Brute Force: The brute force approach is to simulate the movement of each asteroid and check for collisions at every time step.
This requires iterating through the entire array for each time step, resulting in a time complexity of O(N^2).

Optimized Solution: A more efficient approach is to use a stack to keep track of asteroids that have not yet collided. As the
asteroids move, check if the current asteroid collides with the one on the top of the stack. If there's a collision, pop the smaller
asteroid from the stack. Otherwise, push the current asteroid to the stack. This approach results in a time complexity of O(N), as
each asteroid is processed only once.
Question 14

Problem Statement : You are given an integer array nums. The range of a subarray of nums is the difference between the
largest and smallest element in the subarray.

Return the sum of all subarray ranges of nums.

A subarray is a contiguous non-empty sequence of elements within an array.

Brute Force Solution:

Time Complexity: O(n^3). Iterate over all possible subarrays using nested loops and calculate the range of each subarray. The
sum of these ranges is then returned.

Optimized Solution:

Time Complexity: O(n). Maintain two arrays: one to store the minimum element and one to store the maximum element seen so
far while traversing the array from left to right. The range of each subarray can be calculated as the difference between the
maximum and minimum elements in the corresponding subarray. The sum of all subarray ranges is then returned.

Question 15

Problem Statement : Given string num representing a non-negative integer num, and an integer k, return the smallest possible
integer after removing k digits from num.

Brute Force:

Iterate through each digit in num, and for each digit, try removing it and recursively call the function on the remaining string.
Store the smallest result obtained from all these recursive calls. This brute-force approach has a time complexity of O(N^K),
where N is the length of num and K is the number of digits to remove.

Optimized:

Use a stack to store the digits of num. Iterate through each digit in num, and if the current digit is smaller than the top of the stack
and we have removed fewer than k digits, pop the top of the stack. This ensures that we are always removing the largest digits
first. Once we have iterated through all digits in num, pop any remaining digits from the stack. The digits in the stack now
represent the smallest possible integer after removing k digits from num. This optimized approach has a time complexity of O(N),
where N is the length of num.

Question 16

Problem Statement : Given an array of integers heights representing the histogram's bar height where the width of each bar is
1 return the area of the largest rectangle in histogram.

Brute Force Solution: This solution checks all possible rectangles in the histogram. For each bar, it calculates the area of the
rectangle formed by that bar and all bars to the left and right. The maximum of these areas is the maximum area in the
histogram. Time complexity: O(n^3).

Optimized Solution (Stack): This solution uses a stack to keep track of potential rectangle boundaries. It iterates through the
histogram, and when it encounters a bar lower than the current stack top, it calculates the area of the rectangle enclosed by the
bars on the stack (which is the minimal possible width). The stack is then adjusted accordingly. Time complexity: O(n).

Question 17

Problem Statement : Given a rows x cols binary matrix filled with 0's and 1's, find the largest rectangle containing only 1's and
return its area.

Brute Force: In brute force we generate all possible rectangles and calculate the area of every rectangle and store the result in
variable. Time complexity of this approach is O(rows^2*cols^2).

Optimized Solution: In this approach, to find the largest rectangle in O(rows X cols) time. We maintain a temp array of size cols
to store the width of every bar for every row. The width of bar is calculated for every row. We compare the value of current row
with the previous row and find the maximum width for each bar of current row. We find the area of rectangle for every row
and update the maximum area.

Question 18

Problem Statement : Given an array of integers arr, there is a sliding window of size k which is moving from the very left of the
array to the very right. You can only see the k numbers in the window. Each time the sliding window moves right by one
position. Return the max sliding window.

Brute Force Solution: This solution iterates through the array and for each position, it calculates the maximum among the
current window of size k. The maximum is then added to the result array. This approach has a time complexity of O(n*k), where
n is the size of the array.

Optimized Solution (Using Deque): This solution uses a deque (double-ended queue) to keep track of the maximum elements in
the window. The deque is maintained such that it always contains the indices of the maximum elements in the window. When
the window slides, the elements outside the window are removed from the deque and the new element is added to the deque.
The maximum element can be found in O(1) time from the front of the deque. This approach has a time complexity of O(n).

Question 19

Problem Statement : Design an algorithm that collects daily price quotes for some stock and returns the span of that stock's
price for the current day.

The span of the stock's price in one day is the maximum number of consecutive days (starting from that day and going
backward) for which the stock price was less than or equal to the price of that day.

For example, if the prices of the stock in the last four days is [7,2,1,2] and the price of the stock today is 2, then the span of today
is 4 because starting from today, the price of the stock was less than or equal 2 for 4 consecutive days. Also, if the prices of the
stock in the last four days is [7,34,1,2] and the price of the stock today is 8, then the span of today is 3 because starting from
today, the price of the stock was less than or equal 8 for 3 consecutive days. Implement the StockSpanner class:

StockSpanner() Initializes the object of the class. int next(int price) Returns the span of the stock's price given that today's price is
price.

Brute Force:

This approach iterates backward from the current day to find the first day with a higher price. The span is the difference
between the current day and the day with the higher price. Time complexity: O(n) for each next(price) call, where n is the
number of days.

Optimized (Using Stack):

This approach uses a stack to store days with prices less than or equal to the current day. When a day with a higher price is
encountered, the span of the previous days is calculated and popped from the stack. The span of the current day is calculated
using the current day and the day at the top of the stack. Time complexity: O(1) amortized for each next(price) call.

Question 20

Problem Statement : LFU Cache

Brute Force:

In the brute force approach, the LFU cache is implemented using a dictionary to store key-value pairs and a frequency
dictionary to track the frequency of each key. When retrieving a value (get) or inserting/updating a value (put), we perform
linear scans to find the least frequently used key, which is inefficient. This method requires O(n) time for each operation
because we need to search through the entire frequency dictionary to find the least frequently used key to evict.

Optimized (Using Stack):

The optimized approach uses a combination of a hash map and a doubly linked list to maintain frequency counts and support
efficient eviction. Specifically, we use a primary hash map to store key-value pairs, a second hash map to keep track of
frequencies and their corresponding nodes in a frequency list. Nodes in the frequency list are doubly linked to facilitate efficient
updates and removals. This design allows get and put operations to run in O(1) time complexity on average, as it efficiently
updates frequency counts and manages cache evictions by leveraging the properties of the doubly linked list and hash maps to
keep track of the least frequently used and least recently used items.

Greedy
Question 1

Problem Statement : There is one meeting room in a firm. You are given two arrays, start and end each of size N.For an index
‘i’, start[i] denotes the starting time of the ith meeting while end[i] will denote the ending time of the ith meeting. Find the
maximum number of meetings that can be accommodated if only one meeting can happen in the room at a particular time. Print
the order in which these meetings will be performed.

Brute Force: This approach sorts the meetings based on their ending times. Then, it iterates through the sorted meetings and
maintains a count of the maximum number of meetings that can be accommodated. For each meeting, it checks if its starting
time is greater than the ending time of the previous meeting. If so, it increments the count and includes the meeting in the final
schedule. The time complexity of this approach is O(N log N), where N is the number of meetings.

Optimized Solution: This approach uses a greedy algorithm. It sorts the meetings based on their ending times. Then, it iterates
through the sorted meetings and maintains a pointer to the current meeting. For each meeting, it checks if its starting time is
greater than the ending time of the current meeting. If so, it updates the current meeting to the new meeting and increments the
count of the maximum number of meetings that can be accommodated. The time complexity of this approach is O(N log N).

Question 2

Problem Statement : We are given two arrays that represent the arrival and departure times of trains that stop at the platform.
We need to find the minimum number of platforms needed at the railway station so that no train has to wait.

Brute Force Solution: The obvious approach is to consider all pairs of arrival and departure times. For each pair, check if the
arrival time of the first train is less than the departure time of the second train. If it is, then the two trains overlap, and we need
one platform for each overlapping pair. The minimum number of platforms needed is the maximum count of overlapping pairs.

Optimized Solution (Using Sorting): We can optimize the brute force solution by sorting the arrival and departure times
separately. Once sorted, we can iterate through the arrival times, and for each arrival time, check if it overlaps with any of the
previous departure times. If it does, we increment the platform count by one. The maximum platform count encountered during
the iteration is the minimum number of platforms required.

Question 3

Problem Statement : You are given a set of N jobs where each job comes with a deadline and profit. The profit can only be
earned upon completing the job within its deadline. Find the number of jobs done and the maximum profit that can be obtained.
Each job takes a single unit of time and only one job can be performed at a time.

Brute Force Solution: The brute force solution involves trying all possible combinations of jobs and selecting the one that
maximizes profit while meeting deadlines. This approach has a time complexity of O(2^N), where N is the number of jobs.

Optimized Solution (Greedy Approach): A greedy approach sorts jobs in decreasing order of profit per unit time and
iteratively selects jobs that meet deadlines. It maintains a pointer to the current time and checks if the next job's deadline is met.
If so, it updates the maximum profit and pointer to the current time. This approach has a time complexity of O(N log N), where N
is the number of jobs. It achieves a near-optimal solution by prioritizing jobs with higher profit per unit time.

Question 4

Problem Statement : The weight of N items and their corresponding values are given. We have to put these items in a knapsack
of weight W such that the total value obtained is maximized.

Brute Force Solution: Try all possible combinations of items to find the maximum value without exceeding the knapsack
capacity. The time complexity is exponential, O(2^N).

Optimized Solution (Dynamic Programming): Create a table of size (N+1) x (W+1), where N is the number of items and W is
the knapsack capacity. Fill the table in bottom-up manner by considering each item and each possible weight capacity starting
from smaller weight capacities to larger ones. The cell (i, w) of the table represents the maximum value that can be obtained by
considering the first i items for a knapsack capacity of w. The time complexity is O(N * W), which is significantly better than the
brute force approach.

Question 5

Problem Statement : Given a value V, if we want to make a change for V Rs, and we have an infinite supply of each of the
denominations in Indian currency, i.e., we have an infinite supply of { 1, 2, 5, 10, 20, 50, 100, 500, 1000} valued coins/notes, what
is the minimum number of coins and/or notes needed to make the change.

Brute Force Solution: The brute force solution involves considering all possible combinations of coins and notes that add up to
the given value V. For each combination, it calculates the total number of coins and notes required. The combination with the
minimum number of coins and notes is considered the optimal solution.

Optimized Solution: A more efficient approach involves a greedy strategy. It initializes a result to contain a list of coins and
notes and sets the minimum number of coins and notes to a large value. For each coin or note denomination, it calculates the
maximum number of that denomination that can be included in the result. It then subtracts this number from the remaining
value V and adds the corresponding coin or note to the result. The process is repeated until V becomes 0 or no coin or note can
be added. This approach ensures that the result contains the minimum number of coins and notes needed to make the change.

Question 6

Problem Statement : Assume you are an awesome parent and want to give your children some cookies. But, you should give
each child at most one cookie.

Each child i has a greed factor g[i], which is the minimum size of a cookie that the child will be content with; and each cookie j
has a size s[j]. If s[j] >= g[i], we can assign the cookie j to the child i, and the child i will be content. Your goal is to maximize the
number of your content children and output the maximum number.

Brute Force:

The brute force approach would be to try all possible combinations of assigning cookies to children. For each combination,
calculate the number of content children and keep track of the maximum number obtained.

Optimized Solution:

A more efficient approach is to sort both the children and cookies in ascending order based on their preferences and sizes,
respectively. This way, we can start from the smallest child and the smallest cookie, and assign cookies to children as long as the
cookie size is greater than or equal to the child's greed factor. Once we reach a point where the cookie size is less than the child's
greed factor, we know that the child cannot be satisfied and we stop the process. By following this greedy approach, we can find
the maximum number of content children without exhaustively exploring all combinations.

Heaps
Question 1

Problem Statement : Implement the Min Heap data structure.

You will be given 2 types of queries:-

0 X Insert X in the heap.

1 Print the minimum element from the heap and remove it.

Brute Force Solution:

The brute force solution to implement a Min Heap is to maintain an array of elements and perform heapify operations after
every insert or delete operation. This approach requires O(N log N) time complexity for both insert and delete operations, where
N is the number of elements in the heap.

Optimized Solution:

An optimized solution is to use a balanced binary tree where the value of each node is greater than or equal to the values of its
children. To maintain the heap property, we can use the following operations:

Insert: Add the new element to the end of the tree. Then, "bubble up" the element by comparing it with its parent and
swapping them if the new element is smaller.
Delete: Remove the root element, which is the minimum value. Then, "bubble down" the last element (now at the root) by
comparing it with its children and swapping it with the smaller child.

This optimized solution provides O(log N) time complexity for both insert and delete operations, making it significantly more
efficient than the brute force approach.

Question 2

Problem Statement : Given an integer array nums and an integer k, return the kth largest element in the array.

Note that it is the kth largest element in the sorted order, not the kth distinct element.

Can you solve it without sorting?

Brute Force Solution: This approach involves sorting the array first, and then finding the kth largest element from the sorted
array. The time complexity of sorting is O(nlogn), and the time complexity of finding the kth largest element from the sorted
array is O(n). Therefore, the total time complexity of this approach is O(nlogn).
Optimized Solution: This approach uses the "Quick Select" algorithm, which is based on the quicksort algorithm. The idea is to
use a randomized quick sort to find the kth largest element in O(n) time on average. The algorithm partitions the array into two
halves around a pivot element, and then recursively applies the same algorithm to the half that contains the kth largest element.
The time complexity of this approach is O(n) on average, and O(n^2) in the worst case.

Question 3

Problem Statement : Given two equally sized 1-D arrays A, B containing N integers each.

A sum combination is made by adding one element from array A and another element of array B.

Return the maximum C valid sum combinations from all the possible sum combinations.

Brute Force Solution: This solution generates all possible sum combinations and then selects the maximum C valid ones. It
requires O(N^2) time and O(1) space.

Optimized Solution: 1. Sort the arrays A and B in ascending order. 2. Initialize two pointers, i and j, to the beginning of A and B,
respectively. 3. Start a while loop that continues as long as both pointers are within the arrays. 4. Calculate the sum of A[i] and
B[j]. 5. If the sum is valid, increment the count of valid combinations and advance the corresponding pointer (i if A[i] is smaller, j
otherwise). 6. If the sum is too small, advance the pointer for A. 7. If the sum is too large, advance the pointer for B. 8. Repeat
steps 4-7 until both pointers reach the end of their arrays.

This solution runs in O(N log N) time and requires O(1) space.

Question 4

Problem Statement : The median is the middle value in an ordered integer list. If the size of the list is even, there is no middle
value, and the median is the mean of the two middle values.

For example, for arr = [2,3,4], the median is 3. For example, for arr = [2,3], the median is (2 + 3) / 2 = 2.5. Implement the
MedianFinder class:

MedianFinder() initializes the MedianFinder object. void addNum(int num) adds the integer num from the data stream to the
data structure. double findMedian() returns the median of all elements so far. Answers within 10-5 of the actual answer will be
accepted.

Brute Force Solution:

The brute force approach involves sorting the entire array after every addition, and then finding the median. This has a time
complexity of O(n log n) per operation.

Optimized Solution:

A more efficient approach is to use a self-balancing binary search tree (BST) to store the elements. This allows for efficient
addition and removal of elements in O(log n) time. The median can then be found by getting the middle element of the BST in
O(log n) time. This results in an overall time complexity of O(log n) per operation.

Question 5

Problem Statement : You have been given ‘K’ different arrays/lists, which are sorted individually (in ascending order).
You need to merge all the given arrays/list such that the output array/list should be sorted in ascending order.

Brute Force Solution:

The brute force solution is to concatenate all the arrays and sort the resulting array. This approach is straightforward but has a
time complexity of O(N log N), where N is the total number of elements in all arrays.

Optimized Solution:

A more efficient approach is to use a priority queue. The priority queue will maintain a minimum heap of elements from all
arrays. At each step, the smallest element is removed from the priority queue and added to the result array. This ensures that
the elements are added in sorted order. The time complexity of this approach is O(N log K), where K is the number of arrays.

Question 6

Problem Statement : Given an integer array nums and an integer k, return the k most frequent elements. You may return the
answer in any order.

Brute Force Approach: The straightforward approach is to create a frequency map by iterating over the array and counting the
occurrences of each element. Then, sort the map by the values (frequencies) and select the top k elements. This solution has time
complexity O(N log N), where N is the size of the array.

Optimized Approach: A more efficient solution involves using a heap or priority queue. In this approach, create a heap (or
priority queue) with a tuple (frequency, element) for each element in the array. The heap is initialized with all the elements, and
then the k most frequent elements are extracted from the heap, which has a time complexity of O(N log k). This approach is
more efficient because it only iterates through the array once, and it maintains the top k elements without having to sort the
entire map.

Binary Trees
Question 1

Problem Statement : Given the root of the Binary Tree, return the length of its diameter. The Diameter of a Binary Tree is the
longest distance between any two nodes of that tree. This path may or may not pass through the root.

Brute Force Solution: Calculate the height of each subtree rooted at every node using postorder traversal. Update the diameter
by checking the sum of the heights of the left and right subtrees at each node. This approach requires visiting each node at least
twice and has a time complexity of O(N^2).

Optimized Solution: Use a recursive function that calculates the height and diameter of the subtree rooted at each node
simultaneously. During the recursion, track the longest path that includes the current node as well as the height of the current
node. The diameter is updated as the maximum of the longest paths from the subtrees and the sum of the heights of the left and
right subtrees. This approach has a time complexity of O(N) and a space complexity of O(H), where H is the height of the tree.

Question 2

Problem Statement : Given a Binary Tree, determine the maximum sum achievable along any path within the tree. A path in a
binary tree is defined as a sequence of nodes where each pair of adjacent nodes is connected by an edge. Nodes can only appear
once in the sequence, and the path is not required to start from the root. Identify and compute the maximum sum possible along
any path within the given binary tree.

Brute Force Approach:

A straightforward approach to finding the maximum sum path in a binary tree is to recursively compute the sum of all possible
paths and select the path with the highest sum. For each subtree rooted at a node, calculate the sum of the path from the root to
every leaf node. Sum up the values of the nodes along each path. Maintain a variable to store the maximum sum path. Repeat
this process for each subtree until the entire tree has been traversed. The maximum sum path is the one with the highest sum
among all the paths.

Optimized Solution:

The optimized solution uses dynamic programming to avoid redundant calculations. The recursive function returns two values:
the maximum sum path starting from the current node and the maximum sum path that includes the current node and its
children. The function traverses the tree once, and at each node, it calculates both values. The maximum sum path is determined
by selecting the larger of the two values returned for the root node.

Question 3

Problem Statement : Given a Binary Tree, print the zigzag traversal of the Binary Tree. Zigzag traversal of a binary tree is a way
of visiting the nodes of the tree in a zigzag pattern, alternating between left-to-right and right-to-left at each level.

Brute Force Approach:

This approach involves traversing the tree level by level and reversing the order of nodes at each level. It uses two stacks, one
for the current level and the other for the next level. Time complexity: O(n), Space complexity: O(n).

Optimized Approach using Queue:

This approach uses a queue to store the nodes of the tree. It iteratively dequeues nodes from the queue, processes them, and
enqueues their children into the queue in a zigzag manner. Time complexity: O(n), Space complexity: O(n/2) = O(n) since the
queue will have at most n/2 nodes at any given time.

Question 4

Problem Statement : Given a Binary Tree, perform the boundary traversal of the tree. The boundary traversal is the process of
visiting the boundary nodes of the binary tree in the anticlockwise direction, starting from the root.

Brute Force Solution: The brute-force solution involves performing three separate traversals:

1. Left Boundary: Visit all nodes from the root to the leftmost leaf node, including the root.
2. Leaf Nodes: Visit all leaf nodes in the tree.
3. Right Boundary: Mirroring the approach for the left boundary, visit all nodes from the rightmost leaf node back to the
root.

This approach has a time complexity of O(N), where N is the number of nodes in the tree.

Optimized Solution: A more efficient solution is to use two stacks:

1. Left Stack: Push all nodes from the root to the left boundary (excluding the leaf nodes).
2. Right Stack: Push all nodes from the right boundary back to the root (excluding the leaf nodes).

Then, perform a preorder traversal, visiting the boundary nodes in the anticlockwise direction by alternating between popping
from the left and right stacks when appropriate. This approach also has a time complexity of O(N).

Question 5

Problem Statement : Given a Binary Tree, return the Vertical Order Traversal of it starting from the Leftmost level to the
Rightmost level. If there are multiple nodes passing through a vertical line, then they should be printed as they appear in level
order traversal of the tree.

Brute Force Solution: Traverse the tree and store the vertical coordinates of each node in a map. Then, for each vertical
coordinate, sort the nodes by their level and print them. The time complexity of this solution is O(N log N), where N is the
number of nodes in the tree.

Optimized Solution: Use a modified level-order traversal with a queue. In the queue, store a tuple (node, vertical coordinate)
for each node. While processing each node in the queue, print its value and update the vertical coordinate for its children. The
time complexity of this solution is O(N), where N is the number of nodes in the tree.

Question 6

Problem Statement : Given a Binary Tree, return its Top View. The Top View of a Binary Tree is the set of nodes visible when
we see the tree from the top.

Brute Force Solution: The simplest solution is to perform DFS recursively on the tree and keep track of the minimum and
maximum horizontal distance from the root. For each node, we update the top view based on the horizontal distance from the
root. If the horizontal distance is seen for the first time, we add the node to the top view. The time complexity of this approach is
O(N^2).

Optimized Solution: A more efficient solution is to use a hashmap to store the horizontal distances and the nodes associated
with them. We perform level-order traversal of the tree and for each node, we check if the horizontal distance is seen before. If
not, we add the node to the top view. This approach has a time complexity of O(N) and provides the top view of the binary tree.

Question 7

Problem Statement : Given a Binary Tree, return its Bottom View. The Bottom View of a Binary Tree is the set of nodes visible
when we see the tree from the bottom.

Brute Force Approach: The naive approach is to traverse the tree using the Level Order Traversal and for each node at each
level, print the node with the maximum level value. This approach has a time complexity of O(N^2), where N is the number of
nodes in the tree, as it traverses the tree twice for each node.

Optimized Approach: A more efficient approach is to use a HashMap and a Queue to store the level and node data, respectively.
We traverse the tree using Level Order Traversal and add each node to the HashMap with its corresponding level. For each level,
we update the HashMap with the last node visited. Finally, we iterate through the HashMap to print the bottom view of the tree.
This approach has a time complexity of O(N), as it traverses the tree only once and the space complexity of O(N), as it stores the
level and node data in the HashMap.

Question 8

Problem Statement : Given a Binary Tree, return its right and left views.

Brute Force: In the brute-force approach, we perform level order traversal, and for each level, we print the first and last node.
The time complexity of this approach is O(N). Space complexity is also O(N) since we need to store all the nodes in the queue for
level order traversal.

Optimized Solution: The optimized solution is to traverse the binary tree using depth-first search and keep track of the current
level. We maintain two arrays, one for the left view and one for the right view. When we visit a node, we check if it is the first
node visited at that level. If it is, we add it to the corresponding array. The time complexity of this approach is O(N) and space
complexity is O(H), where H is the height of the binary tree.

Question 9

Problem Statement : Given a Binary Tree and a reference to a root belonging to it. Return the path from the root node to the
given leaf node.

Brute Force: The brute force approach is to perform a recursive DFS(depth first search) of the tree. Starting from the root node,
we recursively search for the given leaf node. If the leaf node is found, we add the current node to the path and return the path.
If the leaf node is not found in the current branch, we return an empty path. The time and space complexity of this approach is
O(N), where N is the number of nodes in the tree.

Optimized Solution: A more efficient solution is to use a stack to keep track of the nodes that have been visited while searching
for the target leaf node. Starting from the root node, we recursively traverse the tree and add each node to the stack. When a leaf
node is reached, we check if it is the target leaf node. If it is, we return the path by popping elements from the stack. If it is not,
we remove the leaf node from the stack and continue searching through the tree. This approach only pushes the nodes that are
part of the path to the leaf node, reducing the space complexity to O(H), where H is the height of the tree.

Question 10

Problem Statement : Given a binary tree, Find the Lowest Common Ancestor for two given Nodes (x,y).

Brute Force Approach:

Brute force approach involves performing two depth first searches (DFS), one for each node, starting from the root. Both DFSs
stop when the target node is found. The lowest common ancestor (LCA) of the target nodes is the last common node that was
visited by both DFSs. This approach has a time complexity of O(n), where 'n' is the number of nodes in the tree, as it traverses the
entire tree for each node.

Optimized Approach:

To optimize the solution, we can use a single DFS with recursion and a bit of bookkeeping to find the LCA. During the DFS, we
check if the current node is equal to either target node. If it is, we return the current node. If not, we recursively call the DFS on
the left and right subtrees and store the result in two variables, left_lca and right_lca. If both left_lca and right_lca are not None,
it means the current node is the LCA so we return the current node. Otherwise, we return left_lca or right_lca based on which is
not None. This approach has a time complexity of O(n), where 'n' is the number of nodes in the tree, but it traverses each node
only once.

Question 11

Problem Statement : Given a Binary Tree, return its maximum width. The maximum width of a Binary Tree is the maximum
diameter among all its levels. The width or diameter of a level is the number of nodes between the leftmost and rightmost nodes.

Brute Force Solution: A brute force solution is to perform a level order traversal of the tree and count the number of nodes at
each level. The maximum of these counts is the maximum width of the tree. This solution has a time complexity of O(N), where
N is the number of nodes in the tree.

Optimized Solution: Instead of counting the number of nodes at each level, we can use a queue to keep track of the nodes at
each level. We start with the root node in the queue. We then loop through the queue, dequeuing each node and enqueuing its
left and right children. We also keep track of the maximum number of nodes at any level. After we have dequeued all the nodes
at a level, we update the maximum number of nodes with the current number of nodes. This solution has a time complexity of
O(N), where N is the number of nodes in the tree, and a space complexity of O(W), where W is the maximum width of the tree.

Question 12

Problem Statement : Given a Binary Tree, convert the value of its nodes to follow the Children Sum Property. The Children Sum
Property in a binary tree states that for every node, the sum of its children's values (if they exist) should be equal to the node's
value. If a child is missing, it is considered as having a value of 0.

Brute Force Solution: Traverse the binary tree in recursive post-order and update the value of each node with the sum of its
children's values. The time complexity of this solution is O(N^2), where N is the number of nodes in the tree, as each node is
visited multiple times during the traversal.
Optimized Solution: Utilize a bottom-up approach. Traverse the binary tree in post-order and store the updated values of its
children. While returning from a child node, update the parent node's value with the sum of its children's updated values. The
time complexity of this solution is O(N), where N is the number of nodes in the tree, as each node is visited exactly once during
the traversal.

Question 13

Problem Statement : Given the root of a binary tree, the value of a target node target, and an integer k, return an array of the
values of all nodes that have a distance k from the target node.

You can return the answer in any order.

Brute Force Solution: The brute force solution involves performing a depth-first search (DFS) on the binary tree, starting from
the target node. For each node visited, calculate its distance from the target node. If the distance is equal to k, add the node's
value to the result array. The time complexity of the brute force approach is O(N), where N is the number of nodes in the binary
tree.

Optimized Solution: To optimize the solution, we can leverage the concept of "path compression." During the DFS, we maintain
a dictionary that maps each visited node to its distance from the target node. When visiting a node's child, we first check if it has
been visited. If it has, we can retrieve its distance from the dictionary instead of recalculating it. This path compression step
significantly reduces the time complexity to O(N), where N is the number of unique nodes at distance k from the target node.

Question 14

Problem Statement : Given a binary tree and a node data called target. Find the minimum time required to burn the complete
binary tree if the target is set on fire. It is known that in 1 second all nodes connected to a given node get burned. That is its left
child, right child, and parent. Note: The tree contains unique values.

Brute Force Approach (DFS):

The brute force approach involves performing a Depth-First Search (DFS) on the binary tree, starting from the target node. For
each node visited, we recursively call DFS on its left and right children. While visiting each node, we maintain a timer that
increments by 1 for every node visited. The maximum timer value obtained during the traversal represents the minimum burn
time.

Optimized Approach (BFS):

A more efficient approach involves using Breadth-First Search (BFS) to traverse the tree. We maintain two queues: 'fireQueue'
and 'nodeQueue'. The 'fireQueue' stores nodes that are currently on fire, while the 'nodeQueue' stores nodes that need to be
explored. Initially, we push the target node into both queues. In each iteration of BFS, we burn all nodes in 'fireQueue', and then
for each node in 'nodeQueue', we push its left child, right child, and parent into 'nodeQueue'. We increment the timer by 1 and
update the maximum timer value as we burn nodes. This approach ensures that we explore all nodes connected to the target
node in the shortest possible time.

Question 15

Problem Statement : Given a Complete Binary Tree, count and return the number of nodes in the given tree. A Complete Binary
Tree is a binary tree in which all levels are completely filled, except possibly for the last level, and all nodes are as left as
possible.

Brute Force Solution: A straightforward approach is to recursively traverse the tree and increment a counter for each node
visited. This solution has a time complexity of O(N), where N is the number of nodes in the tree.

Optimized Solution: A more efficient solution takes advantage of the properties of a complete binary tree. In a complete binary
tree, the number of nodes in a subtree rooted at a node can be calculated by multiplying the number of children (2) by the
number of complete subtrees rooted at each of its children. Using this property, the number of nodes in the entire tree can be
calculated recursively as follows:

T(n) = 2 * T(n/2), if n is even T(n) = 2 * T((n+1)/2) + 1, if n is odd

where T(n) represents the number of nodes in a subtree rooted at a node with n children. This solution has a time complexity of
O(log N), making it significantly more efficient than the brute force solution.

Question 16

Problem Statement : Geek wants to know the traversals required to construct a unique binary tree. Given a pair of traversal,
return true if it is possible to construct unique binary tree from the given traversals otherwise return false.
Each traversal is represented with an integer: preorder - 1, inorder - 2, postorder - 3.

Brute Force:

In the brute force approach, for the provided traversals, compute all possible combinations of trees and check if the provided
traversals are the traversals of the computed tree. The time complexity of this approach is O(N^N).

Optimized:

A more efficient approach relies on the properties of binary trees. For every node in an Inorder traversal, there exists a
corresponding unique Preorder and Postorder traversal. Preorder traversal allows us to determine the root node, and Postorder
traversal provides the right subtree for the root. Using this, we can check if there exists a Postorder traversal that corresponds to
the Preorder and Inorder traversals provided. If such a Postorder traversal exists, it means a unique binary tree can be
constructed. The time complexity of this approach is O(N).

Question 17

Problem Statement : Given the Preorder and Inorder traversal of a Binary Tree, construct the Unique Binary Tree represented
by them.

To construct the binary tree from preorder and inorder traversals, you can use a recursive solution that follows these steps: 1)
Create an empty binary tree node. 2) Assign the root of the tree to be the first element of the preorder traversal. 3) Search for the
root of the tree in the inorder traversal. 4) The elements in the inorder traversal before the root form the left subtree. 5) The
elements in the inorder traversal after the root form the right subtree. 6) Recursively construct the left and right subtrees using
the steps above.

An optimized solution is to use the Morris Inorder Traversal. This traversal technique allows us to find the inorder successor of a
node without using recursion or stack. This can significantly reduce the time and space complexity of the algorithm. The Morris
Inorder Traversal works by creating a temporary link between a node and its inorder successor, then traversing the tree using
this link to find the inorder successor of each node. Once the inorder successor of a node is found, the temporary link is
removed.

Question 18

Problem Statement : Given the Postorder and Inorder traversal of a Binary Tree, construct the Unique Binary Tree represented
by them.

Brute Force Solution:

This approach constructs the tree recursively by iterating over the inorder traversal and finding the index of the root node in the
postorder traversal. The left subtree is then constructed using the inorder and postorder sequences up to the root node, and the
right subtree is constructed similarly. This process continues until the entire tree is constructed. The complexity of this approach
is O(n^2), where n is the number of nodes in the tree.

Optimized Solution:

A more efficient approach involves using a hash table to store the positions of nodes in the inorder traversal. This allows for
constant-time lookup of the index of the root node in the postorder traversal, reducing the time complexity to O(n). The tree is
then constructed recursively as in the brute force approach.

Question 19

Problem Statement : Given a Binary Tree, design an algorithm to serialise and deserialise it. There is no restriction on how the
serialisation and deserialization takes place. But it needs to be ensured that the serialised binary tree can be deserialized to the
original tree structure. Serialisation is the process of translating a data structure or object state into a format that can be stored
or transmitted (for example, across a computer network) and reconstructed later. The opposite operation, that is, extracting a
data structure from stored information, is deserialization.

Brute Force Approach: Traverse the tree in pre-order (or any other order) and create a string representation of the tree. The
string representation can include information about the nodes, such as their values, left and right child pointers, etc. To
deserialize the tree, parse the string representation and create the nodes and their connections based on the information in the
string.

Optimized Approach: - Serialization: Perform a depth-first traversal (DFS) of the tree, using a stack to keep track of nodes that
need to be visited. As each node is visited, add its value to a list. For null nodes, add a special value (e.g., -1) to indicate the
absence of a node. - Time Complexity: O(N), where N is the number of nodes in the tree. - Deserialization: Use a queue to keep
track of nodes that have been created so far. Start by creating a root node from the first value in the list and adding it to the
queue. Then, for each remaining value in the list: - If the value is not -1, create a new node with that value and add it to the
queue. - If the value is -1, pop two nodes from the queue and make the first one the left child of the second one. - Time
Complexity: O(N), where N is the number of nodes in the tree.

Question 20

Problem Statement : Given a Binary Tree, implement Morris Inorder Traversal and return the array containing its inorder
sequence.

Brute Force Method: The brute force method is based on the recursive traversal of the binary tree. In the inorder traversal, we
first visit the left subtree, then the root node, and finally, the right subtree. We can use this recursive method to perform inorder
traversal:

def inorder_traversal(root): if root is None: return [] left_inorder = inorder_traversal(root.left) right_inorder =


inorder_traversal(root.right) return left_inorder + [root.val] + right_inorder

Optimized Solution (Morris Inorder Traversal): The Morris Inorder Traversal is an optimized method that doesn't need to use
recursion or a stack to perform inorder traversal. The main idea of this method is to modify the binary tree so that the inorder
successor of each node becomes its right child. Once the binary tree is modified, we can do an inorder traversal by simply
traversing the modified binary tree and printing the values of the nodes.

Here's a step-by-step explanation of the Morris Inorder Traversal: 1. Start from the root node. 2. If the left child of the current
node is null, then print the current node and move to its right child. 3. Otherwise, find the inorder successor of the current node.
4. Make the current node as the right child of its inorder successor. 5. Move to the left child of the current node. 6. Repeat steps 2-
5 until the current node becomes null.

Question 21

Problem Statement : Given a Binary Tree, convert it to a Linked List where the linked list nodes follow the same order as the
pre-order traversal of the binary tree.

Brute Force Solution: This approach involves recursively traversing the binary tree in pre-order and appending each node to
the end of the linked list. The time complexity of this solution is O(n), where n is the number of nodes in the tree.

Optimized Solution: This solution leverages the stack data structure to perform the pre-order traversal and construct the linked
list simultaneously. The time complexity is also O(n), but it uses less space because it does not store the entire binary tree in
memory.

Paragraph Format: The brute force solution, with a time complexity of O(n), recursively traverses the binary tree in pre-order
and appends each node to the end of the linked list. In contrast, the optimized solution leverages the stack data structure to
perform the pre-order traversal and construct the linked list simultaneously, also achieving a time complexity of O(n) while
using less space.

Binary Search Trees


Question 1

Problem Statement : Given a Binary Search Tree and a key, return the ceiling of the given key in the Binary Search Tree.

Brute Force:

In the brute force approach, we perform an inorder traversal of the binary search tree and store the values in an array. After
that, we perform a linear search to find the ceiling value of the given key in the sorted array. The time complexity of this
approach is O(N), where N is the number of nodes in the binary search tree.

Optimized Solution:

The optimized solution is based on the iterative approach. We start from the root node of the binary search tree and compare
the given key with the current node's value. If the key is less than the current node's value, we move to the left subtree. If the key
is greater than or equal to the current node's value, we move to the right subtree. If we reach a leaf node and the key is greater
than the leaf node's value, then the ceiling of the key is null. The time complexity of this approach is O(log N), where N is the
number of nodes in the binary search tree.

Question 2

Problem Statement : Given a Binary Search Tree and a key, return the floor of the given key in the Binary Search Tree.
Brute Force Solution: Perform an inorder traversal of the BST. While traversing, store the maximum value obtained so far.
Once the inorder traversal is complete, the maximum value obtained is the floor of the given key.

Optimized Solution (Time Complexity: O(Log N)): Start from the root node and keep traversing to the right child as long as the
key is greater than the current node's value. This ensures that you move to the maximum possible value less than or equal to the
given key. When the key becomes less than or equal to the current node's value, move to the left child. Continue this process
until you reach a leaf node. The value of the leaf node is the floor of the given key.

Question 3

Problem Statement : You are given the root node of a binary search tree (BST) and a value to insert into the tree. Return the
root node of the BST after the insertion. It is guaranteed that the new value does not exist in the original BST.

Notice that there may exist multiple valid ways for the insertion, as long as the tree remains a BST after insertion. You can
return any of them.

Brute Force Solution (Insertion and Rebuild): This approach inserts the new value into the BST by traversing the tree until the
appropriate insertion point is found. Once inserted, the entire tree is rebuilt to ensure it remains a BST. While this guarantees a
valid solution, it is inefficient for large trees due to the need to rebuild the entire structure.

Optimized Solution (Recursive Insertion): A more efficient method is to perform the insertion recursively. The root node is
compared to the new value. If the value is less than the root, the left subtree is checked; otherwise, the right subtree is checked.
This process continues until an appropriate insertion point is found, where the new node is inserted as a child of the last
checked node. This recursive approach ensures that the BST property is maintained throughout the insertion process,
eliminating the need for rebuilding and improving efficiency for large trees.

Question 4

Problem Statement : Given a root node reference of a BST and a key, delete the node with the given key in the BST. Return the
root node reference (possibly updated) of the BST.

Basically, the deletion can be divided into two stages:

Search for a node to remove. If the node is found, delete the node.

Brute Force: This approach involves traversing the entire tree to find the node to delete and then deleting it.

Optimized Solution: The optimized solution involves utilizing the properties of a BST. - If the node to be deleted has no children,
simply remove it. - If the node to be deleted has one child, replace the node with its child. - If the node to be deleted has two
children, find its inorder successor (the smallest node greater than the current node) and replace the current node with its
inorder successor. Then, delete the inorder successor from its original position.

Benefits of using the optimized solution:

Faster: The optimized solution avoids traversing the entire tree, leading to better time complexity.
Space-efficient: It does not require any additional memory to store intermediate results.
Simpler: The optimized solution is more concise and easier to understand.

Question 5

Problem Statement : Given a Binary Search Tree and an integer ‘K’. Find and return the ‘K-th’ smallest and ‘K-
th’ largest element in the given Binary Search Tree.

Brute Force Solution: In the brute force approach, we perform inorder traversal of the BST and store the elements in an array.
We then use the array to find the ‘K-th’ smallest and ‘K-th’ largest elements. This approach has a time complexity of
O(N), where N is the number of nodes in the BST.

Optimized Solution: The optimized solution uses the fact that the inorder traversal of a BST gives the elements in sorted order.
We maintain two pointers, ‘curr’ and ‘kth’, which traverse the tree simultaneously. ‘curr’ keeps track of the
current node, and ‘kth’ keeps track of the ‘K-th’ smallest element. We move ‘curr’ to the left child if ‘kth’
is greater than 1 and ‘curr’ is not the ‘kth’ smallest element. Otherwise, we move ‘curr’ to the right child. We
decrement ‘kth’ by 1 whenever we visit a node to keep track of the ‘K-th’ smallest element. The time complexity of
this approach is O(N), but it performs fewer comparisons than the brute force approach.

Question 6

Problem Statement : Given the root of a binary tree, determine if it is a valid binary search tree (BST).
A valid BST is defined as follows:

The left subtree of a node contains only nodes with keys less than the node's key. The right subtree of a node contains only nodes
with keys greater than the node's key. Both the left and right subtrees must also be binary search trees.

Brute Force Solution:

Complexity: O(n^2)
Approach: Traverse the entire tree twice. In the first pass, store all the node values in an array. In the second pass, check if
the array is sorted in ascending order. If sorted, the tree is a valid BST.

Optimized Solution:

Complexity: O(n)
Approach: Perform an in-order traversal of the tree and check if the current node's value is greater than the previous
node's value. If the condition holds true throughout the traversal, the tree is a valid BST. This is more efficient as it requires
only one traversal instead of two, and in-order traversal naturally produces the sorted order of node values.

Question 7

Problem Statement : Given a binary search tree (BST), find the lowest common ancestor (LCA) node of two given nodes in the
BST.

According to the definition of LCA on Wikipedia: “The lowest common ancestor is defined between two nodes p and q as the
lowest node in T that has both p and q as descendants (where we allow a node to be a descendant of itself).â€​

Brute Force Solution: One straightforward approach to solve this problem is to find the path from the root to each of the given
nodes. Once you have the paths, you can iterate through them and find the first common node, which will be the LCA. However,
this approach requires O(N) time complexity, where N is the number of nodes in the BST.

Optimized Solution: A more efficient approach to finding the LCA is to utilize the properties of a BST. By recursively traversing
the BST, you can check if the LCA exists in the left or right subtree, or if it is the current node. This approach reduces the time
complexity to O(log N) since it only traverses the nodes on the path from the root to the LCA.

Question 8

Problem Statement : Given an array of integers preorder, which represents the preorder traversal of a BST (i.e., binary search
tree), construct the tree and return its root.

It is guaranteed that there is always possible to find a binary search tree with the given requirements for the given test cases.

A binary search tree is a binary tree where for every node, any descendant of Node.left has a value strictly less than Node.val,
and any descendant of Node.right has a value strictly greater than Node.val.

A preorder traversal of a binary tree displays the value of the node first, then traverses Node.left, then traverses Node.right.

Brute Force Solution

The brute force solution is to generate all possible binary search trees from the given preorder traversal and then check which
one matches the given preorder traversal. The number of possible binary search trees for n nodes is given by the Catalan
number C(n). For n = 5, the Catalan number is 42. So, the time complexity of the brute force solution is exponential, O(C(n)*n!).

Optimized Solution

The optimized solution is based on the observation that the root of the tree is the first element in the preorder traversal. Once
we know the root, we can divide the remaining elements into two parts: the left subtree and the right subtree. We can then
recursively construct the left and right subtrees. The time complexity of the optimized solution is O(n^2), where n is the number
of nodes in the tree.

Question 9

Problem Statement : Given the root of a binary search tree and an integer k, return true if there exist two elements in the BST
such that their sum is equal to k, or false otherwise.

Brute Force Solution: The brute force approach involves exhaustively searching all pairs of elements in the BST and checking if
their sum equals k.

It starts at the root, compares the current node's value with the target sum, and recursively explores both the left and the right
subtree. Time complexity: O(n^2), where n is the number of nodes in the BST.
Optimized Solution: The optimized solution utilizes the property of a BST, where elements are stored in ascending order. It
employs a modified inorder traversal, maintaining a pointer called 'current' that starts at the smallest element. If the sum of
'current' and 'target' is less than k, it moves 'current' right, and if it's greater, it moves left. If it's equal, it returns true. This
solution has a time complexity of O(n), where n is the number of nodes in the BST.

Question 10

Problem Statement : You are given the root of a binary search tree (BST), where the values of exactly two nodes of the tree
were swapped by mistake. Recover the tree without changing its structure.

Brute Force Solution: In the brute force approach, we perform an inorder traversal of the tree and store the values of the nodes
in an array. We then sort the array in ascending order and find the two elements that are out of order. Once we have identified
the two swapped elements, we can swap them back in the tree to recover the BST.

Optimized Solution: The optimized solution uses a recursive approach to recover the BST. The function takes as input a node in
the tree and returns a tuple of three values: the minimum value in the subtree rooted at the node, the maximum value in the
subtree rooted at the node, and a boolean value indicating whether the subtree rooted at the node is a BST. The function then
uses these values to check if the subtree rooted at the node is a BST. If it is not a BST, the function swaps the values of the two
swapped nodes and returns a tuple indicating that the tree has been recovered.

Question 11

Problem Statement : Given a binary tree. Find the size of its largest subtree which is a Binary Search Tree. Note: Here Size
equals the number of nodes in the subtree.

Brute Force Approach: The straightforward approach is to recursively traverse the tree and for each node, check if its left and
right subtrees are also BSTs. If they are, then add the size of the left and right subtrees to the size of the current node and return
this value. Otherwise, return -1. The time complexity of this approach is O(N^2), where N is the number of nodes in the tree.

Optimized Approach: The above approach can be optimized by using the concept of Dynamic Programming. In this approach,
we will store the size of the largest BST subtree for each node in the tree. This way, when we visit a node, we can directly return
the stored size of its largest BST subtree. If the current node is not part of a BST, then we store -1 as its size. The time complexity
of this approach is O(N), where N is the number of nodes in the tree.

Binary Search
Question 1

Problem Statement : Given a sorted array of N integers, write a program to find the index of the last occurrence of the target
key. If the target is not found then return -1.

Brute Force Solution: Traverse the entire array from the last index to the first index and compare each element with the target
key. If the target key is found, return the current index. Otherwise, return -1. The time complexity of this solution is O(N), where
N is the number of elements in the array.

Optimized Solution:

Use binary search to find the last occurrence of the target key.

Start by setting the low index to 0 and the high index to N-1.
While the low index is less than or equal to the high index, do the following:
Calculate the mid index as the average of the low index and the high index.
Compare the element at the mid index with the target key.
If the element at the mid index is equal to the target key, update the last_occurrence index to the mid index.
If the element at the mid index is less than the target key, set the low index to the mid index plus 1.
If the element at the mid index is greater than the target key, set the high index to the mid index minus 1.
If the last_occurrence index is not updated, it means that the target key is not found in the array. Return -1.
Otherwise, return the last_occurrence index.

The time complexity of this optimized solution is O(log N), which is significantly better than the brute force solution.

Question 2

Problem Statement : Given an integer array arr of size N, sorted in ascending order (with distinct values) and a target value k.
Now the array is rotated at some pivot point unknown to you. Find the index at which k is present and if k is not present return
-1.

Brute Force Approach: This approach involves iterating through the entire array and checking if it contains the target value. If
the target value is found at any index, the index is returned. If the target value is not found after iterating through the entire
array, -1 is returned.

Optimized Approach (Binary Search): The optimized approach takes advantage of the fact that the array is already sorted and
rotated. The approach works as follows: 1. Iterate until the low index is less than or equal to the high index. 2. Calculate the mid
index. 3. Check if the target value is equal to the value at the mid index. If it is, return the mid index. 4. Check if the value at the
mid index is greater than the value at the low index. If it is, then the first half of the array is sorted. 5. If the value at the mid
index is less than the value at the low index, then the second half of the array is sorted. 6. Check if the target value is greater
than the value at the low index and less than or equal to the value at the mid index. If it is, then the target value is in the first
half of the array. 7. Otherwise, the target value is in the second half of the array. 8. Update the low or high index accordingly. 9.
If the target value is not found after iterating through the array, return -1.

Question 3

Problem Statement : Given an integer array arr of size N, sorted in ascending order (may contain duplicate values) and a target
value k. Now the array is rotated at some pivot point unknown to you. Return True if k is present and otherwise, return False.

Brute Force Solution:

We can traverse the array linearly and compare each element with k. If k is found at any position, we return True; otherwise, we
return False. The time complexity of this brute force approach is O(N), where N is the size of the array.

Optimized Solution:

Using Binary Search:

We can use a modified binary search to efficiently find k in the rotated array. The binary search algorithm divides the array in
half repeatedly until k is found or the search space is exhausted. The time complexity of this optimized solution is O(logN),
where N is the size of the array.

Question 4

Problem Statement : Given an integer array arr of size N, sorted in ascending order (with distinct values). Now the array is
rotated between 1 to N times which is unknown. Find the minimum element in the array.

Brute Force: Iterate through the array and find the minimum element. Time complexity: O(N).

Optimized Solution: Take advantage of the sorted and rotated nature of the array. Use binary search to efficiently find the
minimum element. The idea is to keep dividing the search space in half until the minimum element is found. Time complexity:
O(log N).

Question 5

Problem Statement : Given an integer array arr of size N, sorted in ascending order (with distinct values). Now the array is
rotated between 1 to N times which is unknown. Find how many times the array has been rotated.

Brute Force Approach:

Iterate from 0 to N-1.


For each index 'i', check if arr[i] > arr[i+1]. If so, increment the count of rotations.
Return the count.

Optimized Approach:

Use binary search to find the minimum element in the rotated array.
The minimum element is located at index 'i' where arr[i] < arr[i-1].
The number of rotations is equal to 'i'.

The optimized approach has a time complexity of O(logN), which is significantly faster than the brute force approach with O(N)
time complexity.

Question 6

Problem Statement : Given an array of N integers. Every number in the array except one appears twice. Find the single number
in the array.
Brute Force Approach:

Iterate over each element in the array and count its frequency. The element with a frequency of 1 is the single number. This
approach has a time complexity of O(N^2), where N is the length of the array.

Optimized Approach:

Use bitwise XOR to find the single number. XOR two identical numbers will result in 0, while XORing a number with itself will
give the number back. Iterate over the array and perform XOR on each element with the result. The final result will be the single
number. This approach has a time complexity of O(N) and a space complexity of O(1).

Question 7

Problem Statement : Given an array of length N. Peak element is defined as the element greater than both of its neighbors.
Formally, if 'arr[i]' is the peak element, 'arr[i - 1]' < 'arr[i]' and 'arr[i + 1]' < 'arr[i]'. Find the index(0-based) of a peak element in
the array. If there are multiple peak numbers, return the index of any peak number.

Brute Force:

The brute force approach involves iterating through the entire array and checking if each element is greater than its neighbors.
If it is, the index of the element is returned as the peak. This approach has a time complexity of O(N), where N is the length of the
array.

Optimized Solution:

A more efficient approach is to use a divide-and-conquer strategy. The array is divided into two halves, and the peak is found in
each half recursively. The two peak indices are then compared, and the larger of the two is returned as the peak index. This
approach has a time complexity of O(log N), significantly faster than the brute force approach for large arrays.

Question 8

Problem Statement : Given two numbers N and M, find the Nth root of M. The nth root of a number M is defined as a number X
when raised to the power N equals M. If the 'nth root is not an integer, return -1.

Brute Force: Iterate through all integers from 1 to M. For each integer X, check if X^N is equal to M. This approach has a time
complexity of O(M^N), making it impractical for large values of N.

Optimized Solution: Use binary search to find the Nth root of M. Start by setting the search range to [1, M]. While the range is
valid, calculate the midpoint X. Check if X^N is equal to M. If it is, return X. Otherwise, adjust the search range based on the
comparison result. This approach has a time complexity of O(N * log(M)), which is significantly faster than the brute force
approach, especially for large values of N.

Question 9

Problem Statement : Given a row-wise sorted matrix of size MXN, where M is no. of rows and N is no. of columns, find the
median in the given matrix.

Brute Force Solution: The simplest solution is to create an array of size MN by copying all the elements from the given matrix
into it. Then, sort the elements of this array in O(MN log (MN)) time using any sorting algorithm such as Merge Sort or Quick Sort,
and finally return the middle element from the sorted array. This approach is not efficient as it requires extra space O(MN) and time
complexity O(MN log (MN)).

The optimized solution is to use Binary Search over the element range to find the median. It begins by finding the minimum
and maximum elements of the matrix. Next, the algorithm computes the median by repeatedly diving the range of possible
median values in the matrix. At each step, it calculates the mid value of the current range, counts the number of elements
smaller than the mid value, and compares it to the desired rank of the median. If the count is less than the desired rank, the
lower limit of the range is updated to mid + 1. Otherwise, the upper limit is updated to mid. This process continues until the
desired rank is achieved, ensuring that the median is found efficiently in O(log(N * M)) time complexity.

Question 10

Problem Statement : Given an array of N integers. Every number in the array except one appears twice. Find the single number
in the array.

Brute Force Solution:

The brute force approach is to traverse the array and for each element, search for another occurrence of the same element. If
found, both occurrences can be skipped. Repeat this process until a single number remains, which is the required single
number. This solution has a time complexity of O(N^2), where N is the number of elements in the array.

Optimized Solution (Bit Manipulation):

A more efficient solution utilizes bit manipulation. XORing (^) two identical numbers results in 0, while XORing a number with 0
leaves the number unchanged. Initialize a variable to store the result initially set to 0. Iterate over the array, and for each
element, XOR it with the result. Since all duplicate elements will cancel out, the result after the loop will be the single number.
This solution has a time complexity of O(N) and a constant space complexity of O(1).

Question 11

Problem Statement : Given an integer array arr of size N, sorted in ascending order (with distinct values) and a target value k.
Now the array is rotated at some pivot point unknown to you. Find the index at which k is present and if k is not present return
-1.

The brute force approach is to simply iterate through the array and check if each element is equal to the target. If the target is
found, then the index of the target is returned. Otherwise, -1 is returned. The time complexity of this approach is O(n) where n is
the size of the array.

The optimized approach is to use binary search. Binary search is a search algorithm that works by repeatedly dividing the
search space in half until the target is found. The time complexity of binary search is O(log n). Here are the steps for performing
binary search on a rotated array:

1. Initialize the left and right pointers to the beginning and end of the array, respectively.
2. While the left pointer is less than or equal to the right pointer, do the following:
3. Calculate the middle index between the left and right pointers.
4. If the element at the middle index is equal to the target, then return the middle index.
5. If the element at the middle index is less than the target, then set the left pointer to the middle index plus one.
6. If the element at the middle index is greater than the target, then set the right pointer to the middle index minus one.
7. If the target is not found, then return -1.

Question 12

Problem Statement : Given two sorted arrays arr1 and arr2 of size m and n respectively, return the median of the two sorted
arrays. The median is defined as the middle value of a sorted list of numbers. In case the length of the list is even, the median is
the average of the two middle elements.

Brute Force Solution:

The brute force solution combines the two sorted arrays into a single array and then sorts the combined array. The median can
then be found by selecting the middle element if the length of the combined array is odd, or the average of the two middle
elements if the length is even. This solution has a time complexity of O(m + n), where m and n are the lengths of the two input
arrays.

Optimized Solution (Merge and Select):

The optimized solution leverages the fact that the arrays are already sorted. It uses a merge-sort-like approach to compare the
elements from the two arrays and select the median without having to create a combined array. This solution has a time
complexity of O(m + n).

Question 13

Problem Statement : Given two sorted arrays of size m and n respectively, you are tasked with finding the element that would
be at the kth position of the final sorted array.

Brute Force Approach: The most straightforward approach to this problem is to merge the two sorted arrays into a single
sorted array of size (m+n). We can do this by iterating through both arrays and comparing the elements at each step. Once the
two arrays are merged, we can simply return the kth element of the merged array. The time complexity of the brute force
approach is O(m+n), as it takes O(m+n) time to merge the two arrays and O(1) time to return the kth element.

Optimized Approach: A more efficient approach to this problem is to use the fact that the two arrays are sorted. We can start by
comparing the first elements of the two arrays. If the first element of the first array is smaller than the first element of the
second array, then we know that the first element of the first array must be the kth smallest element. Otherwise, we know that
the first element of the second array must be the kth smallest element. We can then discard the smaller of the two first elements
and continue comparing the remaining elements of the two arrays. We can continue this process until we have found the kth
smallest element. The time complexity of the optimized approach is O(log(m+n)), as it takes O(log(m+n)) time to find the kth
smallest element.
Question 14

Problem Statement : Given an array ‘arr of integer numbers, ‘ar[i]’ represents the number of pages in the ‘i-th’
book. There are a ‘m’ number of students, and the task is to allocate all the books to the students.

Brute Force: The brute-force approach is to try all possible combinations of allocating books to students. For each combination,
calculate the maximum number of pages allocated to a student and check if it is within the given limit. If it is, then the
combination is valid, otherwise, discard it. The time complexity of this approach is O(n^m), where 'n' is the number of books and
'm' is the number of students.

Optimized using Greedy Approach: The optimized solution starts by sorting the books in ascending order of the number of
pages. Then, it iteratively assigns books to students until all books are assigned. In each iteration, it assigns the book with the
smallest number of pages to the student with the minimum number of pages assigned so far. This process is repeated until all
books are assigned. The time complexity of this approach is O(n log n), where 'n' is the number of books.

Question 15

Problem Statement : You are given an array 'arr' of size 'n' which denotes the position of stalls. You are also given an integer 'k'
which denotes the number of aggressive cows. You are given the task of assigning stalls to 'k' cows such that the minimum
distance between any two of them is the maximum possible. Find the maximum possible minimum distance.

Brute Force Solution:

This solution tries all possible combinations of stalls to place the cows. For each combination, it calculates the minimum distance
between any two cows. The maximum of these minimum distances is the desired answer. The time complexity of this solution is
O(n^k), where n is the number of stalls and k is the number of cows.

Optimized Solution:

This solution uses a binary search to find the maximum possible minimum distance. It starts by sorting the array of stalls. Then,
it initializes the low and high pointers to 1 and n, respectively. The midpoint between low and high is used to calculate the
minimum distance between any two cows. If the minimum distance is greater than or equal to the current maximum, the low
pointer is moved to the midpoint plus 1. Otherwise, the high pointer is moved to the midpoint minus 1. The binary search
terminates when the low and high pointers cross each other. The maximum value of the minimum distance is the desired
answer. The time complexity of this solution is O(nlogn).

Linked List
Question 1

Problem Statement : Given the head of a singly linked list, write a program to reverse the linked list, and return the head
pointer to the reversed list.

Brute Force Solution:

Brute force solution involves reversing the pointers of each node. We start by initializing three pointers: prev, curr, and next.
Initially, prev is None, curr points to the head, and next points to curr.next. We then iterate through the list, updating the pointers
as follows: - curr.next = prev - prev = curr - curr = next

Once we reach the end of the list, curr will be None, and prev will be pointing to the new head of the reversed list. The time
complexity of this solution is O(n), where n is the number of nodes in the linked list.

Optimized Solution (Iterative):

An optimized iterative approach involves reversing the pointers of each node while traversing the list only once. We initialize
prev to None and curr to the head. We then iterate through the list, updating the pointers as follows: - next = curr.next - curr.next
= prev - prev = curr - curr = next

Once we reach the end of the list, prev will be pointing to the new head of the reversed list. The time complexity of this optimized
solution is still O(n), but it is more efficient than the brute force solution since it only iterates through the list once.

Question 2

Problem Statement : Given two sorted linked lists, merge them to produce a combined sorted linked list. Return the head of the
final linked list created.
Brute Force Solution:

The brute force solution involves iteratively comparing the nodes of the two linked lists and merging them into a single sorted
list. This is done by creating a new list, starting from the head, and comparing each node from both linked lists. The node with
the smaller value is added to the new list, and the corresponding pointer is advanced. This process continues until all nodes
from both linked lists have been added to the new list.

Optimized Solution:

An optimized solution is to use a recursive approach, which leverages the already sorted nature of the linked lists. The recursive
function takes two parameters: the heads of the two linked lists. If either list is empty, the other list is returned as the merged
list. Otherwise, the function compares the values of the two heads. The head with the smaller value is added to the merged list,
and the recursive function is called with the rest of the corresponding linked list. This process continues until both linked lists
have been merged.

Question 3

Problem Statement : Given a linked list and an integer N, the task is to delete the Nth node from the end of the linked list and
print the updated linked list.

Brute Force Solution:

Traverse the linked list to find the length of the linked list, let's say it's L.
Now, traverse the linked list again and count L - N nodes from the beginning. The Nth node from the end will be reached.
Delete the Nth node from the end.

Optimized Solution:

Use two pointers, slow and fast. Initialize both pointers to the head of the linked list.
Move the fast pointer N nodes ahead of the slow pointer.
Now, move both pointers simultaneously until the fast pointer reaches the end of the linked list.
At this point, the slow pointer will be pointing to the Nth node from the end of the linked list. Delete the Nth node from the
end.

Question 4

Problem Statement : Given the heads of two non-empty linked lists representing two non-negative integers. The digits are
stored in reverse order, and each of their nodes contains a single digit. Add the two numbers and return the sum as a linked list.

Brute force approach: Consider the two numbers as strings. Convert them to integers, add them, and then convert the result
back to a linked list. While simple, this solution is inefficient, especially for large numbers.

Optimized approach: Iteratively add the digits of the two lists, carrying over any overflow. Store each result in a new node and
link them to form the sum linked list. This approach is more efficient as it avoids converting to and from strings, and it works for
any size of numbers.

Question 5

Problem Statement : Write a function to delete a node in a singly-linked list. You will not be given access to the head of the list
instead, you will be given access to the node to be deleted directly. It is guaranteed that the node to be deleted is not a tail node
in the list.

Brute force approach: One brute force approach to delete a node in a singly-linked list is to iterate through the list until you
find the node to be deleted. Once you have found the node, you can then update the next pointer of the previous node to point to
the node after the one to be deleted. This approach has a time complexity of O(n), where n is the number of nodes in the list.

Optimized approach: A more efficient approach to delete a node in a singly-linked list is to use a dummy node. A dummy node
is a node that is added to the beginning of the list and is used to simplify the deletion process. To delete a node using a dummy
node, you can simply update the next pointer of the dummy node to point to the node after the one to be deleted. This approach
has a time complexity of O(1), as it does not require iterating through the list.

Question 6

Problem Statement : Given the heads of two singly linked-lists headA and headB, return the node at which the two lists
intersect. If the two linked lists have no intersection at all, return null.

Brute Force:
Traverse the first linked list and for each node, traverse the second linked list to check if there's an intersection. Time
complexity: O(m*n), where m and n are the lengths of the two linked lists.

Optimized:

Use two pointers, one for each linked list. Move both pointers forward at the same pace. If one pointer reaches the end of its list,
move it to the head of the other list. This ensures that both pointers will meet at the intersection node if it exists. Time
complexity: O(m+n), where m and n are the lengths of the two linked lists. This approach is faster than the brute force solution
because it traverses each list only once.

Question 7

Problem Statement : Detect a Cycle in a Linked List

Brute Force Solution:

This approach involves iterating through the linked list and maintaining a set of visited nodes. For each node, check if it has
been visited before. If yes, then there's a cycle in the list. The time complexity of this solution is O(n^2), where n is the number of
nodes in the list, as it iterates through the list twice for each node.

Optimized Solution:

The Floyd's tortoise and hare algorithm is an efficient way to detect cycles in a linked list. It uses two pointers, a "slow" and a
"fast" pointer. The slow pointer moves one node at a time, while the fast pointer moves two nodes at a time. If there's a cycle in
the list, the slow and fast pointers will eventually meet at the same node. This approach has a time complexity of O(n), where n is
the number of nodes in the list.

Question 8

Problem Statement : Given the head of a singly linked list of n nodes and an integer k, where k is less than or equal to n. Your
task is to reverse the order of each group of k consecutive nodes, if n is not divisible by k, then the last group of remaining nodes
should remain unchanged.

Brute Force Approach: The brute force approach involves reversing each group of k nodes individually by repeatedly swapping
the curr and prev pointers until k nodes are reversed. This process is repeated for each group of k nodes until the end of the list is
reached.

``` ListNode reverseKGroup(ListNode head, int k) { if (!head || k <= 1) return head; int len = 0; ListNode* p = head; while (p) {
len++; p = p->next; }

ListNode* dummy = new ListNode(0);


dummy->next = head;
ListNode* prev = dummy, *curr, *next;
for (int i = 0; i < len / k; i++) {
curr = prev->next;
next = curr->next;
for (int j = 1; j < k; j++) {
curr->next = next->next;
next->next = prev->next;
prev->next = next;
next = curr->next;
}
prev = curr;
}
return dummy->next;

} ```

Optimized Approach: An optimized approach is to use a stack to store the reversed nodes. We start by iterating through the
linked list and pushing the first k nodes onto the stack. Then, we pop the nodes from the stack and insert them into the linked
list, reversing the order of the first k nodes. We continue this process for each group of k nodes until the end of the list is reached.

ListNode* reverseKGroup(ListNode* head, int k) { stack<ListNode*> stk; ListNode* p = head; ListNode* prev = NULL; while (p) {
for (int i = 0; i < k && p; i++) { stk.push(p); p = p->next; } while (!stk.empty()) { if (prev == NULL) head = stk.top();
else prev->next = stk.top(); prev = stk.top(); stk.pop(); } } if (prev) prev->next = NULL; return head; }

Question 9

Problem Statement : Check if the given Linked List is Palindrome

Brute Force Solution: Traverse the linked list and store the values in an array. Then reverse the array and compare it with the
original array. If both arrays are equal, then the linked list is a palindrome. The time complexity of this solution is O(n), where n
is the number of nodes in the linked list.

Optimized Solution: Use two pointers, one starting from the head and the other from the tail. Traverse the linked list and
compare the values pointed by the two pointers. If they are equal, then move both pointers towards the middle of the linked list.
If the pointers meet and their values are equal, then the linked list is a palindrome. The time complexity of this solution is O(n),
where n is the number of nodes in the linked list.

Question 10

Problem Statement : Given the head of a linked list that may contain a cycle, return the starting point of that cycle. If there is no
cycle in the linked list return null.

Brute Force Approach: Iterate through the linked list, maintaining a set of visited nodes. If a node is encountered that is already
in the set, then a cycle has been found and the node is the starting point of the cycle.

Optimized Approach (Floyd's Tortoise and Hare Algorithm): Maintain two pointers, slow and fast, that move through the
linked list at different speeds. If there is a cycle, the pointers will eventually meet at the same node, which is the starting point of
the cycle. If there is no cycle, the fast pointer will eventually reach the end of the linked list, allowing for an early termination of
the algorithm.

Question 11

Problem Statement : Given a linked list containing ‘N’ head nodes where every node in the linked list contains two
pointers:

Brute Force:

In the brute force approach, for every node we can traverse the whole linked list and find the next node's data that is to be
swapped. This approach is easy to implement but has a time complexity of O(N^2).

Optimized Approach:

In the optimized approach, we use a hashmap to store the nodes that we have already visited. Whenever we encounter a node,
we check if it is already present in the hashmap. If it is present, then we swap the data of the current node and the node present
in the hashmap. If it is not present, we insert the current node into the hashmap. This approach has a time complexity of O(N).

Question 12

Problem Statement : Given the head of a linked list, rotate the list to the right by k places.

Brute Force Approach:

The brute force approach involves iterating through the linked list k times, moving the last element to the front in each iteration.
This is a simple and straightforward solution, but it has a time complexity of O(n*k), where n is the number of nodes in the list.

Optimized Approach:

A more efficient approach uses a combination of two techniques: 1. Finding the length of the list: Iterate through the list to find
its length, say m. 2. Calculating the new starting point: Subtract k from m and find the remainder when dividing by m. This
index represents the new starting point of the rotated list.

By using this approach, we can avoid unnecessary iterations and achieve a time complexity of O(n), where n is the number of
nodes in the list.

Question 13

Problem Statement : Given a linked list where every node in the linked list contains two pointers:

Brute Force Solution: Iterate through the linked list and for each node, check if its next pointer points to a valid node. If it
doesn't, then the next pointer points to an invalid node and needs to be fixed. Time complexity: O(n), where n is the number of
nodes in the linked list.

Optimized Solution: Use a hash table to keep track of the nodes that have already been visited. Iterate through the linked list
and for each node, check if it has been visited before. If it has been visited before, then there is a cycle in the linked list. Time
complexity: O(n), where n is the number of nodes in the linked list.

Question 14

Problem Statement : Given an array of N integers, your task is to find unique triplets that add up to give a sum of zero. In short,
you need to return an array of all the unique triplets [arr[a], arr[b], arr[c]] such that i!=j, j!=k, k!=i, and their sum is equal to zero.

Brute Force Solution:

The brute force approach is to consider all possible triplets and check if their sum is zero. For each triplet, if the sum is zero, add
it to the result list. The complexity of this approach is O(N^3), where N is the number of elements in the array.

Optimized Solution:

The optimized solution is based on sorting the array and then using a two-pointer approach to find triplets that add up to zero.
Start with the first element as the smallest element of the triplet. For each element, find the remaining two elements using
binary search or two pointers. The complexity of this approach is O(N^2).

Question 15

Problem Statement : Given an array of non-negative integers representation elevation of ground. Your task is to find the water
that can be trapped after rain.

Brute Force Solution:

This approach involves iterating through each element in the array to determine the maximum height to the left and to the right.
The difference between the minimum of these two heights and the current element's height represents the amount of water
trapped at that point. By summing these values for all elements, we obtain the total amount of water trapped.

Optimized Solution:

A more efficient solution involves using two pointers, one starting from the left end and the other from the right end of the
array. Both pointers are initialized to the corresponding end elements. We maintain two variables, 'left_max' and 'right_max', to
track the maximum heights encountered so far from the left and right sides, respectively. While the two pointers have not met,
we compare the heights at the current positions. If the left_max is less than the right_max, the potential water trapped at the left
index is calculated. Otherwise, the right index is considered. The pointer with the smaller neighboring height is then moved
towards the center, updating the corresponding left_max or right_max value. This process continues until the pointers meet, and
the sum of the trapped water at each step gives the total amount of water trapped.

Question 16

Problem Statement : Given an integer array sorted in non-decreasing order, remove the duplicates in place such that each
unique element appears only once. The relative order of the elements should be kept the same.

Brute Force Solution: In the brute force approach, we linearly traverse the array and check each element with the previous
elements. If a duplicate is found, we remove it from the array. This approach has a time complexity of O(n^2), where n is the
length of the array.

Optimized Solution: A more efficient approach is to use a hash table to keep track of the elements we have encountered. We
traverse the array linearly and insert each element into the hash table. If the element is already present in the hash table, we
skip it. This approach has a time complexity of O(n), where n is the length of the array, since we only traverse the array once.

Question 17

Problem Statement : Given an array that contains only 1 and 0 return the count of maximum consecutive ones in the array.

Brute Force Algorithm:

The brute force algorithm iterates through the array and counts the consecutive ones for each starting index. The maximum
count is then returned. This approach has a time complexity of O(n^2), where n is the length of the array.

Optimized Algorithm:

The optimized algorithm uses a variable to keep track of the current consecutive ones count and a maximum count variable to
track the overall maximum. As it iterates through the array, it increments the current count if the current element is 1, and
resets it to 0 if it encounters a 0. The maximum count is updated with the maximum value between the current count and the
maximum count. This approach has a time complexity of O(n), where n is the length of the array.

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy