Tree data structure and algorithm/LC relative to it
# Tree
1
/ \
2 3
/ \
4 5
# Array
# We can represent Tree in Array below :
[1,2,3,4,5]
# so, in tree, node with val 1 is idx 0, node with val is 2... and node with val 5 is idx 4
# we can transform between tree and array
https://www.bilibili.com/video/BV1Gd4y1V75u/?share_source=copy_web&vd_source=771d0eba9b524b4f63f92e37bde71301
https://www.bilibili.com/video/BV1Ug411S7my/?share_source=copy_web
height
子節點 到 bottom 距離
)
(node -> bottom
)depth
root 到 子節點 距離
)
(root -> node
)tree
-> think about recursionwhile loop
approaches, but dependspreorder, inorder, and postorder
depending on the relative
order among the root node, left node and right node.preorder, inorder, and postorder
modepreorder
mode# pre-order traversal
= []
r def pre_order_traverse(TreeNode):
r.append(root.value)if root.left:
pre_order_traverse(root.left)if root.right:
pre_order_traverse(root.right)
class Solution(object): # left -> root -> right def inorderTraversal(self, root): # help func def dfs(root, res): if not root: return if root.left: dfs(root.left) res.append(root.val) if root.right: dfs(root.right) res = [] dfs(root) return root
class Solution: def inorderTraversal(self, root): stack = [] res = []
while True:
# NOTE !!! : we GO THROUGH left sub tree to the end first, and form our stack on the same time, then do in-order transversal
"""
Why the Left Traversal is Necessary ?
To process nodes in in-order (left -> root -> right), you need to traverse the left subtree of every node before processing the node itself. This is a characteristic of in-order traversal—without traversing left first, you can't get the correct order.
In summary, the `hile root: stack.append(root); root = root.left part is critical for simulating the recursive left-first behavior in an iterative manner. There’s no way to completely skip this step if you want an in-order traversal (using an iterative approach), but alternatives like Morris traversal exist to avoid using a stack and still traverse efficiently.
"""
while root:
stack.append(root)
root = root.left
if len(stack) == 0:
# NOTE here
break
root = stack.pop()
res.append(root.val)
root = root.right
return res
- post-order traverse
- left -> right -> root
- use in "need sub-tree val in root calculation"
- divide and conquer algorithm
- DP (dynamic programming)
```python
# postorder traversal
r = []
def post_order_traverse(TreeNode):
if root.left:
post_order_traverse(root.left)
if root.right:
post_order_traverse(root.right)
r.append(root.value)
# python
# Init a tree in py
class TreeNode(object):
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
= TreeNode(0)
root = TreeNode(1)
root.left = TreeNode(2)
root.right print (root)
# python
def traverse(TreeNode):
for child in root.children:
# op in pre-traverse
traverse(child)# op in post-traverse
// java
// layer traverse (BST)
// algorithm book (labu) p. 262
void traverse(TreeNode root){
if (root == null) return;
// init queue, add root into queue
Queue<TreeNode> q = new LinkedList<>();
.offer(root);
q
while (! q.isEmpty()){
TreeNode cur = q.poll();
/********* layer traverse *********/
System.out.println(root.val);
/**********************************/
if (cur.left != null){
.offer(cur.left);
q}
if (cur.right != null){
.offer(cur.right);
q}
}
}
# init a tree with root value
class TreeNode(object):
def __init__(self, x):
self.val = x
self.left = None
self.right = None
= 3
s = TreeNode(int(s)) root
Right Side
View// java
// LC 199
List<Integer> res = new ArrayList<>();
Queue<TreeNode> q = new LinkedList<>();
while (!q.isEmpty()) {
TreeNode rightSide = null;
int qLen = q.size();
/**
* NOTE !!!
*
* 1) via for loop, we can get `most right node` (since the order is root -> left -> right)
* 2) via `TreeNode rightSide = null;`, we can get the `most right node` object
* - rightSide could be `right sub tree` or `left sub tree`
*
* e.g.
* 1
* 2 3
*
*
* 1
* 2 3
* 4
*
*/
for (int i = 0; i < qLen; i++) {
TreeNode node = q.poll();
if (node != null) {
= node;
rightSide .offer(node.left);
q.offer(node.right);
q}
}
if (rightSide != null) {
.add(rightSide.val);
res}
}
// get nodes count of binary tree
// get nodes count of perfect tree
// get nodes count of complete tree
// LC 222
// dfs
class Solution {
public int countNodes(TreeNode root) {
if (root == null) {
return 0;
}
// Recursively count the nodes in the left subtree
int leftCount = countNodes(root.left);
// Recursively count the nodes in the right subtree
int rightCount = countNodes(root.right);
// Return the total count (current node + left subtree + right subtree)
return 1 + leftCount + rightCount;
}
}
// bfs
public int countNodes_2(TreeNode root) {
if (root == null){
return 0;
}
List<TreeNode> collected = new ArrayList<>();
Queue<TreeNode> q = new LinkedList<>();
.add(root);
qwhile (!q.isEmpty()){
TreeNode cur = q.poll();
.add(cur);
collectedif (cur.left != null) {
.add(cur.left);
q}
if (cur.right != null) {
.add(cur.right);
q}
}
//return this.count;
System.out.println("collected = " + collected.toString());
return collected.size();
}
// java
// V0
// IDEA : RECURSIVE (DFS)
public int maxDepth(TreeNode root) {
if (root == null){
return 0;
}
// NOTE : below conditon is optional (have or not use is OK)
// if (root.left == null && root.right == null){
// return 1;
// }
int leftD = maxDepth(root.left) + 1;
int rightD = maxDepth(root.right) + 1;
return Math.max(leftD, rightD);
}
// V1
public int getDepthDFS(TreeNode root) {
if (root == null) {
return 0;
}
return Math.max(getDepthDFS(root.left), getDepthDFS(root.right)) + 1;
}
#-----------------
# BFS
#-----------------
# ....
= 1
layer = [[layer, root]]
q = []
res while q:
# NOTE !!! FIFO, so we pop first added element (new element added at right hand side)
= root.pop(0)
layer, tmp """
KEY here !!!!
"""
if tmp and not tmp.left and not tmp.right:
res.append(layer)if tmp.left:
+1, tmp.left])
q.append([layerif tmp.right:
+1, tmp.right])
q.append([layer# ...
// java
// V0'
// IDEA : DFS
public int minDepth(TreeNode root) {
if (root == null){
return 0;
}
return getDepth(root);
}
private int getDepth(TreeNode root){
if (root == null){
return 0;
}
/**
* NOTE !!! below condition
* -> we need to go till meat a node, then calculate min depths (number of node)
* -> Note: A leaf is a node with no children.
* -> plz check below example for idea
* example : [2,null,3,null,4,null,5,null,6]
*
*
*/
if (root.left == null) {
return 1 + getDepth(root.right);
} else if (root.right == null) {
return 1 + getDepth(root.left);
}
return 1 + Math.min(getDepth(root.left), getDepth(root.right));
}
int maxPathSum = 0;
// ...
public int getMaxPathHelper(TreeNode node){
if(node == null){
return 0;
}
int leftMaxDepth = Math.max(getMaxPathHelper(root.left), 0);
int rightMaxDepth = Math.max(getMaxPathHelper(root.right), 0);
= Math.max(maxPathSum, root.val + leftMaxDepth + rightMaxDepth);
maxPathSum
return root.val + Math.max(leftMaxDepth, rightMaxDepth);
}
//...
# LC 236 Lowest Common Ancestor of a Binary Tree
# LC 235 Lowest Common Ancestor of a Binary Search Tree
# LC 1650 Lowest Common Ancestor of a Binary Tree III
# V0
# IDEA : RECURSION + POST ORDER TRANSVERSAL
### NOTE : we need POST ORDER TRANSVERSAL for this problem
# -> left -> right -> root
# -> we can make sure that if p == q, then the root must be p and q's "common ancestor"
class Solution(object):
def lowestCommonAncestor(self, root, p, q):
### NOTE here
# if not root or find p in tree or find q in tree
# -> then we quit the recursion and return root
if not root or p == root or q == root:
return root
### NOTE here
# -> not root.left, root.right, BUT left, right
= self.lowestCommonAncestor(root.left, p, q)
left = self.lowestCommonAncestor(root.right, p, q)
right ### NOTE here
# find q and p on the same time -> LCA is the current node (root)
# if left and right -> p, q MUST in left, right sub tree respectively
if left and right:
return root
### NOTE here
# if p, q both in left sub tree or both in right sub tree
return left if left else right
// java
// algorithm book p. 271
TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q){
// base case
if (root == null) return null;
if (root == p || root == q) return root;
TreeNode left = lowestCommonAncestor(root.left, p, q);
TreeNode right = lowestCommonAncestor(root.right, p, q);
// case 1
if (left != null && right != null){
return root;
}
// case 2
if (left == null && right == null){
return null;
}
// case 3
return left == null ? right: left;
}
# LC 617 Merge Two Binary Trees
# NOTE !!! there is also BFS solution
# V0
# IDEA : DFS + BACKTRACK
class Solution:
def mergeTrees(self, t1, t2):
return self.dfs(t1,t2)
def dfs(self, t1, t2):
if not t1 and not t2:
return
if t1 and t2:
### NOTE here
= TreeNode(t1.val + t2.val)
newT = self.mergeTrees(t1.right, t2.right)
newT.right = self.mergeTrees(t1.left, t2.left)
newT.left return newT
### NOTE here
else:
return t1 or t2
// java
// V0
// IDEA : RECURSIVE
public TreeNode mergeTrees(TreeNode t1, TreeNode t2) {
if (t1 == null && t2 == null){
return null;
}
if (t1 != null && t2 != null){
.val += t2.val;
t1}
if (t1 == null && t2 != null){
// NOTE!!! return t2 directly here
return t2;
}
if (t1 != null && t2 == null){
// NOTE!!! return t1 directly here
return t1;
}
.left = mergeTrees(t1.left, t2.left);
t1.right = mergeTrees(t1.right, t2.right);
t1
return t1;
}
basic
binary tree// java
// algorithm book (labu) p. 250
public int countNodes (TreeNode root){
if (root == null) return 0;
return 1 + countNodes(root.left) + countNodes(root.right);
}
perfect
binary tree// java
// algorithm book (labu) p. 250
public int countNodes(TreeNode root){
int h = 0;
// get tree depth
while (root != null){
= root.left;
root += 1;
h }
// total nodes = 2**n + 1
return (int)Math.pow(2, h) - 1;
}
complete
binary tree// java
// algorithm book (labu) p. 251
public int countNodes(TreeNode root){
TreeNode l = root;
TreeNode r = root;
int hl = 0;
int hr = 0;
while (l != null){
= l = left;
l += 1;
h1 }
while (r != null){
= r.right;
r += 1;
hr }
// if left, right sub tree have SAME depth -> this is a perfect binary tree
if (hl == hr){
return (int)Math.pow(2, hl) - 1;
}
// if left, right sub tree have DIFFERENT depth, then we follow the simple bianry tree approach
return 1 + countNodes(root.left) + countNodes(root.right);
}
// java
// algorithm book (labu) p.256
String SEP = ",";
String NULL = "#";
/* main func : serialize binary tree to string */
String serialize(TreeNode root){
StringBuilder sb = new StringBuilder();
serialize(root, sb);
return sb.toString();
/* help func : put binary tree to StringBuilder */
void serialize(TreeNode root, StringBuilder sb){
if (root == null){
.append(NULL).append(SEP);
sbreturn;
}
}
/********* pre-order traverse *********/
.append(root.val).append(SEP);
sb/**************************************/
serialize(root.left, sb);
serialize(root.right, sb);
}
// java
// algorithm book (labu) p.256
String SEP = ",";
String NULL = "#";
/* main func : dserialize string to binary tree */
TreeNode deserlialize(String data){
// transform string to linkedlist
LinkedList<String> nodes = new LinkedList<>();
for (String s: data.split(SEP)){
.addLast(s);
nodes}
return deserlialize(nodes);
}
/* **** help func : build binary tree via linkedlist (nodes) */
TreeNode deserlialize(LinkedList<String> nodes){
if (nodes.isEmpty()) return null;
/********* pre-order traverse *********/
// the 1st element on left is the root node of the binary tree
String first = nodes.removeFirst();
if (first.equals(NULL)) return null;
TreeNode root = new TreeNode(Integer.parseInt(first));
/**************************************/
.left = deserlialize(nodes);
root.right = deserlialize(nodes);
root
return root;
}
// java
// algorithm book (labu) p.258
String SEP = ",";
String NULL = "#";
StringBuilder sb = new StringBuilder();
/* help func : pit binary tree to StringBuilder*/
void serialize(TreeNode root, StringBuilder sb){
if (root == null){
.append(NULL).append(SEP);
sbreturn;
}
serialize(root.left, sb);
serialize(root.right, sb);
/********* post-order traverse *********/
.append(root.val).append(SEP);
sb/**************************************/
}
// java
// algorithm book (labu) p.260
/* main func : deserialize string to binary tree */
TreeNode deserlialize(String data){
LinkedList<String> nodes = new LinkedList<>();
for (String s : data.split(SEP)){
.addLast(s);
nodes}
return deserlialize(nodes);
}
/* help func : build binary tree via linkedlist */
TreeNode deserlialize(LinkedList<String> nodes){
if (nodes.isEmpty()) return null;
// get element from last to beginning
String last = nodes.removeLast();
if (last.equals(NULL)) return null;
TreeNode root = new TreeNode(Integer.parseInt(last));
// build right sub tree first, then left sub tree
.right = deserlialize(nodes);
root.left = deserlialize(nodes);
root
return root;
}
// java
// layer traverse : https://github.com/yennanliu/CS_basics/blob/master/doc/cheatsheet/tree.md#1-1-basic-op
// algorithm book (labu) p.263
String SEP = ",";
String NULL = "#";
/* Serialize binary tree to string */
String serialize(TreeNode root){
if (root == null) return "";
StringBuilder sb = new StringBuilder();
// init queue, put root into it
Queue<TreeNode> q = new LinkedList<>();
.offer(root);
q
while (!q.isEmpty()){
TreeNode cur = q.poll();
/***** layer traverse ******/
if (cur == null){
.append(NULL).append(SEP);
sbcontinue;
}
.append(cur.val).append(SEP);
sb/**************************/
.offer(cur.left);
q.offer(cur.right);
q}
return sb.toString();
}
// java
// algorithm book (labu) p.264
String SEP = ",";
String NULL = "#";
/* Deserialize binary tree to string */
TreeNode deserlialize(String data){
if (data.isEmpty()) return null;
String[] nodes = data.split(SEP);
// root's value = 1st element's value
TreeNode root = new TreeNode(Integer.parseInt(node[0]));
// queue records parent node, put root into queue
Queue<TreeNode> q = new LinkedList<>();
.offer(root);
q
for (int i = 1; i < nodes.length){
// queue saves parent nodes
TreeNode parent = q.poll();
// parent node's left sub node
String left = nodes[i++];
if (!left.equals(NULL)){
.left = new TreeNode(Integer.parseInt(left));
parent.offer(parent.left);
q}else {
.left = null;
parent}
// parent node's right sub node
String right = nodes[i++];
if (!right.equals(NULL)){
.right = new TreeNode(Integer.parseInt(right));
parent.offer(parent.right);
q}else{
.right = null;
parent}
}
return root;
}
// java
// LC 297
public class Codec{
public String serialize(TreeNode root) {
/** NOTE !!!
*
* if root == null, return "#"
*/
if (root == null){
return "#";
}
/** NOTE !!! return result via pre-order, split with "," */
return root.val + "," + serialize(root.left) + "," + serialize(root.right);
}
public TreeNode deserialize(String data) {
/** NOTE !!!
*
* 1) init queue and append serialize output
* 2) even use queue, but helper func still using DFS
*/
Queue<String> queue = new LinkedList<>(Arrays.asList(data.split(",")));
return helper(queue);
}
private TreeNode helper(Queue<String> queue) {
// get val from queue first
String s = queue.poll();
if (s.equals("#")){
return null;
}
/** NOTE !!! init current node */
TreeNode root = new TreeNode(Integer.valueOf(s));
/** NOTE !!!
*
* since serialize is "pre-order",
* deserialize we use "pre-order" as well
* e.g. root -> left sub tree -> right sub tree
* -> so we get sub tree via below :
*
* root.left = helper(queue);
* root.right = helper(queue);
*
*/
.left = helper(queue);
root.right = helper(queue);
root/** NOTE !!! don't forget to return final deserialize result */
return root;
}
}
# LC 226 Invert Binary Tree
# V0
# IDEA : DFS
# -> below code shows a good example that tree is a type of "linked list"
# -> we don't really modify tree's "value", but we modify the pointer
# -> e.g. make root.left point to root.right, make root.right point to root.left
class Solution(object):
def invertTree(self, root):
def dfs(root):
if not root:
return root
### NOTE THIS
if root:
# NOTE : have to do root.left, root.right ON THE SAME TIME
= dfs(root.right), dfs(root.left)
root.left, root.right
dfs(root)return root
# V0'
# IDEA BFS
class Solution(object):
def invertTree(self, root):
if root == None:
return root
= [root]
queue while queue:
# queue = queue[::-1] <-- this one is NOT working
for i in range(len(queue)):
= queue.pop()
tmp ### NOTE here !!!!!!
# -> we do invert op via below
= tmp.right, tmp.left
tmp.left, tmp.right if tmp.left:
queue.append(tmp.left)if tmp.right:
queue.append(tmp.right)return root
// java
// DFS
// V0
// IDEA : DFS
public TreeNode invertTree(TreeNode root) {
if (root == null) {
return null;
}
/** NOTE !!!!
*
* instead of calling invertTree and assign value to sub tree directly,
* we need to CACHE invertTree result, and assign later
* -> since assign directly will cause tree changed, and affect the other invertTree call
*
* e.g. below is WRONG,
* root.left = invertTree(root.right);
* root.right = invertTree(root.left);
*
* need to cache result
*
* TreeNode left = invertTree(root.left);
* TreeNode right = invertTree(root.right);
*
* then assign to sub tree
*
* root.left = right;
* root.right = left;
*/
TreeNode left = invertTree(root.left);
TreeNode right = invertTree(root.right);
.left = right;
root.right = left;
root/** NOTE !!!! below is WRONG */
// root.left = invertTree(root.right);
// root.right = invertTree(root.left);
return root;
}
# LC 863. All Nodes Distance K in Binary Tree
# dfs
# IDEA : DFS
# ...
from collections import defaultdict
def build(parent,child):
= defaultdict(list)
graph if parent and child:
graph[parent.val].append(child.val)
graph[child.val].append(parent.val)if child.left:
build(child,child.left)if child.right:
build(child,child.right)# ....
# LC 101
class Solution(object):
def isSymmetric(self, root):
if not root:
return True
return self.mirror(root.left, root.right)
def mirror(self, left, right):
if not left or not right:
return left == right
if left.val != right.val:
return False
return self.mirror(left.left, right.right) and self.mirror(left.right, right.left)
# LC 199 Binary Tree Right Side View
# V0
# IDEA : DFS
class Solution(object):
def rightSideView(self, root):
def dfs(root, layer):
if not root:
return
if len(res) <= layer+1:
#if len(res) == layer: # this works as well
res.append([])
res[layer].append(root.val)if root.right:
+1)
dfs(root.right, layerif root.left:
+1)
dfs(root.left, layer
if not root:
return []
=[[]]
res 0)
dfs(root, return [x[0] for x in res if len(x) > 0]
# LC 606. Construct String from Binary Tree
# V0
class Solution:
def tree2str(self, t):
if not t:
return ''
if not t.left and not t.right:
return str(t.val)
### NOTICE HERE
if not t.left:
return str(t.val) + '()' + '(' + self.tree2str(t.right) + ')'
### NOTICE HERE
if not t.right:
return str(t.val) + '(' + self.tree2str(t.left) + ')'
### NOTICE HERE
return str(t.val) + '(' + self.tree2str(t.left) + ')' + '(' + self.tree2str(t.right) + ')'
# V0'
class Solution:
def tree2str(self, root):
if not root:
return ""
### NOTE : this condition can make effect
# as ` return str(root.val) + "(" + self.tree2str(root.left) + ")" + "(" + self.tree2str(root.right) + ")" ` at the bottom
if not root.left and not root.right:
return str(root.val)
if root.left and root.right:
return str(root.val) + "(" + self.tree2str(root.left) + ")" + "(" + self.tree2str(root.right) + ")"
if not root.right:
return str(root.val) + "(" + self.tree2str(root.left) + ")"
if not root.left:
return str(root.val) + "()" + "(" + self.tree2str(root.right) + ")"
#return str(root.val) + "(" + self.tree2str(root.left) + ")" + "(" + self.tree2str(root.right) + ")"
# V0''
class Solution(object):
def tree2str(self, t):
if not t: return ""
= str(t.val)
s if t.left or t.right:
+= "(" + self.tree2str(t.left) + ")"
s if t.right:
+= "(" + self.tree2str(t.right) + ")"
s return s
# LC 662 Maximum Width of Binary Tree
# V0
# IDEA : defaultdict + DFS
from collections import defaultdict
class Solution:
def widthOfBinaryTree(self, root):
def dfs(node, level, idx):
if node:
+= [idx]
d[level] +1, 2*idx)
dfs(node.left, level+1, 2*idx+1)
dfs(node.right, level
= defaultdict(list)
d 0, 0)
dfs(root, return max(v[-1] - v[0] + 1 for _, v in d.items())
# V0'
# IDEA : BFS
# IDEA : GIVEN index = idx -> its left tree index = idx*2 ; its right tree index = idx*2 + 1
# -> SO GO THROUGH ALL LAYERS IN THE TREE, CALCULATE THEIR WIDTH, AND RETRUN THE MAX WIDTH WHICH IS THE NEEDED RESPONSE
from collections import defaultdict
class Solution(object):
def widthOfBinaryTree(self, root):
# edge case
if not root:
return 0
= 0
layer = 0
idx = [[root, layer, idx]]
q = defaultdict(list)
res while q:
for i in range(len(q)):
= q.pop(0)
tmp, layer, idx
res[layer].append(idx)if tmp.left:
+1, idx*2])
q.append([tmp.left, layerif tmp.right:
+1, idx*2+1])
q.append([tmp.right, layer#print ("res = " + str(res))
= [max(res[x]) - min(res[x]) + 1 for x in list(res.keys()) if res[x] > 1]
_res #print ("_res = " + str(_res))
return max(_res)
# LC 606 Construct String from Binary Tree
# V0
# IDEA : tree + check problem examples
# -> if root.right and not root.left
# -> if root.left and not root.right
class Solution(object):
def tree2str(self, root):
def dfs(root):
if not root:
### NOTICE HERE
return ""
### NOTICE HERE
if not root.left and not root.right:
return str(root.val)
### NOTICE HERE : "2()(4)" case
if root.right and not root.left:
return str(root.val) + '()' + '(' + dfs(root.right) + ')'
### NOTICE HERE
if root.left and not root.right:
return str(root.val) + '(' + dfs(root.left) + ')'
### NOTICE HERE
return str(root.val) + '(' + dfs(root.left) + ')' + '(' + dfs(root.right) + ')'
= dfs(root)
res return res
# LeetCode 742. Closest Leaf in a Binary Tree
# V0
# IDEA : DFS build GRAPH + BFS find ans
### NOTE : closest to a leaf means the least number of edges travelled on the binary tree to reach any leaf of the tree. Also, a node is called a leaf if it has no children.
# -> We only consider the min distance between left (no sub tree) and k
### NOTE : we need DFS create the graph
# https://www.youtube.com/watch?v=x1wXkRrpavw
# https://blog.csdn.net/qq_17550379/article/details/87778889
import collections
class Solution:
# build graph via DFS
# node : current node
# parent : parent of current node
def buildGraph(self, node, parent, k):
if not node:
return
# if node.val == k, THEN GET THE start point FROM current "node",
# then build graph based on above
if node.val == k:
self.start = node
if parent:
self.graph[node].append(parent)
self.graph[parent].append(node)
self.buildGraph(node.left, node, k)
self.buildGraph(node.right, node, k)
# search via DFS
def findClosestLeaf(self, root, k):
self.start = None
### NOTE : we need DFS create the graph
self.buildGraph(root, None, k)
= [root], set()
q, visited #q, visited = [self.start], set() # need to validate this
self.graph = collections.defaultdict(list)
while q:
for i in range(len(q)):
= q.pop(0)
cur # add cur to visited, NOT to visit this node again
visited.add(cur)### NOTICE HERE
# if not cur.left and not cur.right: means this is the leaf (HAS NO ANY left/right node) of the tree
# so the first value of this is what we want, just return cur.val as answer directly
if not cur.left and not cur.right:
# return the answer
return cur.val
# if not find the leaf, then go through all neighbors of current node, and search again
for node in self.graph:
if node not in visited: # need to check if "if node not in visited" or "if node in visited"
q.append(node)
# LC 100 Same tree
# V0
# IDEA : Recursion
class Solution(object):
def isSameTree(self, p, q):
def dfs(p, q):
### NOTE : we need to put this as 1st condition, or will cause "not sub tree" error
if not p and not q:
return True
### NOTE : elif (but not `if`)
elif (not p and q) or (p and not q):
return False
### NOTE : elif (but not `if`)
elif p.val != q.val:
return False
return dfs(p.left, q.left) and dfs(p.right, q.right)
= dfs(p, q)
res return res
# 98. Validate Binary Search Tree
# V0
# IDEA : BFS
# -> trick : we make sure current tree and all of sub tree are valid BST
# -> not only compare tmp.val with tmp.left.val, tmp.right.val,
# -> but we need compare if tmp.left.val is SMALLER then `previous node val`
# -> but we need compare if tmp.right.val is BIGGER then `previous node val`
class Solution(object):
def isValidBST(self, root):
if not root:
return True
= -float('inf')
_min = float('inf')
_max ### NOTE : we set q like below
= [[root, _min, _max]]
q while q:
for i in range(len(q)):
= q.pop(0)
tmp, _min, _max if tmp.left:
### NOTE : below condition
if tmp.left.val >= tmp.val or tmp.left.val <= _min:
return False
### NOTE : we append tmp.val as _max
q.append([tmp.left, _min, tmp.val])if tmp.right:
### NOTE : below condition
if tmp.right.val <= tmp.val or tmp.right.val >= _max:
return False
### NOTE : we append tmp.val as _min
q.append([tmp.right, tmp.val, _max])return True
# V0'
# IDEA: RECURSION
class Solution(object):
def isValidBST(self, root):
"""
:type root: TreeNode
:rtype: bool
"""
return self.valid(root, float('-inf'), float('inf'))
def valid(self, root, min_, max_):
if not root: return True
if root.val >= max_ or root.val <= min_:
return False
return self.valid(root.left, min_, root.val) and self.valid(root.right, root.val, max_)
# Construct Binary Tree from Preorder and Inorder Traversal
# V0
# IDEA : BST property
class Solution(object):
def buildTree(self, preorder, inorder):
if len(preorder) == 0:
return None
if len(preorder) == 1:
return TreeNode(preorder[0])
### NOTE : init root like below (via TreeNode and root value (preorder[0]))
= TreeNode(preorder[0])
root # get the index of root.val in order to SPLIT TREE
= inorder.index(root.val) # the index of root at inorder, and we can also get the length of left-sub-tree, right-sub-tree ( preorder[1:index+1]) for following using
index # recursion for root.left
#### NOTE : preorder[1 : index + 1] (for left sub tree)
= self.buildTree(preorder[1 : index + 1], inorder[ : index]) ### since the BST is symmery so the length of left-sub-tree is same in both Preorder and Inorder, so we can use the index to get the left-sub-tree of Preorder as well
root.left # recursion for root.right
= self.buildTree(preorder[index + 1 : ], inorder[index + 1 :]) ### since the BST is symmery so the length of left-sub-tree is same in both Preorder and Inorder, so we can use the index to get the right-sub-tree of Preorder as well
root.right return root
# LC 536 Construct Binary Tree from String
# V0
# IDEA : tree property + recursive
class Solution(object):
def str2tree(self, s):
if not s:
return None
= ''
n while s and s[0] not in ('(', ')'):
+= s[0]
n = s[1:]
s ### NOTE this
= TreeNode(int(n))
node ### NOTE this
= self.divide(s)
left, right = self.str2tree(left[1:-1])
node.left = self.str2tree(right[1:-1])
node.right return node
def divide(self, s):
= '', 0
part, deg while s:
"""
syntax exmaple :
In [9]: x = {'(' : 1, ')' : -1}
In [10]: x.get('(')
Out[10]: 1
In [11]: x.get('(', 0 )
Out[11]: 1
In [12]: x.get('&', 0 )
Out[12]: 0
"""
+= {'(' : 1, ')' : -1}.get(s[0], 0)
deg += s[0]
part = s[1:]
s if deg == 0:
break
return part, s
# V0'
# IDEA : tree property + recursive
class TreeNode(object):
def __init__(self, x):
self.val = x
self.left = None
self.right = None
class Solution(object):
def str2tree(self, s):
"""
:type s: str
:rtype: TreeNode
"""
def str2treeHelper(s, i):
= i
start if s[i] == '-': i += 1
while i < len(s) and s[i].isdigit():
+= 1
i = TreeNode(int(s[start:i]))
node if i < len(s) and s[i] == '(':
+= 1
i = str2treeHelper(s, i)
node.left, i += 1
i if i < len(s) and s[i] == '(':
+= 1
i = str2treeHelper(s, i)
node.right, i += 1
i return node, i
return str2treeHelper(s, 0)[0] if s else None
# LC 111 Minimum Depth of Binary Tree
# V0
# IDEA : BFS
class Solution(object):
def minDepth(self, root):
# edge case
if not root:
return 0
if root and not root.left and not root.right:
return 1
= 1
layer = [[layer, root]]
q = []
res while q:
for i in range(len(q)):
= q.pop(0)
layer, tmp """
NOTE !!! : via below condition, we get "layer" of " A leaf is a node with no children."
"""
if tmp and not tmp.left and not tmp.right:
res.append(layer)if tmp.left:
+1, tmp.left])
q.append([layerif tmp.right:
+1, tmp.right])
q.append([layer# get min
#print ("res = " + str(res))
return min(res)
# V0'
# IDEA : DFS
# compare with LC 104 : Maximum Depth of Binary Tree
class Solution(object):
def minDepth(self, root):
if not root:
return 0
### NOTE here : we need min depth, so if not root.left, then we need to return directly
if not root.left:
return 1 + self.minDepth(root.right)
### NOTE here : we need min depth, so if not root.right, then we need to return directly
elif not root.right:
return 1 + self.minDepth(root.left)
else:
return 1 + min(self.minDepth(root.left), self.minDepth(root.right))
# LC 104 Maximum Depth of Binary Tree
# V0
# IDEA : DFS
class Solution(object):
def maxDepth(self, root):
if root == None:
return 0
return 1 + max(self.maxDepth(root.left), self.maxDepth(root.right))
# V0'
# bfs
class Solution(object):
def maxDepth(self, root):
# edge case
if not root:
return 0
= 0
res = 0
layer = [[root, layer]]
q while q:
for i in range(len(q)):
= q.pop(0)
tmp, layer = max(res, layer)
res if tmp.left:
+1])
q.append([tmp.left, layerif tmp.right:
+1])
q.append([tmp.right, layerreturn res + 1
# LC 863. All Nodes Distance K in Binary Tree
# V0
# IDEA : DFS + BFS
from collections import defaultdict
class Solution:
def build(self,parent,child):
if parent and child:
self.graph[parent.val].append(child.val)
self.graph[child.val].append(parent.val)
if child.left:
self.build(child,child.left)
if child.right:
self.build(child,child.right)
def distanceK(self, root, target, K):
self.graph=defaultdict(list)
self.build(None,root)
=[(target.val,1)]
q=set([target.val])
vis=[]
answhile q:
=q.pop(0)
i,jfor node in self.graph[i]:
if node not in vis:
if j==K:
ans.append(node)
vis.add(node)+1))
q.append((node,jreturn ans if len(q) < K else [target.val]
# LC 545. Boundary of Binary Tree
# V0
# IDEA : DFS
# https://xiaoguan.gitbooks.io/leetcode/content/LeetCode/545-boundary-of-binary-tree-medium.html
# https://www.cnblogs.com/lightwindy/p/9583723.html
class Solution(object):
def boundaryOfBinaryTree(self, root):
def leftBoundary(root, nodes):
if not root or (not root.left and not root.right):
return
nodes.append(root.val)"""
NOTE this !!!
"""
if not root.left:
leftBoundary(root.right, nodes)else:
leftBoundary(root.left, nodes)
def rightBoundary(root, nodes):
if not root or (not root.left and not root.right):
return
"""
NOTE this !!!
"""
if not root.right:
rightBoundary(root.left, nodes)else:
rightBoundary(root.right, nodes)
nodes.append(root.val)
def leaves(root, nodes):
if not root:
return
if not root.left and not root.right:
nodes.append(root.val)return
leaves(root.left, nodes)
leaves(root.right, nodes)
if not root:
return []
= [root.val]
nodes
leftBoundary(root.left, nodes)"""
NOTE this !!!
"""
leaves(root.left, nodes)
leaves(root.right, nodes)
rightBoundary(root.right, nodes)return nodes
# V0'
class Solution(object):
def boundaryOfBinaryTree(self, root):
if not root: return []
= [root]
left_bd_nodes = root.left
cur while cur:
left_bd_nodes.append(cur)= cur.left or cur.right
cur
= [root]
right_bd_nodes = root.right
cur while cur:
right_bd_nodes.append(cur)= cur.right or cur.left
cur
= []
leaf_nodes = [root]
stack while stack:
= stack.pop()
node if node.right:
stack.append(node.right)if node.left:
stack.append(node.left)if not node.left and not node.right:
leaf_nodes.append(node)
= []
ans = set()
seen def visit(node):
if node not in seen:
seen.add(node)
ans.append(node.val)
for node in left_bd_nodes: visit(node)
for node in leaf_nodes: visit(node)
for node in reversed(right_bd_nodes): visit(node)
return ans
// java
// LC 124
// V0-1
// IDEA: DFS (GPT)
private int maxSum = Integer.MIN_VALUE;
public int maxPathSum_0_1(TreeNode root) {
if (root == null) {
return Integer.MIN_VALUE; // Handle null case
}
dfs(root);
return maxSum;
}
/** NOTE !!!
*
* the response type of dfs is `integer`
* e.g. the `max path sum` per input node
*/
private int dfs(TreeNode node) {
if (node == null) {
return 0;
}
// Compute max path sum of left and right children, discard negative values
/**
* NOTE !!!
*
* we cache `leftMax` on current node
* we cache `rightMax` on current node
*
* so we can update global `max path sum` below
*/
int leftMax = Math.max(dfs(node.left), 0);
int rightMax = Math.max(dfs(node.right), 0);
// Update global max sum with current node as the highest ancestor
/**
* NOTE !!!
*
* we update global `max path sum`,
* but the `maxSum` is NOT return as method reponse,
* we simply update the global variable `maxSum`
*
* -> the method return val is local max path (node.val + Math.max(leftMax, rightMax))
*/
= Math.max(maxSum, node.val + leftMax + rightMax);
maxSum
// Return max sum path including this node (but only one subtree path)
/**
* NOTE !!!
*
*
* -> the method return val is local max path (node.val + Math.max(leftMax, rightMax)),
* instead of `maxSum`
*
*/
return node.val + Math.max(leftMax, rightMax);
}
# 124. Binary Tree Maximum Path Sum
# V0
# IDEA : DFS
# https://leetcode.com/problems/binary-tree-maximum-path-sum/discuss/209995/Python-solution
class Solution(object):
def maxPathSum(self, root):
def dfs(root):
if not root:
return 0
= dfs(root.left)
l_max = dfs(root.right)
r_max """
handle if l_max < 0:
-> start again from root.val
else:
-> l_max += root.val
"""
if l_max < 0:
= root.val
l_max else:
+= root.val
l_max """
handle if r_max < 0:
-> start again from root.val
else:
-> r_max += root.val
"""
if r_max < 0:
= root.val
r_max else:
+= root.val
r_max
self.maximum = max(self.maximum, l_max + r_max - root.val)
return max(l_max, r_max)
self.maximum = -float('inf')
dfs(root)return self.maximum
# LC 1597 Build Binary Expression Tree From Infix Expression
# V0
# IDEA : LC 224 Basic Calculator
class Solution(object):
def help(self, numSt, opSt):
= numSt.pop()
right = numSt.pop()
left # Node(val=op, left=lhs, right=rhs)
return Node(opSt.pop(), left, right)
def expTree(self, s):
# hashmap for operator ordering
= {'*': 1, '/': 1, '+': 2, '-': 2, ')': 3, '(': 4}
pr = []
numSt = []
opSt = 0
i while i < len(s):
= s[i]
c += 1
i # check if int(c) if string
if c.isnumeric():
numSt.append(Node(c))else:
if c == '(':
'(')
opSt.append(else:
while(len(opSt) > 0 and pr[c] >= pr[opSt[-1]]):
self.help(numSt, opSt))
numSt.append(if c == ')':
# Now what remains is the closing bracket ')'
opSt.pop() else:
opSt.append(c)while len(opSt) > 0:
self.help(numSt, opSt))
numSt.append(print (">>> numSt = {}, opSt = {}".format(str(numSt), opSt))
return numSt.pop()
# V0'
# IDEA : RECURSIVE
class Solution:
def expTree(self, s):
= len(s)
n if n == 1:
return Node(s)
= None
fstOpIdx = 0
kets for i in range(n-1, 0, -1):
if s[i] == ")":
+= 1
kets elif s[i] == "(":
-= 1
kets elif kets == 0:
if s[i] in "+-":
= i
fstOpIdx break
elif s[i] in "*/" and fstOpIdx is None:
= i
fstOpIdx if fstOpIdx is None:
return self.expTree(s[1:-1])
= Node(s[fstOpIdx])
rtNd = self.expTree(s[:fstOpIdx])
rtNd.left = self.expTree(s[fstOpIdx+1:])
rtNd.right return rtNd
// java
// LC 1448
// V1
// IDEA : DFS
// https://leetcode.com/problems/count-good-nodes-in-binary-tree/editorial/
private int numGoodNodes = 0;
public int goodNodes_2(TreeNode root) {
dfs(root, Integer.MIN_VALUE);
return numGoodNodes;
}
private void dfs(TreeNode node, int maxSoFar) {
if (maxSoFar <= node.val) {
++;
numGoodNodes}
if (node.right != null) {
dfs(node.right, Math.max(node.val, maxSoFar));
}
if (node.left != null) {
dfs(node.left, Math.max(node.val, maxSoFar));
}
}
// V2
// IDEA : DFS + Iterative
// https://leetcode.com/problems/count-good-nodes-in-binary-tree/editorial/
class Pair {
public TreeNode node;
public int maxSoFar;
public Pair(TreeNode node, int maxSoFar) {
this.node = node;
this.maxSoFar = maxSoFar;
}
}
public int goodNodes_3(TreeNode root) {
int numGoodNodes = 0;
Stack<Pair> stack = new Stack<>();
.push(new Pair(root, Integer.MIN_VALUE));
stack
while (stack.size() > 0) {
= stack.pop();
Pair curr if (curr.maxSoFar <= curr.node.val) {
++;
numGoodNodes}
if (curr.node.left != null) {
.push(new Pair(curr.node.left, Math.max(curr.node.val, curr.maxSoFar)));
stack}
if (curr.node.right != null) {
.push(new Pair(curr.node.right, Math.max(curr.node.val, curr.maxSoFar)));
stack}
}
return numGoodNodes;
}
// java
// LC 110
// V0
// IDEA : DFS
// https://www.bilibili.com/video/BV1Ug411S7my/?share_source=copy_web
public boolean isBalanced(TreeNode root) {
// edge
if (root == null) {
return true;
}
if (root.left == null && root.right == null) {
return true;
}
int leftDepth = getDepthDFS(root.left);
int rightDepth = getDepthDFS(root.right);
// check if `current` node is `balanced`
if (Math.abs(leftDepth - rightDepth) > 1) {
return false;
}
// dfs call
// recursively check if `sub left node` and `sub right node` are `balanced`
return isBalanced(root.left) && isBalanced(root.right);
}
// LC 104
public int getDepthDFS(TreeNode root) {
if (root == null) {
return 0;
}
return Math.max(getDepthDFS(root.left), getDepthDFS(root.right)) + 1;
}
// V1
// IDEA : TOP DOWN RECURSION
// https://leetcode.com/problems/balanced-binary-tree/editorial/
// Recursively obtain the height of a tree. An empty tree has -1 height
private int height(TreeNode root) {
// An empty tree has height -1
if (root == null) {
return -1;
}
return 1 + Math.max(height(root.left), height(root.right));
}
public boolean isBalanced(TreeNode root) {
// An empty tree satisfies the definition of a balanced tree
if (root == null) {
return true;
}
// Check if subtrees have height within 1. If they do, check if the
// subtrees are balanced
return Math.abs(height(root.left) - height(root.right)) < 2
&& isBalanced(root.left)
&& isBalanced(root.right);
}
// java
// LC 2415
// V0-1
// IDEA: DFS + `left, right, layer as helper func parameter` (fixed by gpt)
public TreeNode reverseOddLevels_0_1(TreeNode root) {
if (root == null)
return null;
reverseHelper(root.left, root.right, 1);
return root;
}
/**
* NOTE !!!
*
* we NEED to setup 3 parameter in the helper func
*
* 1. left node
* 2. right node
* 3. layer
*
*
* NOTE !!!
*
* the helper func return NOTHING !!! (e.g. void)
*/
private void reverseHelper(TreeNode left, TreeNode right, int level) {
if (left == null || right == null)
return;
// Swap values if we're at an odd level
if (level % 2 == 1) {
int temp = left.val;
.val = right.val;
left.val = temp;
right}
/** NOTE !!! below
*
*
*/
// Recurse into symmetric children
reverseHelper(left.left, right.right, level + 1);
reverseHelper(left.right, right.left, level + 1);
}