Tip: rewrite the code using chatGPT and compare the difference
DFS
BFS
Problem
sub-Problem
sub-Problem
sub-Problem
sub-sub-Problem
sub-sub-Problem
sub-sub-sub-Problem
base-
problem
Search Order:
Search Order:
Problem
sub-Problem
sub-Problem
sub-Problem
sub-sub-Problem
sub-sub-Problem
sub-sub-Problem
sub-sub-Problem
Search Order:
layer by layer
Search Order:
layer by layer
A
/ \
B C
/ \ / \
D E F G
/ \
H I
DFS Search Order:
U, R, D, L
DFS Search Order:
U, R, D, L
BFS Search Order
BFS Search Order
DFS and BFS are both algorithms for searching TREE and GRAPH, but we will ONLY focus on trees, which is more common in interviews.
A
B
C
F
E
D
Given a binary tree, calculate its depth.
1
2
3
Depth: 3
1
2
3
4
Depth: 4
3
2
3
3
4
1
2
3
4
2
Depth: 4
Given a binary tree, calculate its depth.
public int depth(TreeNode root) {
if (root == null) {
return 0;
}
return Math.max(depth(root.left), depth(root.right)) + 1;
}
1
2
3
4
Depth: 4
3
2
3
3
4
Given a binary tree, calculate its depth.
def depth(root):
if root is None:
return 0
return max(depth(root.left), depth(root.right)) + 1
1
2
3
4
Depth: 4
3
2
3
3
4
Given a binary tree, determine if it is height-balanced.
For this problem, a height-balanced binary tree is defined as a binary tree in which the depth of the two subtrees of every node never differ by more than 1.
1
2
2
3
3
4
4
False
1
2
3
4
True
3
2
3
3
4
Given a binary tree, determine if it is height-balanced.
For this problem, a height-balanced binary tree is defined as a binary tree in which the depth of the two subtrees of every node never differ by more than 1.
Given a binary tree, determine if it is height-balanced.
For this problem, a height-balanced binary tree is defined as a binary tree in which the depth of the two subtrees of every node never differ by more than 1.
public boolean isBalanced(TreeNode root) {
if (root == null)
return true;
if (Math.abs(getDepth(root.left) - getDepth(root.right)) > 1)
return false;
return isBalanced(root.left) && isBalanced(root.right);
}
int getDepth(TreeNode root) {
if (root == null)
return 0;
return Math.max(getDepth(root.left), getDepth(root.right)) + 1;
}
Given a binary tree, determine if it is height-balanced.
For this problem, a height-balanced binary tree is defined as a binary tree in which the depth of the two subtrees of every node never differ by more than 1.
def is_balanced(root):
if root is None:
return True
if abs(get_depth(root.left) - get_depth(root.right)) > 1:
return False
return is_balanced(root.left) and is_balanced(root.right)
def get_depth(root):
if root is None:
return 0
return max(get_depth(root.left), get_depth(root.right)) + 1
Given a binary tree, determine if it is height-balanced.
For this problem, a height-balanced binary tree is defined as a binary tree in which the depth of the two subtrees of every node never differ by more than 1.
public boolean isBalanced(TreeNode root) {
return depth(root) != -1;
}
public int depth(TreeNode root) {
if (root == null) {
return 0;
}
int leftDepth = depth(root.left);
int rightDepth = depth(root.right);
if (leftDepth == -1 || rightDepth == -1 ||
Math.abs(leftDepth - rightDepth) > 1) {
return -1;
}
return Math.max(leftDepth, rightDepth) + 1;
}
Given a binary tree, determine if it is height-balanced.
For this problem, a height-balanced binary tree is defined as a binary tree in which the depth of the two subtrees of every node never differ by more than 1.
def is_balanced(root):
return depth(root) != -1
def depth(root):
if root is None:
return 0
left_depth = depth(root.left)
right_depth = depth(root.right)
if left_depth == -1 or right_depth == -1 or abs(left_depth - right_depth) > 1:
return -1
return max(left_depth, right_depth) + 1
Given a binary tree, check whether it is a mirror of itself (ie, symmetric around its center).
1 / \ 2 2 / \ / \ 3 4 4 3
True
1 / \ 2 2 \ \ 3 3
False
1 / \ 2 2 / \ / \ 4 3 4 3
False
1 / \ 2 2 / \ 3 3
True
Given a binary tree, check whether it is a mirror of itself (ie, symmetric around its center).
1 / \ 2 2 / \ / \ 3 4 4 3
True
1 / \ 2 2 \ \ 3 3
False
Given a binary tree, check whether it is a mirror of itself (ie, symmetric around its center).
public boolean isSymmetric(TreeNode root) {
if (root == null) {
return true;
}
return isSymmetric(root.left, root.right);
}
public boolean isSymmetric(TreeNode left, TreeNode right) {
if (left == null && right == null) {
return true;
}
if (left == null && right != null) {
return false;
}
if (left != null && right == null) {
return false;
}
if (left.val != right.val) {
return false;
}
return isSymmetric(left.left, right.right)
&& isSymmetric(left.right, right.left);
}
1 / \ 2 2 / \ / \ 3 4 4 3
True
1 / \ 2 2 \ \ 3 3
False
Given a binary tree, check whether it is a mirror of itself (ie, symmetric around its center).
def is_symmetric(root):
if root is None:
return True
return is_symmetric_helper(root.left, root.right)
def is_symmetric_helper(left, right):
if left is None and right is None:
return True
if left is None or right is None:
return False
if left.val != right.val:
return False
return is_symmetric_helper(left.left, right.right) and is_symmetric_helper(left.right, right.left)
Given a binary tree, find its minimum depth.
The minimum depth is the number of nodes along the shortest path from the root node down to the nearest leaf node.
1 / \ 2 2 / \ / \ 3 4 4 3
3
1 / \ 2 2 / \ 3 4
2
1 / \ 2 2 / \ 3 3
3
Given a binary tree, find its minimum depth.
The minimum depth is the number of nodes along the shortest path from the root node down to the nearest leaf node.
Given a binary tree, find its minimum depth.
The minimum depth is the number of nodes along the shortest path from the root node down to the nearest leaf node.
public int minDepth(TreeNode root) {
if (root == null) {
return 0;
}
int leftDepth = minDepth(root.left);
int rightDepth = minDepth(root.right);
if (leftDepth == 0) {
return rightDepth + 1;
} else if (rightDepth == 0) {
return leftDepth + 1;
}
return Math.min(leftDepth, rightDepth) + 1;
}
Given a binary tree, find its minimum depth.
The minimum depth is the number of nodes along the shortest path from the root node down to the nearest leaf node.
def min_depth(root):
if root is None:
return 0
left_depth = min_depth(root.left)
right_depth = min_depth(root.right)
if left_depth == 0:
return right_depth + 1
elif right_depth == 0:
return left_depth + 1
return min(left_depth, right_depth) + 1
Given a binary tree and a sum, determine if the tree has a root-to-leaf path such that adding up all the values along the path equals the given sum.
5 / \ 4 8 / / \ 11 13 4 / \ \ 7 2 1
22: [5, 4, 11, 2]
3 / \ 2 6 / / \ 9 22 9 / \ \ 7 2 1
31: [3, 6, 22]
Given a binary tree and a sum, determine if the tree has a root-to-leaf path such that adding up all the values along the path equals the given sum.
5 / \ 4 8 / / \ 11 13 4 / \ \ 7 2 1
22: [5, 4, 11, 2]
Knapsack:
Given a binary tree and a sum, determine if the tree has a root-to-leaf path such that adding up all the values along the path equals the given sum.
5 / \ 4 8 / / \ 11 13 4 / \ \ 7 2 1
22: [5, 4, 11, 2]
public boolean hasPathSum(TreeNode root, int sum) {
if (root == null) {
return false;
}
if (root.left == null && root.right == null) {
if (sum == root.val) {
return true;
}
return false;
}
return hasPathSum(root.left, sum-root.val) ||
hasPathSum(root.right, sum-root.val);
}
Given a binary tree and a sum, determine if the tree has a root-to-leaf path such that adding up all the values along the path equals the given sum.
5 / \ 4 8 / / \ 11 13 4 / \ \ 7 2 1
22: [5, 4, 11, 2]
def has_path_sum(root, target_sum):
if root is None:
return False
if root.left is None and root.right is None:
return target_sum == root.val
return (has_path_sum(root.left, target_sum - root.val) or
has_path_sum(root.right, target_sum - root.val))
Given a binary tree, find the maximum path sum.
For this problem, a path is defined as any sequence of nodes from some starting node to any node in the tree along the parent-child connections. The path does not need to go through the root.
5 / \ 4 8 / / \ 11 13 4 / \ \ 7 2 1
maxSum = 7 + 11 + 4 + 5 + 8 + 13 = 48
5 / \ 4 8 / / \ 11 13 4 / \ \ 7 2 1
Given a binary tree, find the maximum path sum.
For this problem, a path is defined as any sequence of nodes from some starting node to any node in the tree along the parent-child connections. The path does not need to go through the root.
5 / \ -4 -8
5 / \ 4 -8
5 / \ 4 8
5 / \ -4 8
Given a binary tree, find the maximum path sum.
For this problem, a path is defined as any sequence of nodes from some starting node to any node in the tree along the parent-child connections. The path does not need to go through the root.
Given a binary tree, find the maximum path sum.
For this problem, a path is defined as any sequence of nodes from some starting node to any node in the tree along the parent-child connections. The path does not need to go through the root.
int max_sum = Integer.MIN_VALUE;
public int maxPathSum(TreeNode root) {
if (root == null)
return 0;
max_sum = Integer.MIN_VALUE;
maxBranchSum(root);
return max_sum;
}
public int maxBranchSum(TreeNode root) {
if (root == null) {
return 0;
}
int leftSum = maxBranchSum(root.left);
int rightSum = maxBranchSum(root.right);
int branchMaxSum = root.val + Math.max(0, Math.max(leftSum, rightSum));
max_sum = Math.max(max_sum,
Math.max(branchMaxSum, leftSum + root.val + rightSum));
return branchMaxSum;
}
Given a binary tree, find the maximum path sum.
For this problem, a path is defined as any sequence of nodes from some starting node to any node in the tree along the parent-child connections. The path does not need to go through the root.
class Solution:
def __init__(self):
self.max_sum = float('-inf')
def max_path_sum(self, root):
def max_branch_sum(node):
if node is None:
return 0
left_sum = max_branch_sum(node.left)
right_sum = max_branch_sum(node.right)
# Maximum sum for the current branch including the node
branch_max_sum = node.val + max(0, max(left_sum, right_sum))
# Update the global maximum sum
self.max_sum = max(self.max_sum, branch_max_sum, left_sum + node.val + right_sum)
return branch_max_sum
max_branch_sum(root)
return self.max_sum
Given a binary tree, find the maximum path sum.
Use int[] to pass the maximum through the recursion
public static int maxPathSum(TreeNode root) {
if (root == null)
return 0;
int[] max = {Integer.MIN_VALUE};
maxBranchSum(root, max);
return max[0];
}
public static int maxBranchSum(TreeNode root, int[] max) {
if (root == null) {
return 0;
}
int leftSum = maxBranchSum(root.left, max);
int rightSum = maxBranchSum(root.right, max);
int branchMaxSum = root.val + Math.max(0, Math.max(leftSum, rightSum));
max[0] = Math.max(max[0], Math.max(branchMaxSum, leftSum + root.val + rightSum));
return branchMaxSum;
}
Given a binary tree, find the maximum path sum.
Use int[] to pass the maximum through the recursion
def max_path_sum(root):
if root is None:
return 0
max_sum = [float('-inf')]
max_branch_sum(root, max_sum)
return max_sum[0]
def max_branch_sum(node, max_sum):
if node is None:
return 0
left_sum = max_branch_sum(node.left, max_sum)
right_sum = max_branch_sum(node.right, max_sum)
branch_max_sum = node.val + max(0, max(left_sum, right_sum))
max_sum[0] = max(max_sum[0], max(branch_max_sum, left_sum + node.val + right_sum))
return branch_max_sum
Given a 2D board and a word, find if the word exists in the grid.
The word can be constructed from letters of sequentially adjacent cell, where "adjacent" cells are those horizontally or vertically neighboring. The same letter cell may not be used more than once.
For example, given a board,
[
['A','B','C','E'],
['S','F','C','S'],
['A','D','E','E']
]
word = "ABCCED", -> returns true,
word = "SEE", -> returns true,
word = "ABCB", -> returns false.
public boolean exist(char[][] board, String word) {
if (board.length == 0 || board[0].length == 0)
return false;
boolean[][] flag = new boolean[board.length][board[0].length];
for (int i = 0; i < board.length; i++) {
Arrays.fill(flag[i], false);
}
for (int i = 0; i < board.length; i++) {
for (int j = 0; j < board[0].length; j++) {
if (exist(board, i, j, word, 0, flag))
return true;
}
}
return false;
}
public boolean exist(char[][] board, int x, int y, String word, int index, boolean[][] flag) {
if (index == word.length())
return true;
if (x < 0 || y < 0 || x >= board.length || y >= board[0].length
|| flag[x][y] || board[x][y] != word.charAt(index))
return false;
int[] dx = {1, 0, -1, 0};
int[] dy = {0, 1, 0, -1};
for (int i = 0; i < 4; i++) {
flag[x][y] = true;
if (exist(board, x + dx[i], y + dy[i], word, index + 1, flag))
return true;
flag[x][y] = false;
}
return false;
}
class Solution:
def exist(self, board, word):
if len(board) == 0 or len(board[0]) == 0:
return False
flag = [[False] * len(board[0]) for _ in range(len(board))]
for i in range(len(board)):
for j in range(len(board[0])):
if self.dfs(board, i, j, word, 0, flag):
return True
return False
def dfs(self, board, x, y, word, index, flag):
if index == len(word):
return True
if x < 0 or y < 0 or x >= len(board) or y >= len(board[0]) or flag[x][y] or board[x][y] != word[index]:
return False
# Directions: right, down, left, up
dx = [1, 0, -1, 0]
dy = [0, 1, 0, -1]
flag[x][y] = True # Mark the cell as visited
for i in range(4):
if self.dfs(board, x + dx[i], y + dy[i], word, index + 1, flag):
return True
flag[x][y] = False # Unmark the cell (backtrack)
return False
Given a string s, partition s such that every substring of the partition is a palindrome.
Return all possible palindrome partitioning of s.
Example:
Input: "aab"
Output: [ ["aa", "b"], ["a", "a", "b"] ]
Given a string s, partition s such that every substring of the partition is a palindrome.
Return all possible palindrome partitioning of s.
public List<List<String>> partition(String s) {
List<List<String>> results = new ArrayList<>();
partition(results, s, 0, new ArrayList<String>());
return results;
}
public void partition(List<List<String>> results, String s, int start,
List<String> path) {
if (start == s.length()) {
results.add(new ArrayList<>(path));
return;
}
for (int i = start + 1; i <= s.length(); i++) {
String sub = s.substring(start, i);
if (isPalindrome(sub)) {
path.add(sub);
partition(results, s, i, path);
path.remove(path.size() - 1);
}
}
}
public boolean isPalindrome(String s) {
for (int i = 0, j = s.length() - 1; i < j; i++, j--) {
if (s.charAt(i) != s.charAt(j))
return false;
}
return true;
}
class Solution:
def partition(self, s):
results = []
self.partition_helper(results, s, 0, [])
return results
def partition_helper(self, results, s, start, path):
if start == len(s):
results.append(path[:])
return
for i in range(start + 1, len(s) + 1):
sub = s[start:i]
if self.is_palindrome(sub):
path.append(sub)
self.partition_helper(results, s, i, path)
path.pop()
def is_palindrome(self, s):
return s == s[::-1]
public List<List<String>> partition(String s) {
int n = s.length();
boolean[][] isPalindrome = new boolean[n][n];
// Precompute the palindrome table
for (int i = 0; i < n; i++) {
isPalindrome[i][i] = true; // every single character is a palindrome
}
for (int i = 0; i < n - 1; i++) {
isPalindrome[i][i + 1] = (s.charAt(i) == s.charAt(i + 1)); // check adjacent characters
}
for (int len = 2; len < n; len++) { // len is the length of the substring
for (int i = 0; i < n - len; i++) {
int j = i + len;
isPalindrome[i][j] = (s.charAt(i) == s.charAt(j)) && isPalindrome[i + 1][j - 1];
}
}
List<List<String>> results = new ArrayList<>();
partition(results, s, 0, new ArrayList<>(), isPalindrome);
return results;
}
private void partition(List<List<String>> results, String s, int start, List<String> path, boolean[][] isPalindrome) {
if (start == s.length()) {
results.add(new ArrayList<>(path));
return;
}
for (int i = start; i < s.length(); i++) {
if (isPalindrome[start][i]) {
path.add(s.substring(start, i + 1));
partition(results, s, i + 1, path, isPalindrome);
path.remove(path.size() - 1);
}
}
}
class Solution:
def partition(self, s):
n = len(s)
# Precompute the palindrome table
is_palindrome = [[False] * n for _ in range(n)]
# Every single character is a palindrome
for i in range(n):
is_palindrome[i][i] = True
# Check adjacent characters
for i in range(n - 1):
is_palindrome[i][i + 1] = (s[i] == s[i + 1])
# Check substrings of length > 2
for length in range(2, n):
for i in range(n - length):
j = i + length
is_palindrome[i][j] = (s[i] == s[j]) and is_palindrome[i + 1][j - 1]
results = []
self.partition_helper(results, s, 0, [], is_palindrome)
return results
def partition_helper(self, results, s, start, path, is_palindrome):
if start == len(s):
results.append(path[:]) # Make a copy of the path
return
for i in range(start, len(s)):
if is_palindrome[start][i]:
path.append(s[start:i + 1])
self.partition_helper(results, s, i + 1, path, is_palindrome)
path.pop()