diff --git a/articles/binary-tree-from-preorder-and-inorder-traversal.md b/articles/binary-tree-from-preorder-and-inorder-traversal.md index 0e2049976..621fc91cd 100644 --- a/articles/binary-tree-from-preorder-and-inorder-traversal.md +++ b/articles/binary-tree-from-preorder-and-inorder-traversal.md @@ -240,11 +240,11 @@ class Solution { ### Time & Space Complexity * Time complexity: $O(n ^ 2)$ -* Space complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ --- -## 2. Hash Map +## 2. Hash Map + Depth First Search ::tabs-start @@ -780,4 +780,4 @@ class Solution { ### Time & Space Complexity * Time complexity: $O(n)$ -* Space complexity: $O(n)$ \ No newline at end of file +* Space complexity: $O(n)$ for recursion stack. \ No newline at end of file diff --git a/articles/binary-tree-zigzag-level-order-traversal.md b/articles/binary-tree-zigzag-level-order-traversal.md new file mode 100644 index 000000000..d41aac9cd --- /dev/null +++ b/articles/binary-tree-zigzag-level-order-traversal.md @@ -0,0 +1,647 @@ +## 1. Breadth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def zigzagLevelOrder(self, root: Optional[TreeNode]) -> List[List[int]]: + res = [] + q = deque([root] if root else []) + while q: + level = [] + for i in range(len(q)): + node = q.popleft() + level.append(node.val) + if node.left: + q.append(node.left) + if node.right: + q.append(node.right) + if len(res) % 2: + level.reverse() + res.append(level) + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public List> zigzagLevelOrder(TreeNode root) { + List> res = new ArrayList<>(); + if (root == null) return res; + + Queue q = new LinkedList<>(); + q.offer(root); + + while (!q.isEmpty()) { + List level = new ArrayList<>(); + for (int i = q.size(); i > 0; i--) { + TreeNode node = q.poll(); + level.add(node.val); + if (node.left != null) q.offer(node.left); + if (node.right != null) q.offer(node.right); + } + if (res.size() % 2 != 0) Collections.reverse(level); + res.add(level); + } + return res; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + vector> zigzagLevelOrder(TreeNode* root) { + vector> res; + if (!root) return res; + + queue q; + q.push(root); + + while (!q.empty()) { + vector level; + for (int i = q.size(); i > 0; i--) { + TreeNode* node = q.front(); + q.pop(); + level.push_back(node->val); + if (node->left) q.push(node->left); + if (node->right) q.push(node->right); + } + if (res.size() % 2) reverse(level.begin(), level.end()); + res.push_back(level); + } + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number[][]} + */ + zigzagLevelOrder(root) { + const res = []; + if (!root) return res; + + const queue = new Queue([root]); + + while (!queue.isEmpty()) { + const level = []; + for (let i = queue.size(); i > 0; i--) { + const node = queue.pop(); + level.push(node.val); + if (node.left) queue.push(node.left); + if (node.right) queue.push(node.right); + } + if (res.length % 2 !== 0) level.reverse(); + res.push(level); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Breadth First Search (Optimal) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def zigzagLevelOrder(self, root: Optional[TreeNode]) -> List[List[int]]: + res = [] + q = deque([root] if root else []) + while q: + size = len(q) + level = [0] * size + for i in range(size): + node = q.popleft() + idx = size - i - 1 if len(res) % 2 else i + level[idx] = node.val + if node.left: + q.append(node.left) + if node.right: + q.append(node.right) + res.append(level) + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public List> zigzagLevelOrder(TreeNode root) { + List> res = new ArrayList<>(); + if (root == null) return res; + + Queue q = new LinkedList<>(); + q.offer(root); + + while (!q.isEmpty()) { + int size = q.size(); + Integer[] level = new Integer[size]; + for (int i = 0; i < size; i++) { + TreeNode node = q.poll(); + int idx = (res.size() % 2 == 0) ? i : size - i - 1; + level[idx] = node.val; + if (node.left != null) q.offer(node.left); + if (node.right != null) q.offer(node.right); + } + res.add(Arrays.asList(level)); + } + return res; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + vector> zigzagLevelOrder(TreeNode* root) { + vector> res; + if (!root) return res; + + queue q; + q.push(root); + + while (!q.empty()) { + int size = q.size(); + vector level(size); + for (int i = 0; i < size; ++i) { + TreeNode* node = q.front(); + q.pop(); + int idx = (res.size() % 2 == 0) ? i : size - i - 1; + level[idx] = node->val; + if (node->left) q.push(node->left); + if (node->right) q.push(node->right); + } + res.push_back(level); + } + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number[][]} + */ + zigzagLevelOrder(root) { + const res = []; + if (!root) return res; + + const q = new Queue([root]); + + while (!q.isEmpty()) { + const size = q.size(); + const level = Array(size).fill(0); + + for (let i = 0; i < size; i++) { + const node = q.pop(); + const idx = res.length % 2 === 0 ? i : size - i - 1; + level[idx] = node.val; + if (node.left) q.push(node.left); + if (node.right) q.push(node.right); + } + res.push(level); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Depth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def zigzagLevelOrder(self, root: Optional[TreeNode]) -> List[List[int]]: + res = [] + + def dfs(node, depth): + if not node: + return + if depth == len(res): + res.append([]) + res[depth].append(node.val) + dfs(node.left, depth + 1) + dfs(node.right, depth + 1) + + dfs(root, 0) + for i, level in enumerate(res): + if i & 1: + level.reverse() + + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public List> zigzagLevelOrder(TreeNode root) { + List> res = new ArrayList<>(); + dfs(root, 0, res); + for (int i = 0; i < res.size(); i++) { + if ((i & 1) == 1) { + Collections.reverse(res.get(i)); + } + } + return res; + } + + private void dfs(TreeNode node, int depth, List> res) { + if (node == null) return; + if (depth == res.size()) { + res.add(new ArrayList<>()); + } + res.get(depth).add(node.val); + dfs(node.left, depth + 1, res); + dfs(node.right, depth + 1, res); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + vector> zigzagLevelOrder(TreeNode* root) { + vector> res; + dfs(root, 0, res); + for (int i = 0; i < res.size(); ++i) { + if (i & 1) { + reverse(res[i].begin(), res[i].end()); + } + } + return res; + } + +private: + void dfs(TreeNode* node, int depth, vector>& res) { + if (!node) return; + if (depth == res.size()) { + res.push_back({}); + } + res[depth].push_back(node->val); + dfs(node->left, depth + 1, res); + dfs(node->right, depth + 1, res); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number[][]} + */ + zigzagLevelOrder(root) { + const res = []; + const dfs = (node, depth) => { + if (!node) return; + if (depth === res.length) res.push([]); + res[depth].push(node.val); + dfs(node.left, depth + 1); + dfs(node.right, depth + 1); + }; + + dfs(root, 0); + for (let i = 0; i < res.length; i++) { + if (i % 2 === 1) res[i].reverse(); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Iterative DFS + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def zigzagLevelOrder(self, root: Optional[TreeNode]) -> List[List[int]]: + if not root: + return [] + + res = [] + stack = [(root, 0)] + + while stack: + node, depth = stack.pop() + if depth == len(res): + res.append([]) + + res[depth].append(node.val) + + if node.right: + stack.append((node.right, depth + 1)) + if node.left: + stack.append((node.left, depth + 1)) + + for i in range(len(res)): + if i % 2 == 1: + res[i].reverse() + + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public List> zigzagLevelOrder(TreeNode root) { + if (root == null) return new ArrayList<>(); + + List> res = new ArrayList<>(); + Stack stack = new Stack<>(); + stack.push(new Pair(root, 0)); + + while (!stack.isEmpty()) { + Pair current = stack.pop(); + TreeNode node = current.getKey(); + int depth = current.getValue(); + + if (depth == res.size()) { + res.add(new ArrayList<>()); + } + res.get(depth).add(node.val); + + if (node.right != null) stack.push(new Pair<>(node.right, depth + 1)); + if (node.left != null) stack.push(new Pair<>(node.left, depth + 1)); + } + + for (int i = 0; i < res.size(); i++) { + if (i % 2 == 1) { + Collections.reverse(res.get(i)); + } + } + + return res; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + vector> zigzagLevelOrder(TreeNode* root) { + if (!root) return {}; + + vector> res; + stack> s; + s.push({root, 0}); + + while (!s.empty()) { + auto [node, depth] = s.top(); + s.pop(); + + if (depth == res.size()) { + res.push_back({}); + } + res[depth].push_back(node->val); + + if (node->right) s.push({node->right, depth + 1}); + if (node->left) s.push({node->left, depth + 1}); + } + + for (int i = 0; i < res.size(); i++) { + if (i % 2 == 1) { + reverse(res[i].begin(), res[i].end()); + } + } + + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number[][]} + */ + zigzagLevelOrder(root) { + if (!root) return []; + + const res = []; + const stack = [[root, 0]]; + + while (stack.length) { + const [node, depth] = stack.pop(); + + if (depth === res.length) res.push([]); + res[depth].push(node.val); + + if (node.right) stack.push([node.right, depth + 1]); + if (node.left) stack.push([node.left, depth + 1]); + } + + for (let i = 0; i < res.length; i++) { + if (i % 2 === 1) res[i].reverse(); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/check-completeness-of-a-binary-tree.md b/articles/check-completeness-of-a-binary-tree.md new file mode 100644 index 000000000..90a185822 --- /dev/null +++ b/articles/check-completeness-of-a-binary-tree.md @@ -0,0 +1,591 @@ +## 1. Breadth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def isCompleteTree(self, root: Optional[TreeNode]) -> bool: + q = deque([root]) + while q: + node = q.popleft() + if node: + q.append(node.left) + q.append(node.right) + else: + while q: + if q.popleft(): + return False + return True +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public boolean isCompleteTree(TreeNode root) { + Queue q = new LinkedList<>(); + q.add(root); + + while (!q.isEmpty()) { + TreeNode node = q.poll(); + if (node != null) { + q.add(node.left); + q.add(node.right); + } else { + while (!q.isEmpty()) { + if (q.poll() != null) { + return false; + } + } + } + } + return true; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + bool isCompleteTree(TreeNode* root) { + queue q; + q.push(root); + + while (!q.empty()) { + TreeNode* node = q.front(); + q.pop(); + if (node) { + q.push(node->left); + q.push(node->right); + } else { + while (!q.empty()) { + if (q.front()) { + return false; + } + q.pop(); + } + } + } + return true; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {boolean} + */ + isCompleteTree(root) { + const queue = new Queue([root]); + + while (!queue.isEmpty()) { + const node = queue.pop(); + if (node) { + queue.push(node.left); + queue.push(node.right); + } else { + while (!queue.isEmpty()) { + if (queue.pop()) { + return false; + } + } + } + } + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Breadth First Search (Optimal) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def isCompleteTree(self, root: Optional[TreeNode]) -> bool: + q = deque([root]) + nullSeen = False + while q: + node = q.popleft() + if node: + if nullSeen: + return False + q.append(node.left) + q.append(node.right) + else: + nullSeen = True + return True +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public boolean isCompleteTree(TreeNode root) { + Queue q = new LinkedList<>(); + q.add(root); + boolean nullSeen = false; + + while (!q.isEmpty()) { + TreeNode node = q.poll(); + if (node != null) { + if (nullSeen) return false; + q.add(node.left); + q.add(node.right); + } else { + nullSeen = true; + } + } + return true; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + bool isCompleteTree(TreeNode* root) { + queue q; + q.push(root); + bool nullSeen = false; + + while (!q.empty()) { + TreeNode* node = q.front(); + q.pop(); + if (node) { + if (nullSeen) return false; + q.push(node->left); + q.push(node->right); + } else { + nullSeen = true; + } + } + return true; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {boolean} + */ + isCompleteTree(root) { + const queue = new Queue([root]); + let nullSeen = false; + + while (!queue.isEmpty()) { + const node = queue.pop(); + if (node) { + if (nullSeen) return false; + queue.push(node.left); + queue.push(node.right); + } else { + nullSeen = true; + } + } + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Depth First Search (Two Pass) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def isCompleteTree(self, root: Optional[TreeNode]) -> bool: + def dfs(node, index, n): + if not node: + return True + if index >= n: + return False + + left = dfs(node.left, 2 * index + 1, n) + right = dfs(node.right, 2 * index + 2, n) + return left and right + + def countNodes(node): + if not node: + return 0 + return 1 + countNodes(node.left) + countNodes(node.right) + + n = countNodes(root) + return dfs(root, 0, n) +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + private int countNodes(TreeNode root) { + if (root == null) return 0; + return 1 + countNodes(root.left) + countNodes(root.right); + } + + private boolean dfs(TreeNode node, int index, int n) { + if (node == null) return true; + if (index >= n) return false; + return dfs(node.left, 2 * index + 1, n) && dfs(node.right, 2 * index + 2, n); + } + + public boolean isCompleteTree(TreeNode root) { + int n = countNodes(root); + return dfs(root, 0, n); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + int countNodes(TreeNode* root) { + if (!root) return 0; + return 1 + countNodes(root->left) + countNodes(root->right); + } + + bool dfs(TreeNode* node, int index, int n) { + if (!node) return true; + if (index >= n) return false; + return dfs(node->left, 2 * index + 1, n) && dfs(node->right, 2 * index + 2, n); + } + + bool isCompleteTree(TreeNode* root) { + int n = countNodes(root); + return dfs(root, 0, n); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {boolean} + */ + isCompleteTree(root) { + const countNodes = (node) => { + if (!node) return 0; + return 1 + countNodes(node.left) + countNodes(node.right); + }; + + const dfs = (node, index, n) => { + if (!node) return true; + if (index >= n) return false; + return dfs(node.left, 2 * index + 1, n) && dfs(node.right, 2 * index + 2, n); + }; + + const n = countNodes(root); + return dfs(root, 0, n); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 4. Depth First Search (Optimal) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def isCompleteTree(self, root: Optional[TreeNode]) -> bool: + treeHgt = 0 + nullSeen = False + + def dfs(node, hgt): + nonlocal treeHgt, nullSeen + if not node: + if treeHgt == 0: + treeHgt = hgt + elif hgt == treeHgt - 1: + nullSeen = True + elif hgt != treeHgt: + return False + return not (hgt == treeHgt and nullSeen) + + return dfs(node.left, hgt + 1) and dfs(node.right, hgt + 1) + + return dfs(root, 0) +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + private int treeHgt = 0; + private boolean nullSeen = false; + + private boolean dfs(TreeNode node, int hgt) { + if (node == null) { + if (treeHgt == 0) { + treeHgt = hgt; + } else if (hgt == treeHgt - 1) { + nullSeen = true; + } else if (hgt != treeHgt) { + return false; + } + return !(hgt == treeHgt && nullSeen); + } + + return dfs(node.left, hgt + 1) && dfs(node.right, hgt + 1); + } + + public boolean isCompleteTree(TreeNode root) { + return dfs(root, 0); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + int treeHgt = 0; + bool nullSeen = false; + + bool dfs(TreeNode* node, int hgt) { + if (!node) { + if (treeHgt == 0) { + treeHgt = hgt; + } else if (hgt == treeHgt - 1) { + nullSeen = true; + } else if (hgt != treeHgt) { + return false; + } + return !(hgt == treeHgt && nullSeen); + } + + return dfs(node->left, hgt + 1) && dfs(node->right, hgt + 1); + } + + bool isCompleteTree(TreeNode* root) { + return dfs(root, 0); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {boolean} + */ + isCompleteTree(root) { + let treeHgt = 0; + let nullSeen = false; + + const dfs = (node, hgt) => { + if (!node) { + if (treeHgt === 0) { + treeHgt = hgt; + } else if (hgt === treeHgt - 1) { + nullSeen = true; + } else if (hgt !== treeHgt) { + return false; + } + return !(hgt === treeHgt && nullSeen); + } + + return dfs(node.left, hgt + 1) && dfs(node.right, hgt + 1); + }; + + return dfs(root, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. \ No newline at end of file diff --git a/articles/construct-binary-tree-from-inorder-and-postorder-traversal.md b/articles/construct-binary-tree-from-inorder-and-postorder-traversal.md new file mode 100644 index 000000000..eb086eacd --- /dev/null +++ b/articles/construct-binary-tree-from-inorder-and-postorder-traversal.md @@ -0,0 +1,470 @@ +## 1. Depth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def buildTree(self, inorder: List[int], postorder: List[int]) -> Optional[TreeNode]: + if not postorder or not inorder: + return None + + root = TreeNode(postorder[-1]) + mid = inorder.index(postorder[-1]) + root.left = self.buildTree(inorder[:mid], postorder[:mid]) + root.right = self.buildTree(inorder[mid + 1:], postorder[mid:-1]) + return root +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public TreeNode buildTree(int[] inorder, int[] postorder) { + if (postorder.length == 0 || inorder.length == 0) { + return null; + } + + TreeNode root = new TreeNode(postorder[postorder.length - 1]); + int mid = 0; + for (int i = 0; i < inorder.length; i++) { + if (inorder[i] == postorder[postorder.length - 1]) { + mid = i; + break; + } + } + + root.left = buildTree( + Arrays.copyOfRange(inorder, 0, mid), + Arrays.copyOfRange(postorder, 0, mid) + ); + root.right = buildTree( + Arrays.copyOfRange(inorder, mid + 1, inorder.length), + Arrays.copyOfRange(postorder, mid, postorder.length - 1) + ); + + return root; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + TreeNode* buildTree(vector& inorder, vector& postorder) { + if (postorder.empty() || inorder.empty()) { + return nullptr; + } + + TreeNode* root = new TreeNode(postorder.back()); + auto it = find(inorder.begin(), inorder.end(), postorder.back()); + int mid = distance(inorder.begin(), it); + + vector leftInorder(inorder.begin(), inorder.begin() + mid); + vector rightInorder(inorder.begin() + mid + 1, inorder.end()); + vector leftPostorder(postorder.begin(), postorder.begin() + mid); + vector rightPostorder(postorder.begin() + mid, postorder.end() - 1); + + root->left = buildTree(leftInorder, leftPostorder); + root->right = buildTree(rightInorder, rightPostorder); + + return root; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {number[]} inorder + * @param {number[]} postorder + * @return {TreeNode} + */ + buildTree(inorder, postorder) { + if (postorder.length === 0 || inorder.length === 0) { + return null; + } + + const rootVal = postorder[postorder.length - 1]; + const root = new TreeNode(rootVal); + const mid = inorder.indexOf(rootVal); + + root.left = this.buildTree(inorder.slice(0, mid), postorder.slice(0, mid)); + root.right = this.buildTree(inorder.slice(mid + 1), postorder.slice(mid, postorder.length - 1)); + + return root; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 2. Hash Map + Depth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def buildTree(self, inorder: List[int], postorder: List[int]) -> Optional[TreeNode]: + inorderIdx = {v: i for i, v in enumerate(inorder)} + + def dfs(l, r): + if l > r: + return None + + root = TreeNode(postorder.pop()) + idx = inorderIdx[root.val] + root.right = dfs(idx + 1, r) + root.left = dfs(l, idx - 1) + return root + + return dfs(0, len(inorder) - 1) +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + private HashMap inorderIdx; + private int postIdx; + + public TreeNode buildTree(int[] inorder, int[] postorder) { + inorderIdx = new HashMap<>(); + for (int i = 0; i < inorder.length; i++) { + inorderIdx.put(inorder[i], i); + } + postIdx = postorder.length - 1; + + return dfs(0, inorder.length - 1, postorder); + } + + private TreeNode dfs(int l, int r, int[] postorder) { + if (l > r) { + return null; + } + + TreeNode root = new TreeNode(postorder[postIdx--]); + int idx = inorderIdx.get(root.val); + root.right = dfs(idx + 1, r, postorder); + root.left = dfs(l, idx - 1, postorder); + return root; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + unordered_map inorderIdx; + int postIdx; + + TreeNode* buildTree(vector& inorder, vector& postorder) { + for (int i = 0; i < inorder.size(); i++) { + inorderIdx[inorder[i]] = i; + } + postIdx = postorder.size() - 1; + + return dfs(0, inorder.size() - 1, postorder); + } + +private: + TreeNode* dfs(int l, int r, vector& postorder) { + if (l > r) { + return nullptr; + } + + TreeNode* root = new TreeNode(postorder[postIdx--]); + int idx = inorderIdx[root->val]; + root->right = dfs(idx + 1, r, postorder); + root->left = dfs(l, idx - 1, postorder); + return root; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {number[]} inorder + * @param {number[]} postorder + * @return {TreeNode} + */ + buildTree(inorder, postorder) { + const inorderIdx = new Map(); + inorder.forEach((val, idx) => inorderIdx.set(val, idx)); + let postIdx = postorder.length - 1; + + const dfs = (l, r) => { + if (l > r) return null; + + const root = new TreeNode(postorder[postIdx--]); + const idx = inorderIdx.get(root.val); + root.right = dfs(idx + 1, r); + root.left = dfs(l, idx - 1); + return root; + }; + + return dfs(0, inorder.length - 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Depth First Search (Optimal) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def buildTree(self, inorder: List[int], postorder: List[int]) -> Optional[TreeNode]: + postIdx = inIdx = len(postorder) - 1 + def dfs(limit): + nonlocal postIdx, inIdx + if postIdx < 0: + return None + if inorder[inIdx] == limit: + inIdx -= 1 + return None + + root = TreeNode(postorder[postIdx]) + postIdx -= 1 + root.right = dfs(root.val) + root.left = dfs(limit) + return root + return dfs(float('inf')) +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + private int postIdx; + private int inIdx; + + public TreeNode buildTree(int[] inorder, int[] postorder) { + postIdx = postorder.length - 1; + inIdx = inorder.length - 1; + + return dfs(postorder, inorder, Integer.MAX_VALUE); + } + + private TreeNode dfs(int[] postorder, int[] inorder, int limit) { + if (postIdx < 0) { + return null; + } + + if (inorder[inIdx] == limit) { + inIdx--; + return null; + } + + TreeNode root = new TreeNode(postorder[postIdx--]); + root.right = dfs(postorder, inorder, root.val); + root.left = dfs(postorder, inorder, limit); + return root; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + int postIdx; + int inIdx; + + TreeNode* buildTree(vector& inorder, vector& postorder) { + postIdx = postorder.size() - 1; + inIdx = inorder.size() - 1; + + return dfs(postorder, inorder, numeric_limits::max()); + } + +private: + TreeNode* dfs(vector& postorder, vector& inorder, int limit) { + if (postIdx < 0) { + return nullptr; + } + + if (inorder[inIdx] == limit) { + inIdx--; + return nullptr; + } + + TreeNode* root = new TreeNode(postorder[postIdx--]); + root->right = dfs(postorder, inorder, root->val); + root->left = dfs(postorder, inorder, limit); + return root; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {number[]} inorder + * @param {number[]} postorder + * @return {TreeNode} + */ + buildTree(inorder, postorder) { + let postIdx = postorder.length - 1; + let inIdx = inorder.length - 1; + + const dfs = (limit) => { + if (postIdx < 0) return null; + + if (inorder[inIdx] === limit) { + inIdx--; + return null; + } + + const root = new TreeNode(postorder[postIdx--]); + root.right = dfs(root.val); + root.left = dfs(limit); + return root; + }; + + return dfs(Infinity); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. \ No newline at end of file diff --git a/articles/construct-string-from-binary-tree.md b/articles/construct-string-from-binary-tree.md new file mode 100644 index 000000000..98b9da146 --- /dev/null +++ b/articles/construct-string-from-binary-tree.md @@ -0,0 +1,518 @@ +## 1. Depth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def tree2str(self, root: Optional[TreeNode]) -> str: + if not root: + return "" + + cur = root.val + left = self.tree2str(root.left) + right = self.tree2str(root.right) + + if left and right: + return f"{cur}({left})({right})" + + if right: + return f"{cur}()({right})" + + if left: + return f"{cur}({left})" + + return str(cur) +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public String tree2str(TreeNode root) { + if (root == null) { + return ""; + } + + String cur = Integer.toString(root.val); + String left = tree2str(root.left); + String right = tree2str(root.right); + + if (!left.isEmpty() && !right.isEmpty()) { + return cur + "(" + left + ")" + "(" + right + ")"; + } + + if (!right.isEmpty()) { + return cur + "()" + "(" + right + ")"; + } + + if (!left.isEmpty()) { + return cur + "(" + left + ")"; + } + + return cur; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + string tree2str(TreeNode* root) { + if (!root) { + return ""; + } + + string cur = to_string(root->val); + string left = tree2str(root->left); + string right = tree2str(root->right); + + if (!left.empty() && !right.empty()) { + return cur + "(" + left + ")(" + right + ")"; + } + + if (!right.empty()) { + return cur + "()(" + right + ")"; + } + + if (!left.empty()) { + return cur + "(" + left + ")"; + } + + return cur; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {string} + */ + tree2str(root) { + if (!root) { + return ""; + } + + const cur = root.val.toString(); + const left = this.tree2str(root.left); + const right = this.tree2str(root.right); + + if (left && right) { + return `${cur}(${left})(${right})`; + } + + if (right) { + return `${cur}()(${right})`; + } + + if (left) { + return `${cur}(${left})`; + } + + return cur; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 2. Depth First Search (Optimal) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def tree2str(self, root: Optional[TreeNode]) -> str: + res = [] + + def preorder(root): + if not root: + return + res.append("(") + res.append(str(root.val)) + if not root.left and root.right: + res.append("()") + preorder(root.left) + preorder(root.right) + res.append(")") + + preorder(root) + return "".join(res)[1:-1] +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public String tree2str(TreeNode root) { + StringBuilder res = new StringBuilder(); + + preorder(root, res); + return res.substring(1, res.length() - 1); + } + + private void preorder(TreeNode root, StringBuilder res) { + if (root == null) return; + + res.append("(").append(root.val); + if (root.left == null && root.right != null) { + res.append("()"); + } + preorder(root.left, res); + preorder(root.right, res); + res.append(")"); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + string tree2str(TreeNode* root) { + string res; + preorder(root, res); + return res.substr(1, res.size() - 2); + } + +private: + void preorder(TreeNode* root, string& res) { + if (!root) return; + + res += "(" + to_string(root->val); + if (!root->left && root->right) { + res += "()"; + } + preorder(root->left, res); + preorder(root->right, res); + res += ")"; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {string} + */ + tree2str(root) { + let res = []; + + const preorder = (root) => { + if (!root) return; + + res.push("("); + res.push(root.val.toString()); + if (!root.left && root.right) { + res.push("()"); + } + preorder(root.left); + preorder(root.right); + res.push(")"); + }; + + preorder(root); + return res.join("").slice(1, -1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Iterative DFS + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def tree2str(self, root: Optional[TreeNode]) -> str: + if not root: + return "" + + res = [] + stack = [] + last_visited = None + cur = root + + while cur or stack: + if cur: + res.append(f"({cur.val}") + if not cur.left and cur.right: + res.append("()") + + stack.append(cur) + cur = cur.left + else: + top = stack[-1] + if top.right and last_visited != top.right: + cur = top.right + else: + stack.pop() + res.append(")") + last_visited = top + + return "".join(res)[1:-1] +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public String tree2str(TreeNode root) { + if (root == null) { + return ""; + } + + StringBuilder res = new StringBuilder(); + Stack stack = new Stack<>(); + TreeNode lastVisited = null; + TreeNode cur = root; + + while (cur != null || !stack.isEmpty()) { + if (cur != null) { + res.append("(").append(cur.val); + if (cur.left == null && cur.right != null) { + res.append("()"); + } + + stack.push(cur); + cur = cur.left; + } else { + TreeNode top = stack.peek(); + if (top.right != null && lastVisited != top.right) { + cur = top.right; + } else { + stack.pop(); + res.append(")"); + lastVisited = top; + } + } + } + + return res.substring(1, res.length() - 1); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + string tree2str(TreeNode* root) { + if (!root) { + return ""; + } + + string res; + stack stack; + TreeNode* lastVisited = nullptr; + TreeNode* cur = root; + + while (cur || !stack.empty()) { + if (cur) { + res += "(" + to_string(cur->val); + if (!cur->left && cur->right) { + res += "()"; + } + + stack.push(cur); + cur = cur->left; + } else { + TreeNode* top = stack.top(); + if (top->right && lastVisited != top->right) { + cur = top->right; + } else { + stack.pop(); + res += ")"; + lastVisited = top; + } + } + } + + return res.substr(1, res.size() - 2); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {string} + */ + tree2str(root) { + if (!root) { + return ""; + } + + let res = []; + let stack = []; + let lastVisited = null; + let cur = root; + + while (cur || stack.length > 0) { + if (cur) { + res.push(`(${cur.val}`); + if (!cur.left && cur.right) { + res.push("()"); + } + + stack.push(cur); + cur = cur.left; + } else { + let top = stack[stack.length - 1]; + if (top.right && lastVisited !== top.right) { + cur = top.right; + } else { + stack.pop(); + res.push(")"); + lastVisited = top; + } + } + } + + return res.join("").slice(1, -1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/convert-sorted-array-to-binary-search-tree.md b/articles/convert-sorted-array-to-binary-search-tree.md new file mode 100644 index 000000000..dd933b982 --- /dev/null +++ b/articles/convert-sorted-array-to-binary-search-tree.md @@ -0,0 +1,437 @@ +## 1. Depth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def sortedArrayToBST(self, nums: List[int]) -> Optional[TreeNode]: + if not nums: + return None + + mid = len(nums) // 2 + root = TreeNode(nums[mid]) + root.left = self.sortedArrayToBST(nums[:mid]) + root.right = self.sortedArrayToBST(nums[mid + 1:]) + return root +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public TreeNode sortedArrayToBST(int[] nums) { + if (nums.length == 0) { + return null; + } + + int mid = nums.length / 2; + TreeNode root = new TreeNode(nums[mid]); + root.left = sortedArrayToBST(Arrays.copyOfRange(nums, 0, mid)); + root.right = sortedArrayToBST(Arrays.copyOfRange(nums, mid + 1, nums.length)); + return root; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + TreeNode* sortedArrayToBST(vector& nums) { + if (nums.empty()) { + return nullptr; + } + + int mid = nums.size() / 2; + TreeNode* root = new TreeNode(nums[mid]); + vector left(nums.begin(), nums.begin() + mid); + vector right(nums.begin() + mid + 1, nums.end()); + root->left = sortedArrayToBST(left); + root->right = sortedArrayToBST(right); + return root; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {number[]} nums + * @return {TreeNode} + */ + sortedArrayToBST(nums) { + if (nums.length === 0) { + return null; + } + + const mid = Math.floor(nums.length / 2); + const root = new TreeNode(nums[mid]); + root.left = this.sortedArrayToBST(nums.slice(0, mid)); + root.right = this.sortedArrayToBST(nums.slice(mid + 1)); + return root; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Depth First Search (Optimal) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def sortedArrayToBST(self, nums: List[int]) -> TreeNode: + def helper(l, r): + if l > r: + return None + m = (l + r) // 2 + root = TreeNode(nums[m]) + root.left = helper(l, m - 1) + root.right = helper(m + 1, r) + return root + + return helper(0, len(nums) - 1) +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public TreeNode sortedArrayToBST(int[] nums) { + return helper(nums, 0, nums.length - 1); + } + + private TreeNode helper(int[] nums, int l, int r) { + if (l > r) { + return null; + } + int m = (l + r) / 2; + TreeNode root = new TreeNode(nums[m]); + root.left = helper(nums, l, m - 1); + root.right = helper(nums, m + 1, r); + return root; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + TreeNode* sortedArrayToBST(vector& nums) { + return helper(nums, 0, nums.size() - 1); + } + +private: + TreeNode* helper(vector& nums, int l, int r) { + if (l > r) { + return nullptr; + } + int m = (l + r) / 2; + TreeNode* root = new TreeNode(nums[m]); + root->left = helper(nums, l, m - 1); + root->right = helper(nums, m + 1, r); + return root; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {number[]} nums + * @return {TreeNode} + */ + sortedArrayToBST(nums) { + const helper = (l, r) => { + if (l > r) { + return null; + } + const m = Math.floor((l + r) / 2); + const root = new TreeNode(nums[m]); + root.left = helper(l, m - 1); + root.right = helper(m + 1, r); + return root; + }; + + return helper(0, nums.length - 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: + * $O(\log n)$ space for recursion stack. + * $O(n)$ space for the output. + +--- + +## 3. Iterative DFS + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def sortedArrayToBST(self, nums: List[int]) -> TreeNode: + if not nums: + return None + + root = TreeNode(0) + stack = [(root, 0, len(nums) - 1)] + + while stack: + node, l, r = stack.pop() + m = (l + r) // 2 + node.val = nums[m] + if l <= m - 1: + node.left = TreeNode(0) + stack.append((node.left, l, m - 1)) + if m + 1 <= r: + node.right = TreeNode(0) + stack.append((node.right, m + 1, r)) + + return root +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public TreeNode sortedArrayToBST(int[] nums) { + if (nums.length == 0) { + return null; + } + + TreeNode root = new TreeNode(0); + Stack stack = new Stack<>(); + Stack nodes = new Stack<>(); + stack.push(new int[]{0, nums.length - 1}); + nodes.push(root); + + while (!stack.isEmpty()) { + int[] range = stack.pop(); + TreeNode node = nodes.pop(); + int l = range[0], r = range[1]; + int m = (l + r) / 2; + node.val = nums[m]; + + if (l <= m - 1) { + node.left = new TreeNode(0); + stack.push(new int[]{l, m - 1}); + nodes.push(node.left); + } + if (m + 1 <= r) { + node.right = new TreeNode(0); + stack.push(new int[]{m + 1, r}); + nodes.push(node.right); + } + } + + return root; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + TreeNode* sortedArrayToBST(vector& nums) { + if (nums.empty()) return nullptr; + + TreeNode* root = new TreeNode(0); + stack> stack; + stack.push({root, 0, (int)nums.size() - 1}); + + while (!stack.empty()) { + auto [node, l, r] = stack.top(); + stack.pop(); + int m = (l + r) / 2; + node->val = nums[m]; + + if (l <= m - 1) { + node->left = new TreeNode(0); + stack.push({node->left, l, m - 1}); + } + if (m + 1 <= r) { + node->right = new TreeNode(0); + stack.push({node->right, m + 1, r}); + } + } + + return root; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {number[]} nums + * @return {TreeNode} + */ + sortedArrayToBST(nums) { + if (nums.length === 0) { + return null; + } + + const root = new TreeNode(0); + const stack = [[root, 0, nums.length - 1]]; + + while (stack.length) { + const [node, l, r] = stack.pop(); + const m = Math.floor((l + r) / 2); + node.val = nums[m]; + + if (l <= m - 1) { + node.left = new TreeNode(0); + stack.push([node.left, l, m - 1]); + } + if (m + 1 <= r) { + node.right = new TreeNode(0); + stack.push([node.right, m + 1, r]); + } + } + + return root; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: + * $O(\log n)$ space for the stack. + * $O(n)$ space for the output. \ No newline at end of file diff --git a/articles/count-triplets-that-can-form-two-arrays-of-equal-xor.md b/articles/count-triplets-that-can-form-two-arrays-of-equal-xor.md new file mode 100644 index 000000000..129c14f82 --- /dev/null +++ b/articles/count-triplets-that-can-form-two-arrays-of-equal-xor.md @@ -0,0 +1,432 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def countTriplets(self, arr: List[int]) -> int: + N = len(arr) + res = 0 + + for i in range(N - 1): + for j in range(i + 1, N): + for k in range(j, N): + a = b = 0 + for idx in range(i, j): + a ^= arr[idx] + for idx in range(j, k + 1): + b ^= arr[idx] + if a == b: + res += 1 + + return res +``` + +```java +public class Solution { + public int countTriplets(int[] arr) { + int N = arr.length; + int res = 0; + + for (int i = 0; i < N - 1; i++) { + for (int j = i + 1; j < N; j++) { + for (int k = j; k < N; k++) { + int a = 0, b = 0; + for (int idx = i; idx < j; idx++) { + a ^= arr[idx]; + } + for (int idx = j; idx <= k; idx++) { + b ^= arr[idx]; + } + if (a == b) { + res++; + } + } + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int countTriplets(vector& arr) { + int N = arr.size(); + int res = 0; + + for (int i = 0; i < N - 1; ++i) { + for (int j = i + 1; j < N; ++j) { + for (int k = j; k < N; ++k) { + int a = 0, b = 0; + for (int idx = i; idx < j; ++idx) { + a ^= arr[idx]; + } + for (int idx = j; idx <= k; ++idx) { + b ^= arr[idx]; + } + if (a == b) { + res++; + } + } + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @return {number} + */ + countTriplets(arr) { + const N = arr.length; + let res = 0; + + for (let i = 0; i < N - 1; i++) { + for (let j = i + 1; j < N; j++) { + for (let k = j; k < N; k++) { + let a = 0, b = 0; + for (let idx = i; idx < j; idx++) { + a ^= arr[idx]; + } + for (let idx = j; idx <= k; idx++) { + b ^= arr[idx]; + } + if (a === b) { + res++; + } + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 4)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 2. Brute Force (Optimized) + +::tabs-start + +```python +class Solution: + def countTriplets(self, arr: List[int]) -> int: + N = len(arr) + res = 0 + + for i in range(N - 1): + a = 0 + for j in range(i + 1, N): + a ^= arr[j - 1] + b = 0 + for k in range(j, N): + b ^= arr[k] + if a == b: + res += 1 + + return res +``` + +```java +public class Solution { + public int countTriplets(int[] arr) { + int N = arr.length; + int res = 0; + + for (int i = 0; i < N - 1; i++) { + int a = 0; + for (int j = i + 1; j < N; j++) { + a ^= arr[j - 1]; + int b = 0; + for (int k = j; k < N; k++) { + b ^= arr[k]; + if (a == b) { + res++; + } + } + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int countTriplets(vector& arr) { + int N = arr.size(); + int res = 0; + + for (int i = 0; i < N - 1; ++i) { + int a = 0; + for (int j = i + 1; j < N; ++j) { + a ^= arr[j - 1]; + int b = 0; + for (int k = j; k < N; ++k) { + b ^= arr[k]; + if (a == b) { + res++; + } + } + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @return {number} + */ + countTriplets(arr) { + const N = arr.length; + let res = 0; + + for (let i = 0; i < N - 1; i++) { + let a = 0; + for (let j = i + 1; j < N; j++) { + a ^= arr[j - 1]; + let b = 0; + for (let k = j; k < N; k++) { + b ^= arr[k]; + if (a === b) { + res++; + } + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 3)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 3. Math + Bitwise XOR + +::tabs-start + +```python +class Solution: + def countTriplets(self, arr: List[int]) -> int: + N = len(arr) + res = 0 + + for i in range(N - 1): + cur_xor = arr[i] + for k in range(i + 1, N): + cur_xor ^= arr[k] + if cur_xor == 0: + res += k - i + + return res +``` + +```java +public class Solution { + public int countTriplets(int[] arr) { + int N = arr.length; + int res = 0; + + for (int i = 0; i < N - 1; i++) { + int curXor = arr[i]; + for (int k = i + 1; k < N; k++) { + curXor ^= arr[k]; + if (curXor == 0) { + res += k - i; + } + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int countTriplets(vector& arr) { + int N = arr.size(); + int res = 0; + + for (int i = 0; i < N - 1; ++i) { + int curXor = arr[i]; + for (int k = i + 1; k < N; ++k) { + curXor ^= arr[k]; + if (curXor == 0) { + res += k - i; + } + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @return {number} + */ + countTriplets(arr) { + const N = arr.length; + let res = 0; + + for (let i = 0; i < N - 1; i++) { + let curXor = arr[i]; + for (let k = i + 1; k < N; k++) { + curXor ^= arr[k]; + if (curXor === 0) { + res += k - i; + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 4. Math + Bitwise XOR (Optimal) + +::tabs-start + +```python +class Solution: + def countTriplets(self, arr: List[int]) -> int: + N = len(arr) + res = prefix = 0 + count = defaultdict(int) # number of prefixes + index_sum = defaultdict(int) # sum of indices with that prefix + count[0] = 1 + + for i in range(N): + prefix ^= arr[i] + if prefix in count: + res += i * count[prefix] - index_sum[prefix] + count[prefix] += 1 + index_sum[prefix] += i + 1 + + return res +``` + +```java +public class Solution { + public int countTriplets(int[] arr) { + int N = arr.length, res = 0, prefix = 0; + Map count = new HashMap<>(); // number of prefixes + Map indexSum = new HashMap<>(); // sum of indices with that prefix + count.put(0, 1); + + for (int i = 0; i < N; i++) { + prefix ^= arr[i]; + if (count.containsKey(prefix)) { + res += i * count.get(prefix) - indexSum.getOrDefault(prefix, 0); + } + count.put(prefix, count.getOrDefault(prefix, 0) + 1); + indexSum.put(prefix, indexSum.getOrDefault(prefix, 0) + i + 1); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int countTriplets(vector& arr) { + int N = arr.size(), res = 0, prefix = 0; + unordered_map count; // number of prefixes + unordered_map indexSum; // sum of indices with that prefix + count[0] = 1; + + for (int i = 0; i < N; i++) { + prefix ^= arr[i]; + if (count.count(prefix)) { + res += i * count[prefix] - indexSum[prefix]; + } + count[prefix]++; + indexSum[prefix] += i + 1; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @return {number} + */ + countTriplets(arr) { + const N = arr.length; + let res = 0, prefix = 0; + const count = new Map(); // number of prefixes + const indexSum = new Map(); // sum of indices with that prefix + count.set(0, 1); + + for (let i = 0; i < N; i++) { + prefix ^= arr[i]; + if (count.has(prefix)) { + res += i * count.get(prefix) - (indexSum.get(prefix) || 0); + } + count.set(prefix, (count.get(prefix) || 0) + 1); + indexSum.set(prefix, (indexSum.get(prefix) || 0) + i + 1); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/design-twitter-feed.md b/articles/design-twitter-feed.md index 2c53e29a8..56d7a5848 100644 --- a/articles/design-twitter-feed.md +++ b/articles/design-twitter-feed.md @@ -486,13 +486,14 @@ public: ```javascript /** - * const { MaxPriorityQueue } = require('@datastructures-js/priority-queue'); + * const { PriorityQueue } = require('@datastructures-js/priority-queue'); */ class Twitter { constructor() { - this.users = new Map(); - this.timestamp = 0; + this.count = 0; + this.tweetMap = new Map(); // userId -> array of [count, tweetId] + this.followMap = new Map(); // userId -> set of followeeIds } /** @@ -501,13 +502,11 @@ class Twitter { * @return {void} */ postTweet(userId, tweetId) { - if (!this.users.has(userId)) { - this.users.set(userId, { tweets: [], following: new Set() }); + if (!this.tweetMap.has(userId)) { + this.tweetMap.set(userId, []); } - this.users - .get(userId) - .tweets.push({ timestamp: this.timestamp, tweetId }); - this.timestamp += 1; + this.tweetMap.get(userId).push([this.count, tweetId]); + this.count -= 1; } /** @@ -515,38 +514,34 @@ class Twitter { * @return {number[]} */ getNewsFeed(userId) { - if (!this.users.has(userId)) { - return []; + const res = []; + if (!this.followMap.has(userId)) { + this.followMap.set(userId, new Set()); } - - const maxPQ = new MaxPriorityQueue(tweet => tweet.timestamp); - const seenTweets = new Set(); - - const user = this.users.get(userId); - user.tweets.forEach(tweet => { - if (!seenTweets.has(tweet.tweetId)) { - maxPQ.enqueue(tweet); - seenTweets.add(tweet.tweetId); + this.followMap.get(userId).add(userId); + const minHeap = new PriorityQueue( + (a, b) => a[0] - b[0] + ); + + for (const followeeId of this.followMap.get(userId)) { + if (this.tweetMap.has(followeeId)) { + const tweets = this.tweetMap.get(followeeId); + const index = tweets.length - 1; + const [count, tweetId] = tweets[index]; + minHeap.enqueue([count, tweetId, followeeId, index - 1]); } - }); + } - user.following.forEach(followeeId => { - if (this.users.has(followeeId)) { - this.users.get(followeeId).tweets.forEach(tweet => { - if (!seenTweets.has(tweet.tweetId)) { - maxPQ.enqueue(tweet); - seenTweets.add(tweet.tweetId); - } - }); + while (!minHeap.isEmpty() && res.length < 10) { + const [count, tweetId, followeeId, nextIndex] = minHeap.dequeue(); + res.push(tweetId); + if (nextIndex >= 0) { + const [olderCount, olderTweetId] = this.tweetMap.get(followeeId)[nextIndex]; + minHeap.enqueue([olderCount, olderTweetId, followeeId, nextIndex - 1]); } - }); - - const newsFeed = []; - for (let i = 0; i < 10 && !maxPQ.isEmpty(); i++) { - newsFeed.push(maxPQ.dequeue().tweetId); } - return newsFeed; + return res; } /** @@ -555,10 +550,10 @@ class Twitter { * @return {void} */ follow(followerId, followeeId) { - if (!this.users.has(followerId)) { - this.users.set(followerId, { tweets: [], following: new Set() }); + if (!this.followMap.has(followerId)) { + this.followMap.set(followerId, new Set()); } - this.users.get(followerId).following.add(followeeId); + this.followMap.get(followerId).add(followeeId); } /** @@ -567,8 +562,8 @@ class Twitter { * @return {void} */ unfollow(followerId, followeeId) { - if (this.users.has(followerId)) { - this.users.get(followerId).following.delete(followeeId); + if (this.followMap.has(followerId)) { + this.followMap.get(followerId).delete(followeeId); } } } @@ -778,7 +773,663 @@ class Twitter { ### Time & Space Complexity +* Time complexity: $O(n \log n)$ for each $getNewsFeed()$ call and $O(1)$ for remaining methods. +* Space complexity: $O(N * m + N * M + n)$ + +> Where $n$ is the total number of $followeeIds$ associated with the $userId$, $m$ is the maximum number of tweets by any user, $N$ is the total number of $userIds$ and $M$ is the maximum number of followees for any user. + +--- + +## 3. Heap (Optimal) + +::tabs-start + +```python +class Twitter: + + def __init__(self): + self.count = 0 + self.tweetMap = defaultdict(list) # userId -> list of [count, tweetIds] + self.followMap = defaultdict(set) # userId -> set of followeeId + + def postTweet(self, userId: int, tweetId: int) -> None: + self.tweetMap[userId].append([self.count, tweetId]) + if len(self.tweetMap[userId]) > 10: + self.tweetMap[userId].pop(0) + self.count -= 1 + + def getNewsFeed(self, userId: int) -> List[int]: + res = [] + minHeap = [] + self.followMap[userId].add(userId) + if len(self.followMap[userId]) >= 10: + maxHeap = [] + for followeeId in self.followMap[userId]: + if followeeId in self.tweetMap: + index = len(self.tweetMap[followeeId]) - 1 + count, tweetId = self.tweetMap[followeeId][index] + heapq.heappush(maxHeap, [-count, tweetId, followeeId, index - 1]) + if len(maxHeap) > 10: + heapq.heappop(maxHeap) + while maxHeap: + count, tweetId, followeeId, index = heapq.heappop(maxHeap) + heapq.heappush(minHeap, [-count, tweetId, followeeId, index]) + else: + for followeeId in self.followMap[userId]: + if followeeId in self.tweetMap: + index = len(self.tweetMap[followeeId]) - 1 + count, tweetId = self.tweetMap[followeeId][index] + heapq.heappush(minHeap, [count, tweetId, followeeId, index - 1]) + + while minHeap and len(res) < 10: + count, tweetId, followeeId, index = heapq.heappop(minHeap) + res.append(tweetId) + if index >= 0: + count, tweetId = self.tweetMap[followeeId][index] + heapq.heappush(minHeap, [count, tweetId, followeeId, index - 1]) + + return res + + def follow(self, followerId: int, followeeId: int) -> None: + self.followMap[followerId].add(followeeId) + + def unfollow(self, followerId: int, followeeId: int) -> None: + if followeeId in self.followMap[followerId]: + self.followMap[followerId].remove(followeeId) +``` + +```java +public class Twitter { + + private int count; + private Map> tweetMap; + private Map> followMap; + + public Twitter() { + this.count = 0; + this.tweetMap = new HashMap<>(); + this.followMap = new HashMap<>(); + } + + public void postTweet(int userId, int tweetId) { + tweetMap.computeIfAbsent(userId, k -> new ArrayList<>()) + .add(new int[]{count, tweetId}); + if (tweetMap.get(userId).size() > 10) { + tweetMap.get(userId).remove(0); + } + count--; + } + + public List getNewsFeed(int userId) { + List res = new ArrayList<>(); + PriorityQueue minHeap = new PriorityQueue<>( + (a, b) -> Integer.compare(a[0], b[0]) + ); + followMap.computeIfAbsent(userId, k -> new HashSet<>()).add(userId); + if (followMap.get(userId).size() >= 10) { + PriorityQueue maxHeap = new PriorityQueue<>( + (a, b) -> Integer.compare(a[0], b[0]) + ); + for (int followeeId : followMap.get(userId)) { + if (!tweetMap.containsKey(followeeId)) continue; + List tweets = tweetMap.get(followeeId); + int index = tweets.size() - 1; + int[] tweet = tweets.get(index); + maxHeap.offer(new int[]{-tweet[0], tweet[1], followeeId, index - 1}); + if (maxHeap.size() > 10) { + maxHeap.poll(); + } + } + while (!maxHeap.isEmpty()) { + int[] top = maxHeap.poll(); + minHeap.offer(new int[]{-top[0], top[1], top[2], top[3]}); + } + } else { + for (int followeeId : followMap.get(userId)) { + if (!tweetMap.containsKey(followeeId)) continue; + List tweets = tweetMap.get(followeeId); + int index = tweets.size() - 1; + int[] tweet = tweets.get(index); + minHeap.offer(new int[]{tweet[0], tweet[1], followeeId, index - 1}); + } + } + + while (!minHeap.isEmpty() && res.size() < 10) { + int[] top = minHeap.poll(); + res.add(top[1]); + int nextIndex = top[3]; + if (nextIndex >= 0) { + List tweets = tweetMap.get(top[2]); + int[] nextTweet = tweets.get(nextIndex); + minHeap.offer(new int[]{nextTweet[0], nextTweet[1], top[2], nextIndex - 1}); + } + } + return res; + } + + public void follow(int followerId, int followeeId) { + followMap.computeIfAbsent(followerId, k -> new HashSet<>()).add(followeeId); + } + + public void unfollow(int followerId, int followeeId) { + if (followMap.containsKey(followerId)) { + followMap.get(followerId).remove(followeeId); + } + } +} +``` + +```cpp +class Twitter { +public: + int count; + unordered_map>> tweetMap; + unordered_map> followMap; + + Twitter() { + count = 0; + } + + void postTweet(int userId, int tweetId) { + tweetMap[userId].push_back({count, tweetId}); + if (tweetMap[userId].size() > 10) { + tweetMap[userId].erase(tweetMap[userId].begin()); + } + count--; + } + + vector getNewsFeed(int userId) { + vector res; + followMap[userId].insert(userId); + priority_queue, vector>, greater>> minHeap; + if (followMap[userId].size() >= 10) { + priority_queue> maxHeap; + for (auto f : followMap[userId]) { + if (!tweetMap.count(f)) continue; + int idx = tweetMap[f].size() - 1; + auto &p = tweetMap[f][idx]; + maxHeap.push({-p.first, p.second, f, idx - 1}); + if (maxHeap.size() > 10) maxHeap.pop(); + } + while (!maxHeap.empty()) { + auto t = maxHeap.top(); + maxHeap.pop(); + minHeap.push({-t[0], t[1], t[2], t[3]}); + } + } else { + for (auto f : followMap[userId]) { + if (!tweetMap.count(f)) continue; + int idx = tweetMap[f].size() - 1; + auto &p = tweetMap[f][idx]; + minHeap.push({p.first, p.second, f, idx - 1}); + } + } + while (!minHeap.empty() && res.size() < 10) { + auto t = minHeap.top(); + minHeap.pop(); + res.push_back(t[1]); + int idx = t[3]; + if (idx >= 0) { + auto &p = tweetMap[t[2]][idx]; + minHeap.push({p.first, p.second, t[2], idx - 1}); + } + } + return res; + } + + void follow(int followerId, int followeeId) { + followMap[followerId].insert(followeeId); + } + + void unfollow(int followerId, int followeeId) { + if (followMap[followerId].count(followeeId)) { + followMap[followerId].erase(followeeId); + } + } +}; +``` + +```javascript +/** + * const { PriorityQueue } = require('@datastructures-js/priority-queue'); + */ +class Twitter { + constructor() { + this.count = 0; + this.tweetMap = new Map(); + this.followMap = new Map(); + } + + /** + * @param {number} userId + * @param {number} tweetId + * @return {void} + */ + postTweet(userId, tweetId) { + if (!this.tweetMap.has(userId)) { + this.tweetMap.set(userId, []); + } + const tweets = this.tweetMap.get(userId); + tweets.push([this.count, tweetId]); + if (tweets.length > 10) { + tweets.shift(); + } + this.count--; + } + + /** + * @param {number} userId + * @return {number[]} + */ + getNewsFeed(userId) { + const res = []; + if (!this.followMap.has(userId)) { + this.followMap.set(userId, new Set()); + } + this.followMap.get(userId).add(userId); + const minHeap = new PriorityQueue((a, b) => a[0] - b[0]); + + if (this.followMap.get(userId).size >= 10) { + const maxHeap = new PriorityQueue((a, b) => a[0] - b[0]); + for (const followeeId of this.followMap.get(userId)) { + if (!this.tweetMap.has(followeeId)) continue; + const tweets = this.tweetMap.get(followeeId); + const idx = tweets.length - 1; + const [cnt, tId] = tweets[idx]; + maxHeap.enqueue([-cnt, tId, followeeId, idx - 1]); + if (maxHeap.size() > 10) { + maxHeap.dequeue(); + } + } + while (maxHeap.size() > 0) { + const [negCount, tId, fId, idx] = maxHeap.dequeue(); + minHeap.enqueue([-negCount, tId, fId, idx]); + } + + } else { + for (const followeeId of this.followMap.get(userId)) { + if (!this.tweetMap.has(followeeId)) continue; + const tweets = this.tweetMap.get(followeeId); + const idx = tweets.length - 1; + const [cnt, tId] = tweets[idx]; + minHeap.enqueue([cnt, tId, followeeId, idx - 1]); + } + } + + while (minHeap.size() > 0 && res.length < 10) { + const [cnt, tId, fId, idx] = minHeap.dequeue(); + res.push(tId); + if (idx >= 0) { + const [olderCount, olderTId] = this.tweetMap.get(fId)[idx]; + minHeap.enqueue([olderCount, olderTId, fId, idx - 1]); + } + } + return res; + } + + /** + * @param {number} followerId + * @param {number} followeeId + * @return {void} + */ + follow(followerId, followeeId) { + if (!this.followMap.has(followerId)) { + this.followMap.set(followerId, new Set()); + } + this.followMap.get(followerId).add(followeeId); + } + + /** + * @param {number} followerId + * @param {number} followeeId + * @return {void} + */ + unfollow(followerId, followeeId) { + if (this.followMap.has(followerId)) { + this.followMap.get(followerId).delete(followeeId); + } + } +} +``` + +```csharp +public class Twitter +{ + private int count; + private Dictionary> tweetMap; // userId -> list of (count, tweetId) + private Dictionary> followMap; // userId -> set of followeeId + + public Twitter() + { + count = 0; + tweetMap = new Dictionary>(); + followMap = new Dictionary>(); + } + + public void PostTweet(int userId, int tweetId) + { + if (!tweetMap.ContainsKey(userId)) + { + tweetMap[userId] = new List<(int, int)>(); + } + tweetMap[userId].Add((count, tweetId)); + if (tweetMap[userId].Count > 10) + { + tweetMap[userId].RemoveAt(0); + } + count--; + } + + public List GetNewsFeed(int userId) + { + var res = new List(); + if (!followMap.ContainsKey(userId)) + { + followMap[userId] = new HashSet(); + } + followMap[userId].Add(userId); + var minHeap = new PriorityQueue<(int, int, int, int), int>(); + if (followMap[userId].Count >= 10) + { + var maxHeap = new PriorityQueue<(int, int, int, int), int>(); + foreach (var fId in followMap[userId]) + { + if (tweetMap.ContainsKey(fId)) + { + var tweets = tweetMap[fId]; + int idx = tweets.Count - 1; + var (c, tId) = tweets[idx]; + maxHeap.Enqueue( + ( -c, tId, fId, idx - 1 ), + -c + ); + if (maxHeap.Count > 10) + { + maxHeap.Dequeue(); + } + } + } + + while (maxHeap.Count > 0) + { + var item = maxHeap.Dequeue(); + var negCount = item.Item1; + var tId = item.Item2; + var fId = item.Item3; + var idx = item.Item4; + + int originalCount = -negCount; + minHeap.Enqueue( + ( originalCount, tId, fId, idx ), + originalCount + ); + } + } + else + { + foreach (var fId in followMap[userId]) + { + if (tweetMap.ContainsKey(fId)) + { + var tweets = tweetMap[fId]; + int idx = tweets.Count - 1; + var (c, tId) = tweets[idx]; + minHeap.Enqueue( + ( c, tId, fId, idx - 1 ), + c + ); + } + } + } + + while (minHeap.Count > 0 && res.Count < 10) + { + var (c, tId, fId, idx) = minHeap.Dequeue(); + res.Add(tId); + if (idx >= 0) + { + var (olderCount, olderTid) = tweetMap[fId][idx]; + minHeap.Enqueue( + ( olderCount, olderTid, fId, idx - 1 ), + olderCount + ); + } + } + + return res; + } + + public void Follow(int followerId, int followeeId) + { + if (!followMap.ContainsKey(followerId)) + { + followMap[followerId] = new HashSet(); + } + followMap[followerId].Add(followeeId); + } + + public void Unfollow(int followerId, int followeeId) + { + if (followMap.ContainsKey(followerId)) + { + followMap[followerId].Remove(followeeId); + } + } +} +``` + +```go +type Twitter struct { + count int + tweetMap map[int][][2]int // userId -> [count, tweetId] + followMap map[int]map[int]bool // userId -> set of followeeIds +} + +func Constructor() Twitter { + return Twitter{ + count: 0, + tweetMap: make(map[int][][2]int), + followMap: make(map[int]map[int]bool), + } +} + +func (t *Twitter) PostTweet(userId int, tweetId int) { + if _, exists := t.tweetMap[userId]; !exists { + t.tweetMap[userId] = make([][2]int, 0, 10) + } + t.tweetMap[userId] = append(t.tweetMap[userId], [2]int{t.count, tweetId}) + if len(t.tweetMap[userId]) > 10 { + t.tweetMap[userId] = t.tweetMap[userId][1:] + } + t.count-- +} + +func maxHeapComparator(a, b interface{}) int { + A := a.([]int) + B := b.([]int) + switch { + case A[0] < B[0]: + return -1 + case A[0] > B[0]: + return 1 + default: + return 0 + } +} + +func minHeapComparator(a, b interface{}) int { + A := a.([]int) + B := b.([]int) + switch { + case A[0] < B[0]: + return -1 + case A[0] > B[0]: + return 1 + default: + return 0 + } +} + +func (t *Twitter) GetNewsFeed(userId int) []int { + res := []int{} + if _, ok := t.followMap[userId]; !ok { + t.followMap[userId] = make(map[int]bool) + } + t.followMap[userId][userId] = true + minHeap := priorityqueue.NewWith(minHeapComparator) + + if len(t.followMap[userId]) >= 10 { + maxHeap := priorityqueue.NewWith(maxHeapComparator) + for fId := range t.followMap[userId] { + if tweets, exists := t.tweetMap[fId]; exists && len(tweets) > 0 { + idx := len(tweets) - 1 + c := tweets[idx][0] + tId := tweets[idx][1] + maxHeap.Enqueue([]int{-c, tId, fId, idx - 1}) + if maxHeap.Size() > 10 { + maxHeap.Dequeue() + } + } + } + + for !maxHeap.Empty() { + item, _ := maxHeap.Dequeue() + arr := item.([]int) + negCount := arr[0] + tId := arr[1] + fId := arr[2] + nextIdx := arr[3] + realCount := -negCount + minHeap.Enqueue([]int{realCount, tId, fId, nextIdx}) + } + } else { + for fId := range t.followMap[userId] { + if tweets, exists := t.tweetMap[fId]; exists && len(tweets) > 0 { + idx := len(tweets) - 1 + c := tweets[idx][0] + tId := tweets[idx][1] + minHeap.Enqueue([]int{c, tId, fId, idx - 1}) + } + } + } + + for !minHeap.Empty() && len(res) < 10 { + top, _ := minHeap.Dequeue() + arr := top.([]int) + tId := arr[1] + fId := arr[2] + nextIdx := arr[3] + + res = append(res, tId) + if nextIdx >= 0 { + older := t.tweetMap[fId][nextIdx] + minHeap.Enqueue([]int{older[0], older[1], fId, nextIdx - 1}) + } + } + + return res +} + +func (t *Twitter) Follow(followerId, followeeId int) { + if _, ok := t.followMap[followerId]; !ok { + t.followMap[followerId] = make(map[int]bool) + } + t.followMap[followerId][followeeId] = true +} + +func (t *Twitter) Unfollow(followerId, followeeId int) { + if _, ok := t.followMap[followerId]; ok { + delete(t.followMap[followerId], followeeId) + } +} +``` + +```kotlin +class Twitter { + private var count = 0 + private val tweetMap = mutableMapOf>>() + private val followMap = mutableMapOf>() + + fun postTweet(userId: Int, tweetId: Int) { + if (!tweetMap.containsKey(userId)) { + tweetMap[userId] = mutableListOf() + } + val tweets = tweetMap[userId]!! + tweets.add(Pair(count, tweetId)) + if (tweets.size > 10) { + tweets.removeAt(0) + } + count-- + } + + fun getNewsFeed(userId: Int): List { + val res = mutableListOf() + if (!followMap.containsKey(userId)) { + followMap[userId] = mutableSetOf() + } + followMap[userId]!!.add(userId) + val minHeap = PriorityQueue> { a, b -> a[0].compareTo(b[0]) } + if (followMap[userId]!!.size >= 10) { + val maxHeap = PriorityQueue> { a, b -> a[0].compareTo(b[0]) } + for (fId in followMap[userId]!!) { + if (!tweetMap.containsKey(fId)) continue + val tweets = tweetMap[fId]!! + if (tweets.isEmpty()) continue + val idx = tweets.size - 1 + val (c, tId) = tweets[idx] + maxHeap.offer(listOf(-c, tId, fId, idx - 1)) + if (maxHeap.size > 10) { + maxHeap.poll() + } + } + while (maxHeap.isNotEmpty()) { + val (negCount, tId, fId, nextIdx) = maxHeap.poll() + val realCount = -negCount + minHeap.offer(listOf(realCount, tId, fId, nextIdx)) + } + } else { + for (fId in followMap[userId]!!) { + if (!tweetMap.containsKey(fId)) continue + val tweets = tweetMap[fId]!! + if (tweets.isEmpty()) continue + val idx = tweets.size - 1 + val (c, tId) = tweets[idx] + minHeap.offer(listOf(c, tId, fId, idx - 1)) + } + } + + while (minHeap.isNotEmpty() && res.size < 10) { + val (c, tId, fId, idx) = minHeap.poll() + res.add(tId) + if (idx >= 0) { + val (olderCount, olderTid) = tweetMap[fId]!![idx] + minHeap.offer(listOf(olderCount, olderTid, fId, idx - 1)) + } + } + + return res + } + + fun follow(followerId: Int, followeeId: Int) { + if (!followMap.containsKey(followerId)) { + followMap[followerId] = mutableSetOf() + } + followMap[followerId]!!.add(followeeId) + } + + fun unfollow(followerId: Int, followeeId: Int) { + if (followMap.containsKey(followerId)) { + followMap[followerId]!!.remove(followeeId) + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + * Time complexity: $O(n)$ for each $getNewsFeed()$ call and $O(1)$ for remaining methods. * Space complexity: $O(N * m + N * M + n)$ -> Where $n$ is the total number of $followeeIds$ associated with the $userId$, $m$ is the maximum number of tweets by any user, $N$ is the total number of $userIds$ and $M$ is the maximum number of followees for any user. \ No newline at end of file +> Where $n$ is the total number of $followeeIds$ associated with the $userId$, $m$ is the maximum number of tweets by any user ($m$ can be at most $10$), $N$ is the total number of $userIds$ and $M$ is the maximum number of followees for any user. \ No newline at end of file diff --git a/articles/evaluate-boolean-binary-tree.md b/articles/evaluate-boolean-binary-tree.md new file mode 100644 index 000000000..4b8fd67ca --- /dev/null +++ b/articles/evaluate-boolean-binary-tree.md @@ -0,0 +1,309 @@ +## 1. Depth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def evaluateTree(self, root: Optional[TreeNode]) -> bool: + if not root.left: + return root.val == 1 + + if root.val == 2: + return self.evaluateTree(root.left) or self.evaluateTree(root.right) + + if root.val == 3: + return self.evaluateTree(root.left) and self.evaluateTree(root.right) +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public boolean evaluateTree(TreeNode root) { + if (root.left == null) { + return root.val == 1; + } + + if (root.val == 2) { + return evaluateTree(root.left) || evaluateTree(root.right); + } + + if (root.val == 3) { + return evaluateTree(root.left) && evaluateTree(root.right); + } + + return false; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + bool evaluateTree(TreeNode* root) { + if (!root->left) { + return root->val == 1; + } + + if (root->val == 2) { + return evaluateTree(root->left) || evaluateTree(root->right); + } + + if (root->val == 3) { + return evaluateTree(root->left) && evaluateTree(root->right); + } + + return false; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {boolean} + */ + evaluateTree(root) { + if (!root.left) { + return root.val === 1; + } + + if (root.val === 2) { + return this.evaluateTree(root.left) || this.evaluateTree(root.right); + } + + if (root.val === 3) { + return this.evaluateTree(root.left) && this.evaluateTree(root.right); + } + + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Iterative DFS + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def evaluateTree(self, root: Optional[TreeNode]) -> bool: + stack = [root] + value = {} # map (node -> value) + + while stack: + node = stack.pop() + + if not node.left: + value[node] = node.val == 1 + elif node.left in value: + if node.val == 2: + value[node] = value[node.left] or value[node.right] + if node.val == 3: + value[node] = value[node.left] and value[node.right] + else: + stack.extend([node, node.left, node.right]) + + return value[root] +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public boolean evaluateTree(TreeNode root) { + Stack stack = new Stack<>(); + Map value = new HashMap<>(); + stack.push(root); + + while (!stack.isEmpty()) { + TreeNode node = stack.pop(); + + if (node.left == null) { + value.put(node, node.val == 1); + } else if (value.containsKey(node.left)) { + boolean leftValue = value.get(node.left); + boolean rightValue = value.get(node.right); + + if (node.val == 2) { + value.put(node, leftValue || rightValue); + } else if (node.val == 3) { + value.put(node, leftValue && rightValue); + } + } else { + stack.push(node); + stack.push(node.right); + stack.push(node.left); + } + } + + return value.get(root); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + bool evaluateTree(TreeNode* root) { + stack stk; + unordered_map value; + stk.push(root); + + while (!stk.empty()) { + TreeNode* node = stk.top(); + stk.pop(); + + if (!node->left) { + value[node] = node->val == 1; + } else if (value.count(node->left)) { + bool leftValue = value[node->left]; + bool rightValue = value[node->right]; + + if (node->val == 2) { + value[node] = leftValue || rightValue; + } else if (node->val == 3) { + value[node] = leftValue && rightValue; + } + } else { + stk.push(node); + stk.push(node->right); + stk.push(node->left); + } + } + + return value[root]; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {boolean} + */ + evaluateTree(root) { + const stack = [root]; + const value = new Map(); + + while (stack.length) { + const node = stack.pop(); + + if (!node.left) { + value.set(node, node.val === 1); + } else if (value.has(node.left)) { + const leftValue = value.get(node.left); + const rightValue = value.get(node.right); + + if (node.val === 2) { + value.set(node, leftValue || rightValue); + } else if (node.val === 3) { + value.set(node, leftValue && rightValue); + } + } else { + stack.push(node, node.right, node.left); + } + } + + return value.get(root); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/find-duplicate-subtrees.md b/articles/find-duplicate-subtrees.md new file mode 100644 index 000000000..bf3d6d7e9 --- /dev/null +++ b/articles/find-duplicate-subtrees.md @@ -0,0 +1,550 @@ +## 1. Brute Force (DFS) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def findDuplicateSubtrees(self, root: Optional[TreeNode]) -> List[Optional[TreeNode]]: + def same(node1, node2): + if not node1 and not node2: + return True + if not node1 or not node2: + return False + return (node1.val == node2.val and + same(node1.left, node2.left) and + same(node1.right, node2.right)) + + subTree = [] + def dfs(root): + if not root: + return + subTree.append(root) + dfs(root.left) + dfs(root.right) + + dfs(root) + res = [] + seen = set() + + for i in range(len(subTree)): + if subTree[i] in seen: + continue + for j in range(i + 1, len(subTree)): + if subTree[j] in seen: + continue + + if same(subTree[i], subTree[j]): + if subTree[i] not in seen: + res.append(subTree[i]) + seen.add(subTree[i]) + seen.add(subTree[j]) + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public List findDuplicateSubtrees(TreeNode root) { + List res = new ArrayList<>(); + Set seen = new HashSet<>(); + List subTree = new ArrayList<>(); + dfs(root, subTree); + + for (int i = 0; i < subTree.size(); i++) { + if (seen.contains(subTree.get(i))) continue; + for (int j = i + 1; j < subTree.size(); j++) { + if (seen.contains(subTree.get(j))) continue; + + if (same(subTree.get(i), subTree.get(j))) { + if (!seen.contains(subTree.get(i))) { + res.add(subTree.get(i)); + seen.add(subTree.get(i)); + } + seen.add(subTree.get(j)); + } + } + } + return res; + } + + private boolean same(TreeNode node1, TreeNode node2) { + if (node1 == null && node2 == null) return true; + if (node1 == null || node2 == null) return false; + return node1.val == node2.val && + same(node1.left, node2.left) && + same(node1.right, node2.right); + } + + private void dfs(TreeNode root, List subTree) { + if (root == null) return; + subTree.add(root); + dfs(root.left, subTree); + dfs(root.right, subTree); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + vector findDuplicateSubtrees(TreeNode* root) { + vector res; + unordered_set seen; + vector subTree; + dfs(root, subTree); + + for (int i = 0; i < subTree.size(); i++) { + if (seen.count(subTree[i])) continue; + for (int j = i + 1; j < subTree.size(); j++) { + if (seen.count(subTree[j])) continue; + + if (same(subTree[i], subTree[j])) { + if (!seen.count(subTree[i])) { + res.push_back(subTree[i]); + seen.insert(subTree[i]); + } + seen.insert(subTree[j]); + } + } + } + return res; + } + +private: + bool same(TreeNode* node1, TreeNode* node2) { + if (!node1 && !node2) return true; + if (!node1 || !node2) return false; + return node1->val == node2->val && + same(node1->left, node2->left) && + same(node1->right, node2->right); + } + + void dfs(TreeNode* root, vector& subTree) { + if (!root) return; + subTree.push_back(root); + dfs(root->left, subTree); + dfs(root->right, subTree); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {TreeNode[]} + */ + findDuplicateSubtrees(root) { + const res = []; + const seen = new Set(); + + const subTree = []; + const dfs = (root) => { + if (!root) return; + subTree.push(root); + dfs(root.left); + dfs(root.right); + }; + dfs(root); + + const same = (node1, node2) => { + if (!node1 && !node2) return true; + if (!node1 || !node2) return false; + return node1.val === node2.val && + same(node1.left, node2.left) && + same(node1.right, node2.right); + }; + + + for (let i = 0; i < subTree.length; i++) { + if (seen.has(subTree[i])) continue; + for (let j = i + 1; j < subTree.length; j++) { + if (seen.has(subTree[j])) continue; + + if (same(subTree[i], subTree[j])) { + if (!seen.has(subTree[i])) { + res.push(subTree[i]); + seen.add(subTree[i]); + } + seen.add(subTree[j]); + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 3)$ +* Space complexity: $O(n)$ + +--- + +## 2. DFS + Serialization + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def findDuplicateSubtrees(self, root: Optional[TreeNode]) -> List[Optional[TreeNode]]: + subtrees = defaultdict(list) + res = [] + + def dfs(node): + if not node: + return "null" + s = ",".join([str(node.val), dfs(node.left), dfs(node.right)]) + if len(subtrees[s]) == 1: + res.append(node) + subtrees[s].append(node) + return s + + dfs(root) + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + private Map> subtrees; + private List res; + + public List findDuplicateSubtrees(TreeNode root) { + subtrees = new HashMap<>(); + res = new ArrayList<>(); + dfs(root); + return res; + } + + private String dfs(TreeNode node) { + if (node == null) return "null"; + String s = node.val + "," + dfs(node.left) + "," + dfs(node.right); + subtrees.putIfAbsent(s, new ArrayList<>()); + if (subtrees.get(s).size() == 1) { + res.add(node); + } + subtrees.get(s).add(node); + return s; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { + unordered_map> subtrees; + vector res; + +public: + vector findDuplicateSubtrees(TreeNode* root) { + dfs(root); + return res; + } + +private: + string dfs(TreeNode* node) { + if (!node) return "null"; + string s = to_string(node->val) + "," + dfs(node->left) + "," + dfs(node->right); + if (subtrees[s].size() == 1) { + res.push_back(node); + } + subtrees[s].push_back(node); + return s; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {TreeNode[]} + */ + findDuplicateSubtrees(root) { + const subtrees = new Map(); + const res = []; + + const dfs = (node) => { + if (!node) return "null"; + const s = `${node.val},${dfs(node.left)},${dfs(node.right)}`; + if (!subtrees.has(s)) { + subtrees.set(s, []); + } + if (subtrees.get(s).length === 1) { + res.push(node); + } + subtrees.get(s).push(node); + return s; + }; + + dfs(root); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 3. Depth First Search (Optimal) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def findDuplicateSubtrees(self, root: Optional[TreeNode]) -> List[Optional[TreeNode]]: + id_map = {} + count = defaultdict(int) + res = [] + + def dfs(node): + if not node: + return -1 + cur = (dfs(node.left), node.val, dfs(node.right)) + if cur not in id_map: + id_map[cur] = len(id_map) + 1 + + curId = id_map[cur] + if count[curId] == 1: + res.append(node) + count[curId] += 1 + return curId + + dfs(root) + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + private Map idMap; + private Map count; + private List res; + + public List findDuplicateSubtrees(TreeNode root) { + idMap = new HashMap<>(); + count = new HashMap<>(); + res = new ArrayList<>(); + + dfs(root); + return res; + } + + private int dfs(TreeNode node) { + if (node == null) return -1; + String cur = dfs(node.left) + "," + node.val + "," + dfs(node.right); + idMap.putIfAbsent(cur, idMap.size()); + int curId = idMap.get(cur); + count.put(curId, count.getOrDefault(curId, 0) + 1); + if (count.get(curId) == 2) { + res.add(node); + } + return curId; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { + unordered_map idMap; + unordered_map count; + vector res; + +public: + vector findDuplicateSubtrees(TreeNode* root) { + dfs(root); + return res; + } + +private: + int dfs(TreeNode* node) { + if (!node) return -1; + string cur = to_string(dfs(node->left)) + "," + + to_string(node->val) + "," + + to_string(dfs(node->right)); + if (idMap.find(cur) == idMap.end()) { + idMap[cur] = idMap.size(); + } + int curId = idMap[cur]; + count[curId]++; + if (count[curId] == 2) { + res.push_back(node); + } + return curId; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {TreeNode[]} + */ + findDuplicateSubtrees(root) { + const idMap = new Map(); + const count = new Map(); + const res = []; + + const dfs = (node) => { + if (!node) return -1; + + const cur = `${dfs(node.left)},${node.val},${dfs(node.right)}`; + if (!idMap.has(cur)) { + idMap.set(cur, idMap.size + 1); + } + + const curId = idMap.get(cur); + count.set(curId, (count.get(curId) || 0) + 1); + if (count.get(curId) === 2) { + res.push(node); + } + + return curId; + }; + + dfs(root); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/invert-a-binary-tree.md b/articles/invert-a-binary-tree.md index ed6a548cc..2c7622067 100644 --- a/articles/invert-a-binary-tree.md +++ b/articles/invert-a-binary-tree.md @@ -387,7 +387,7 @@ public class Solution { --- -## 3. Depth First Search (Stack) +## 3. Iterative DFS ::tabs-start diff --git a/articles/leaf-similar-trees.md b/articles/leaf-similar-trees.md new file mode 100644 index 000000000..5e92ef739 --- /dev/null +++ b/articles/leaf-similar-trees.md @@ -0,0 +1,504 @@ +## 1. Depth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def leafSimilar(self, root1: Optional[TreeNode], root2: Optional[TreeNode]) -> bool: + def dfs(root, leaf): + if not root: + return + if not root.left and not root.right: + leaf.append(root.val) + return + dfs(root.left, leaf) + dfs(root.right, leaf) + + leaf1, leaf2 = [], [] + dfs(root1, leaf1) + dfs(root2, leaf2) + return leaf1 == leaf2 +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public boolean leafSimilar(TreeNode root1, TreeNode root2) { + List leaf1 = new ArrayList<>(); + List leaf2 = new ArrayList<>(); + + dfs(root1, leaf1); + dfs(root2, leaf2); + + return leaf1.equals(leaf2); + } + + private void dfs(TreeNode root, List leaf) { + if (root == null) return; + if (root.left == null && root.right == null) { + leaf.add(root.val); + return; + } + dfs(root.left, leaf); + dfs(root.right, leaf); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + bool leafSimilar(TreeNode* root1, TreeNode* root2) { + vector leaf1, leaf2; + dfs(root1, leaf1); + dfs(root2, leaf2); + return leaf1 == leaf2; + } + +private: + void dfs(TreeNode* root, vector& leaf) { + if (!root) return; + if (!root->left && !root->right) { + leaf.push_back(root->val); + return; + } + dfs(root->left, leaf); + dfs(root->right, leaf); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root1 + * @param {TreeNode} root2 + * @return {boolean} + */ + leafSimilar(root1, root2) { + const dfs = (root, leaf) => { + if (!root) return; + if (!root.left && !root.right) { + leaf.push(root.val); + return; + } + dfs(root.left, leaf); + dfs(root.right, leaf); + }; + + const leaf1 = []; + const leaf2 = []; + dfs(root1, leaf1); + dfs(root2, leaf2); + + return JSON.stringify(leaf1) === JSON.stringify(leaf2); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(n + m)$ + +> Where $n$ and $m$ are the number of nodes in the given trees. + +--- + +## 2. Depth First Search (Space Optimized) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def leafSimilar(self, root1: Optional[TreeNode], root2: Optional[TreeNode]) -> bool: + def dfs(root, leaf): + if not root: + return + if not root.left and not root.right: + leaf.append(root.val) + return + dfs(root.left, leaf) + dfs(root.right, leaf) + + leaf1 = [] + dfs(root1, leaf1) + + def dfs1(root, leaf): + if not root: + return True + if not root.left and not root.right: + if not leaf: + return False + return leaf.pop() == root.val + return dfs1(root.right, leaf) and dfs1(root.left, leaf) + + return dfs1(root2, leaf1) and not leaf1 +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public boolean leafSimilar(TreeNode root1, TreeNode root2) { + Stack leaf1 = new Stack<>(); + dfs(root1, leaf1); + return dfs1(root2, leaf1) && leaf1.isEmpty(); + } + + private void dfs(TreeNode root, Stack leaf) { + if (root == null) return; + if (root.left == null && root.right == null) { + leaf.push(root.val); + return; + } + dfs(root.left, leaf); + dfs(root.right, leaf); + } + + private boolean dfs1(TreeNode root, Stack leaf) { + if (root == null) return true; + if (root.left == null && root.right == null) { + if (leaf.isEmpty()) return false; + return leaf.pop() == root.val; + } + return dfs1(root.right, leaf) && dfs1(root.left, leaf); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + bool leafSimilar(TreeNode* root1, TreeNode* root2) { + vector leaf1; + dfs(root1, leaf1); + return dfs1(root2, leaf1) && leaf1.empty(); + } + +private: + void dfs(TreeNode* root, vector& leaf) { + if (!root) return; + if (!root->left && !root->right) { + leaf.push_back(root->val); + return; + } + dfs(root->left, leaf); + dfs(root->right, leaf); + } + + bool dfs1(TreeNode* root, vector& leaf) { + if (!root) return true; + if (!root->left && !root->right) { + if (leaf.empty() || leaf.back() != root->val) { + return false; + } + leaf.pop_back(); + return true; + } + return dfs1(root->right, leaf) && dfs1(root->left, leaf); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root1 + * @param {TreeNode} root2 + * @return {boolean} + */ + leafSimilar(root1, root2) { + const dfs = (root, leaf) => { + if (!root) return; + if (!root.left && !root.right) { + leaf.push(root.val); + return; + } + dfs(root.left, leaf); + dfs(root.right, leaf); + }; + + const dfs1 = (root, leaf) => { + if (!root) return true; + if (!root.left && !root.right) { + if (!leaf.length) return false; + return leaf.pop() === root.val; + } + return dfs1(root.right, leaf) && dfs1(root.left, leaf); + }; + + const leaf1 = []; + dfs(root1, leaf1); + return dfs1(root2, leaf1) && leaf1.length === 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(n + m)$ + +> Where $n$ and $m$ are the number of nodes in the given trees. + +--- + +## 3. Iterative DFS + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def leafSimilar(self, root1: Optional[TreeNode], root2: Optional[TreeNode]) -> bool: + def getPathLeaf(stack): + while stack: + node = stack.pop() + if node.right: + stack.append(node.right) + if node.left: + stack.append(node.left) + if not node.left and not node.right: + return node.val + + stack1, stack2 = [root1], [root2] + while stack1 and stack2: + if getPathLeaf(stack1) != getPathLeaf(stack2): + return False + + return not stack1 and not stack2 +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public boolean leafSimilar(TreeNode root1, TreeNode root2) { + Stack stack1 = new Stack<>(); + Stack stack2 = new Stack<>(); + stack1.push(root1); + stack2.push(root2); + + while (!stack1.isEmpty() && !stack2.isEmpty()) { + if (getPathLeaf(stack1) != getPathLeaf(stack2)) { + return false; + } + } + return stack1.isEmpty() && stack2.isEmpty(); + } + + private int getPathLeaf(Stack stack) { + while (!stack.isEmpty()) { + TreeNode node = stack.pop(); + if (node.right != null) { + stack.push(node.right); + } + if (node.left != null) { + stack.push(node.left); + } + if (node.left == null && node.right == null) { + return node.val; + } + } + return -1; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + bool leafSimilar(TreeNode* root1, TreeNode* root2) { + stack stack1, stack2; + stack1.push(root1); + stack2.push(root2); + + while (!stack1.empty() && !stack2.empty()) { + if (getPathLeaf(stack1) != getPathLeaf(stack2)) { + return false; + } + } + return stack1.empty() && stack2.empty(); + } + +private: + int getPathLeaf(stack& stack) { + while (!stack.empty()) { + TreeNode* node = stack.top(); + stack.pop(); + if (node->right) { + stack.push(node->right); + } + if (node->left) { + stack.push(node->left); + } + if (!node->left && !node->right) { + return node->val; + } + } + return -1; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root1 + * @param {TreeNode} root2 + * @return {boolean} + */ + leafSimilar(root1, root2) { + const getPathLeaf = (stack) => { + while (stack.length) { + const node = stack.pop(); + if (node.right) stack.push(node.right); + if (node.left) stack.push(node.left); + if (!node.left && !node.right) return node.val; + } + }; + + const stack1 = [root1], stack2 = [root2]; + while (stack1.length && stack2.length) { + if (getPathLeaf(stack1) !== getPathLeaf(stack2)) { + return false; + } + } + return stack1.length === 0 && stack2.length === 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(n + m)$ + +> Where $n$ and $m$ are the number of nodes in the given trees. \ No newline at end of file diff --git a/articles/maximum-width-of-binary-tree.md b/articles/maximum-width-of-binary-tree.md new file mode 100644 index 000000000..ba741fb5d --- /dev/null +++ b/articles/maximum-width-of-binary-tree.md @@ -0,0 +1,513 @@ +## 1. Breadth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def widthOfBinaryTree(self, root: Optional[TreeNode]) -> int: + res = 0 + q = deque([(root, 1, 0)]) # [node, num, level] + prevLevel, prevNum = 0, 1 + + while q: + node, num, level = q.popleft() + + if level > prevLevel: + prevLevel = level + prevNum = num + + res = max(res, num - prevNum + 1) + if node.left: + q.append((node.left, 2 * num, level + 1)) + if node.right: + q.append((node.right, 2 * num + 1, level + 1)) + + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public int widthOfBinaryTree(TreeNode root) { + if (root == null) return 0; + + int res = 0; + Queue queue = new LinkedList<>(); + queue.offer(new Tuple(root, 1, 0)); // [node, num, level] + int prevLevel = 0, prevNum = 1; + + while (!queue.isEmpty()) { + Tuple current = queue.poll(); + TreeNode node = current.node; + int num = current.num; + int level = current.level; + + if (level > prevLevel) { + prevLevel = level; + prevNum = num; + } + + res = Math.max(res, num - prevNum + 1); + if (node.left != null) { + queue.offer(new Tuple(node.left, 2 * num, level + 1)); + } + if (node.right != null) { + queue.offer(new Tuple(node.right, 2 * num + 1, level + 1)); + } + } + + return res; + } + + class Tuple { + TreeNode node; + int num, level; + + Tuple(TreeNode node, int num, int level) { + this.node = node; + this.num = num; + this.level = level; + } + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + int widthOfBinaryTree(TreeNode* root) { + if (!root) return 0; + + int res = 0; + queue> q; // [node, num, level] + q.push({root, 1, 0}); + int prevLevel = 0, prevNum = 1; + + while (!q.empty()) { + auto [node, num, level] = q.front(); + q.pop(); + + if (level > prevLevel) { + prevLevel = level; + prevNum = num; + } + + res = max(res, int(num - prevNum) + 1); + if (node->left) { + q.push({node->left, 2 * num, level + 1}); + } + if (node->right) { + q.push({node->right, 2 * num + 1, level + 1}); + } + } + + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + widthOfBinaryTree(root) { + if (!root) return 0; + + let res = 0n; + const queue = new Queue([[root, 1n, 0]]); // [node, num, level] + let prevLevel = 0, prevNum = 1n; + + while (!queue.isEmpty()) { + const [node, num, level] = queue.pop(); + + if (level > prevLevel) { + prevLevel = level; + prevNum = num; + } + + res = res > (num - prevNum + 1n) ? res : (num - prevNum + 1n); + if (node.left) { + queue.push([node.left, 2n * num, level + 1]); + } + if (node.right) { + queue.push([node.right, 2n * num + 1n, level + 1]); + } + } + + return Number(res); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Breadth First Search (Optimal) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def widthOfBinaryTree(self, root: Optional[TreeNode]) -> int: + res = 0 + q = deque([(root, 0)]) + + while q: + start = q[0][1] + for _ in range(len(q)): + node, num = q.popleft() + curNum = num - start + res = max(res, curNum + 1) + if node.left: + q.append((node.left, 2 * curNum)) + if node.right: + q.append((node.right, 2 * curNum + 1)) + + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public int widthOfBinaryTree(TreeNode root) { + int res = 0; + Queue> q = new LinkedList<>(); + q.offer(new Pair<>(root, 0)); + + while (!q.isEmpty()) { + int start = q.peek().getValue(); + for (int i = q.size(); i > 0; i--) { + Pair pair = q.poll(); + TreeNode node = pair.getKey(); + int num = pair.getValue() - start; + + res = Math.max(res, num + 1); + if (node.left != null) { + q.offer(new Pair<>(node.left, 2 * num)); + } + if (node.right != null) { + q.offer(new Pair<>(node.right, 2 * num + 1)); + } + } + } + + return res; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + int widthOfBinaryTree(TreeNode* root) { + int res = 0; + queue> q; + q.push({root, 0}); + + while (!q.empty()) { + int start = q.front().second; + + for (int i = q.size(); i > 0; --i) { + auto [node, num] = q.front(); + q.pop(); + uint curNum = num - start; + res = max(res, int(curNum) + 1); + if (node->left) { + q.push({node->left, 2 * curNum}); + } + if (node->right) { + q.push({node->right, 2 * curNum + 1}); + } + } + } + + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + widthOfBinaryTree(root) { + let res = 0; + const q = new Queue([[root, 0]]); + + while (!q.isEmpty()) { + const start = q.front()[1]; + + for (let i = q.size(); i > 0; i--) { + const [node, num] = q.pop(); + const curNum = num - start; + res = Math.max(res, curNum + 1); + if (node.left) { + q.push([node.left, 2 * curNum]); + } + if (node.right) { + q.push([node.right, 2 * curNum + 1]); + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Depth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def widthOfBinaryTree(self, root: Optional[TreeNode]) -> int: + first = {} + res = 0 + + def dfs(node, level, num): + nonlocal res + if not node: + return + + if level not in first: + first[level] = num + + res = max(res, num - first[level] + 1) + dfs(node.left, level + 1, 2 * num) + dfs(node.right, level + 1, 2 * num + 1) + + dfs(root, 0, 0) + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + private Map first; + public int widthOfBinaryTree(TreeNode root) { + first = new HashMap<>(); + int[] res = new int[1]; + + dfs(root, 0, 0, res); + return res[0]; + } + + private void dfs(TreeNode node, int level, int num, int[] res) { + if (node == null) { + return; + } + + first.putIfAbsent(level, num); + res[0] = Math.max(res[0], num - first.get(level) + 1); + dfs(node.left, level + 1, 2 * num, res); + dfs(node.right, level + 1, 2 * num + 1, res); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { + unordered_map first; + +public: + int widthOfBinaryTree(TreeNode* root) { + unsigned long long res = 0; + + dfs(root, 0, 0, res); + return int(res); + } + +private: + void dfs(TreeNode* node, int level, unsigned long long num, unsigned long long& res) { + if (!node) { + return; + } + + if (!first.count(level)) { + first[level] = num; + } + + res = max(res, num - first[level] + 1); + dfs(node->left, level + 1, 2 * (num - first[level]), res); + dfs(node->right, level + 1, 2 * (num - first[level]) + 1, res); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + widthOfBinaryTree(root) { + const first = new Map(); + let res = 0; + + const dfs = (node, level, curNum) => { + if (!node) return; + + if (!first.has(level)) { + first.set(level, curNum); + } + + res = Math.max(res, curNum - first.get(level) + 1); + dfs(node.left, level + 1, 2 * (curNum - first.get(level))); + dfs(node.right, level + 1, 2 * (curNum - first.get(level)) + 1); + }; + + dfs(root, 0, 0); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/merge-two-binary-trees.md b/articles/merge-two-binary-trees.md new file mode 100644 index 000000000..0b7636434 --- /dev/null +++ b/articles/merge-two-binary-trees.md @@ -0,0 +1,658 @@ +## 1. Depth First Search (Creating New Tree) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def mergeTrees(self, root1: Optional[TreeNode], root2: Optional[TreeNode]) -> Optional[TreeNode]: + if not root1 and not root2: + return None + + v1 = root1.val if root1 else 0 + v2 = root2.val if root2 else 0 + root = TreeNode(v1 + v2) + + root.left = self.mergeTrees(root1.left if root1 else None, root2.left if root2 else None) + root.right = self.mergeTrees(root1.right if root1 else None, root2.right if root2 else None) + + return root +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public TreeNode mergeTrees(TreeNode root1, TreeNode root2) { + if (root1 == null && root2 == null) { + return null; + } + + int v1 = (root1 != null) ? root1.val : 0; + int v2 = (root2 != null) ? root2.val : 0; + TreeNode root = new TreeNode(v1 + v2); + + root.left = mergeTrees( + (root1 != null) ? root1.left : null, (root2 != null) ? root2.left : null + ); + root.right = mergeTrees( + (root1 != null) ? root1.right : null, (root2 != null) ? root2.right : null + ); + + return root; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) { + if (!root1 && !root2) { + return nullptr; + } + + int v1 = root1 ? root1->val : 0; + int v2 = root2 ? root2->val : 0; + TreeNode* root = new TreeNode(v1 + v2); + + root->left = mergeTrees(root1 ? root1->left : nullptr, root2 ? root2->left : nullptr); + root->right = mergeTrees(root1 ? root1->right : nullptr, root2 ? root2->right : nullptr); + + return root; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root1 + * @param {TreeNode} root2 + * @return {TreeNode} + */ + mergeTrees(root1, root2) { + if (!root1 && !root2) { + return null; + } + + const v1 = root1 ? root1.val : 0; + const v2 = root2 ? root2.val : 0; + const root = new TreeNode(v1 + v2); + + root.left = this.mergeTrees(root1 ? root1.left : null, root2 ? root2.left : null); + root.right = this.mergeTrees(root1 ? root1.right : null, root2 ? root2.right : null); + + return root; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m + n)$ +* Space complexity: + * $O(m + n)$ space for recursion stack. + * $O(m + n)$ space for the output. + +> Where $m$ and $n$ are the number of nodes in the given trees. + +--- + +## 2. Depth First Search (In Place) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def mergeTrees(self, root1: Optional[TreeNode], root2: Optional[TreeNode]) -> Optional[TreeNode]: + if not root1: + return root2 + if not root2: + return root1 + + root1.val += root2.val + root1.left = self.mergeTrees(root1.left, root2.left) + root1.right = self.mergeTrees(root1.right, root2.right) + return root1 +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public TreeNode mergeTrees(TreeNode root1, TreeNode root2) { + if (root1 == null) return root2; + if (root2 == null) return root1; + + root1.val += root2.val; + root1.left = mergeTrees(root1.left, root2.left); + root1.right = mergeTrees(root1.right, root2.right); + return root1; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) { + if (!root1) return root2; + if (!root2) return root1; + + root1->val += root2->val; + root1->left = mergeTrees(root1->left, root2->left); + root1->right = mergeTrees(root1->right, root2->right); + return root1; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root1 + * @param {TreeNode} root2 + * @return {TreeNode} + */ + mergeTrees(root1, root2) { + if (!root1) return root2; + if (!root2) return root1; + + root1.val += root2.val; + root1.left = this.mergeTrees(root1.left, root2.left); + root1.right = this.mergeTrees(root1.right, root2.right); + return root1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(min(m, n))$ +* Space complexity: $O(min(m, n))$ for recursion stack. + +> Where $m$ and $n$ are the number of nodes in the given trees. + +--- + +## 3. Iterative DFS (Creating New Tree) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def mergeTrees(self, root1: Optional[TreeNode], root2: Optional[TreeNode]) -> Optional[TreeNode]: + if not root1: + return root2 + if not root2: + return root1 + + root = TreeNode(root1.val + root2.val) + stack = [(root1, root2, root)] + + while stack: + node1, node2, node = stack.pop() + + if node1.left and node2.left: + node.left = TreeNode(node1.left.val + node2.left.val) + stack.append((node1.left, node2.left, node.left)) + elif not node1.left: + node.left = node2.left + else: + node.left = node1.left + + if node1.right and node2.right: + node.right = TreeNode(node1.right.val + node2.right.val) + stack.append((node1.right, node2.right, node.right)) + elif not node1.right: + node.right = node2.right + else: + node.right = node1.right + + return root +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public TreeNode mergeTrees(TreeNode root1, TreeNode root2) { + if (root1 == null && root2 == null) return null; + + int val = (root1 != null ? root1.val : 0) + (root2 != null ? root2.val : 0); + TreeNode root = new TreeNode(val); + Stack stack = new Stack<>(); + stack.push(new TreeNode[]{root1, root2, root}); + + while (!stack.isEmpty()) { + TreeNode[] nodes = stack.pop(); + TreeNode node1 = nodes[0], node2 = nodes[1], node = nodes[2]; + + TreeNode left1 = node1 != null ? node1.left : null; + TreeNode left2 = node2 != null ? node2.left : null; + if (left1 != null || left2 != null) { + int leftVal = (left1 != null ? left1.val : 0) + (left2 != null ? left2.val : 0); + node.left = new TreeNode(leftVal); + stack.push(new TreeNode[]{left1, left2, node.left}); + } + + TreeNode right1 = node1 != null ? node1.right : null; + TreeNode right2 = node2 != null ? node2.right : null; + if (right1 != null || right2 != null) { + int rightVal = (right1 != null ? right1.val : 0) + (right2 != null ? right2.val : 0); + node.right = new TreeNode(rightVal); + stack.push(new TreeNode[]{right1, right2, node.right}); + } + } + + return root; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) { + if (!root1 && !root2) return nullptr; + + int val = (root1 ? root1->val : 0) + (root2 ? root2->val : 0); + TreeNode* root = new TreeNode(val); + stack> st; + st.push({root1, root2, root}); + + while (!st.empty()) { + auto [node1, node2, node] = st.top(); + st.pop(); + + TreeNode* left1 = node1 ? node1->left : nullptr; + TreeNode* left2 = node2 ? node2->left : nullptr; + if (left1 || left2) { + int leftVal = (left1 ? left1->val : 0) + (left2 ? left2->val : 0); + node->left = new TreeNode(leftVal); + st.push({left1, left2, node->left}); + } + + TreeNode* right1 = node1 ? node1->right : nullptr; + TreeNode* right2 = node2 ? node2->right : nullptr; + if (right1 || right2) { + int rightVal = (right1 ? right1->val : 0) + (right2 ? right2->val : 0); + node->right = new TreeNode(rightVal); + st.push({right1, right2, node->right}); + } + } + + return root; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root1 + * @param {TreeNode} root2 + * @return {TreeNode} + */ + mergeTrees(root1, root2) { + if (!root1 && !root2) return null; + + let val = (root1 ? root1.val : 0) + (root2 ? root2.val : 0); + let root = new TreeNode(val); + let stack = [[root1, root2, root]]; + + while (stack.length) { + let [node1, node2, node] = stack.pop(); + + let left1 = node1 ? node1.left : null; + let left2 = node2 ? node2.left : null; + if (left1 || left2) { + let leftVal = (left1 ? left1.val : 0) + (left2 ? left2.val : 0); + node.left = new TreeNode(leftVal); + stack.push([left1, left2, node.left]); + } + + let right1 = node1 ? node1.right : null; + let right2 = node2 ? node2.right : null; + if (right1 || right2) { + let rightVal = (right1 ? right1.val : 0) + (right2 ? right2.val : 0); + node.right = new TreeNode(rightVal); + stack.push([right1, right2, node.right]); + } + } + + return root; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m + n)$ +* Space complexity: + * $O(m + n)$ space for the stack. + * $O(m + n)$ space for the output. + +> Where $m$ and $n$ are the number of nodes in the given trees. + +--- + +## 4. Iterative DFS (In Place) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def mergeTrees(self, root1: Optional[TreeNode], root2: Optional[TreeNode]) -> Optional[TreeNode]: + if not root1: + return root2 + if not root2: + return root1 + + stack = [(root1, root2)] + + while stack: + node1, node2 = stack.pop() + if not node1 or not node2: + continue + + node1.val += node2.val + + if node1.left and node2.left: + stack.append((node1.left, node2.left)) + elif not node1.left: + node1.left = node2.left + + if node1.right and node2.right: + stack.append((node1.right, node2.right)) + elif not node1.right: + node1.right = node2.right + + return root1 +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public TreeNode mergeTrees(TreeNode root1, TreeNode root2) { + if (root1 == null) return root2; + if (root2 == null) return root1; + + Stack stack = new Stack<>(); + stack.push(new TreeNode[] { root1, root2 }); + + while (!stack.isEmpty()) { + TreeNode[] nodes = stack.pop(); + TreeNode node1 = nodes[0]; + TreeNode node2 = nodes[1]; + + if (node2 == null) continue; + + node1.val += node2.val; + + if (node1.left == null) { + node1.left = node2.left; + } else { + stack.push(new TreeNode[] { node1.left, node2.left }); + } + + if (node1.right == null) { + node1.right = node2.right; + } else { + stack.push(new TreeNode[] { node1.right, node2.right }); + } + } + + return root1; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) { + if (!root1) return root2; + if (!root2) return root1; + + stack> stk; + stk.push({root1, root2}); + + while (!stk.empty()) { + auto [node1, node2] = stk.top(); + stk.pop(); + + if (!node2) continue; + + node1->val += node2->val; + + if (!node1->left) { + node1->left = node2->left; + } else { + stk.push({node1->left, node2->left}); + } + + if (!node1->right) { + node1->right = node2->right; + } else { + stk.push({node1->right, node2->right}); + } + } + + return root1; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root1 + * @param {TreeNode} root2 + * @return {TreeNode} + */ + mergeTrees(root1, root2) { + if (!root1) return root2; + if (!root2) return root1; + + let stack = [[root1, root2]]; + + while (stack.length) { + let [node1, node2] = stack.pop(); + if (!node1 || !node2) continue; + + node1.val += node2.val; + + if (node1.left && node2.left) { + stack.push([node1.left, node2.left]); + } else if (!node1.left) { + node1.left = node2.left; + } + + if (node1.right && node2.right) { + stack.push([node1.right, node2.right]); + } else if (!node1.right) { + node1.right = node2.right; + } + } + + return root1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(min(m, n))$ +* Space complexity: $O(min(m, n))$ for the stack. + +> Where $m$ and $n$ are the number of nodes in the given trees. \ No newline at end of file diff --git a/articles/minimum-distance-between-bst-nodes.md b/articles/minimum-distance-between-bst-nodes.md new file mode 100644 index 000000000..d8cd6bda0 --- /dev/null +++ b/articles/minimum-distance-between-bst-nodes.md @@ -0,0 +1,840 @@ +## 1. Brute Force (DFS) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def minDiffInBST(self, root: Optional[TreeNode]) -> int: + def dfs(node): + if not node: + return float("inf") + res = dfs1(root, node) + res = min(res, dfs(node.left)) + res = min(res, dfs(node.right)) + return res + + def dfs1(root, node): + if not root: + return float("inf") + + res = float("inf") + if root != node: + res = abs(root.val - node.val) + res = min(res, dfs1(root.left, node)) + res = min(res, dfs1(root.right, node)) + return res + + return dfs(root) +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public int minDiffInBST(TreeNode root) { + return dfs(root, root); + } + + private int dfs(TreeNode root, TreeNode node) { + if (node == null) { + return Integer.MAX_VALUE; + } + int res = dfs1(root, node); + res = Math.min(res, dfs(root, node.left)); + res = Math.min(res, dfs(root, node.right)); + return res; + } + + private int dfs1(TreeNode root, TreeNode node) { + if (root == null) { + return Integer.MAX_VALUE; + } + int res = Integer.MAX_VALUE; + if (root != node) { + res = Math.abs(root.val - node.val); + } + res = Math.min(res, dfs1(root.left, node)); + res = Math.min(res, dfs1(root.right, node)); + return res; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + int minDiffInBST(TreeNode* root) { + return dfs(root, root); + } + +private: + int dfs(TreeNode* root, TreeNode* node) { + if (!node) { + return INT_MAX; + } + int res = dfs1(root, node); + res = min(res, dfs(root, node->left)); + res = min(res, dfs(root, node->right)); + return res; + } + + int dfs1(TreeNode* root, TreeNode* node) { + if (!root) { + return INT_MAX; + } + int res = INT_MAX; + if (root != node) { + res = abs(root->val - node->val); + } + res = min(res, dfs1(root->left, node)); + res = min(res, dfs1(root->right, node)); + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + minDiffInBST(root) { + const dfs = (node) => { + if (!node) { + return Infinity; + } + let res = dfs1(root, node); + res = Math.min(res, dfs(node.left)); + res = Math.min(res, dfs(node.right)); + return res; + }; + + const dfs1 = (root, node) => { + if (!root) { + return Infinity; + } + let res = Infinity; + if (root !== node) { + res = Math.abs(root.val - node.val); + } + res = Math.min(res, dfs1(root.left, node)); + res = Math.min(res, dfs1(root.right, node)); + return res; + }; + + return dfs(root); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 2. Inorder Traversal + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def minDiffInBST(self, root: Optional[TreeNode]) -> int: + arr = [] + + def dfs(node): + if not node: + return + dfs(node.left) + arr.append(node.val) + dfs(node.right) + + dfs(root) + res = arr[1] - arr[0] + for i in range(2, len(arr)): + res = min(res, arr[i] - arr[i - 1]) + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public int minDiffInBST(TreeNode root) { + List arr = new ArrayList<>(); + + dfs(root, arr); + int res = arr.get(1) - arr.get(0); + for (int i = 2; i < arr.size(); i++) { + res = Math.min(res, arr.get(i) - arr.get(i - 1)); + } + return res; + } + + private void dfs(TreeNode node, List arr) { + if (node == null) { + return; + } + dfs(node.left, arr); + arr.add(node.val); + dfs(node.right, arr); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + int minDiffInBST(TreeNode* root) { + vector arr; + dfs(root, arr); + + int res = arr[1] - arr[0]; + for (int i = 2; i < arr.size(); i++) { + res = min(res, arr[i] - arr[i - 1]); + } + return res; + } + +private: + void dfs(TreeNode* node, vector& arr) { + if (!node) return; + dfs(node->left, arr); + arr.push_back(node->val); + dfs(node->right, arr); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + minDiffInBST(root) { + const arr = []; + + const dfs = (node) => { + if (!node) return; + dfs(node.left); + arr.push(node.val); + dfs(node.right); + }; + + dfs(root); + let res = arr[1] - arr[0]; + for (let i = 2; i < arr.length; i++) { + res = Math.min(res, arr[i] - arr[i - 1]); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Inorder Traversal (Space Optimized) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def minDiffInBST(self, root: Optional[TreeNode]) -> int: + prev, res = None, float("inf") + + def dfs(node): + nonlocal prev, res + if not node: + return + + dfs(node.left) + if prev: + res = min(res, node.val - prev.val) + prev = node + dfs(node.right) + + dfs(root) + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + private TreeNode prev = null; + private int res = Integer.MAX_VALUE; + + public int minDiffInBST(TreeNode root) { + dfs(root); + return res; + } + + private void dfs(TreeNode node) { + if (node == null) return; + + dfs(node.left); + if (prev != null) { + res = Math.min(res, node.val - prev.val); + } + prev = node; + dfs(node.right); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + int minDiffInBST(TreeNode* root) { + TreeNode* prev = nullptr; + int res = INT_MAX; + + dfs(root, prev, res); + return res; + } + +private: + void dfs(TreeNode* node, TreeNode*& prev, int& res) { + if (!node) return; + + dfs(node->left, prev, res); + if (prev) { + res = min(res, node->val - prev->val); + } + prev = node; + dfs(node->right, prev, res); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + minDiffInBST(root) { + let prev = null; + let res = Infinity; + + const dfs = (node) => { + if (!node) return; + + dfs(node.left); + if (prev !== null) { + res = Math.min(res, node.val - prev.val); + } + prev = node; + dfs(node.right); + }; + + dfs(root); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 4. Iterative DFS (Inorder Traversal) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def minDiffInBST(self, root: Optional[TreeNode]) -> int: + stack, prev, res = [], None, float("inf") + cur = root + + while stack or cur: + while cur: + stack.append(cur) + cur = cur.left + + cur = stack.pop() + if prev: + res = min(res, cur.val - prev.val) + prev = cur + cur = cur.right + + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public int minDiffInBST(TreeNode root) { + Stack stack = new Stack<>(); + TreeNode prev = null; + int res = Integer.MAX_VALUE; + TreeNode cur = root; + + while (!stack.isEmpty() || cur != null) { + while (cur != null) { + stack.push(cur); + cur = cur.left; + } + + cur = stack.pop(); + if (prev != null) { + res = Math.min(res, cur.val - prev.val); + } + prev = cur; + cur = cur.right; + } + + return res; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + int minDiffInBST(TreeNode* root) { + stack st; + TreeNode* prev = nullptr; + TreeNode* cur = root; + int res = INT_MAX; + + while (!st.empty() || cur) { + while (cur) { + st.push(cur); + cur = cur->left; + } + + cur = st.top(); + st.pop(); + if (prev) { + res = min(res, cur->val - prev->val); + } + prev = cur; + cur = cur->right; + } + + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + minDiffInBST(root) { + let stack = []; + let prev = null; + let res = Infinity; + let cur = root; + + while (stack.length > 0 || cur !== null) { + while (cur !== null) { + stack.push(cur); + cur = cur.left; + } + + cur = stack.pop(); + if (prev !== null) { + res = Math.min(res, cur.val - prev.val); + } + prev = cur; + cur = cur.right; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 5. Morris Traversal + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def minDiffInBST(self, root: Optional[TreeNode]) -> int: + prevVal = res = float("inf") + cur = root + + while cur: + if not cur.left: + if prevVal != float("inf"): + res = min(res, cur.val - prevVal) + prevVal = cur.val + cur = cur.right + else: + prev = cur.left + while prev.right and prev.right != cur: + prev = prev.right + + if not prev.right: + prev.right = cur + cur = cur.left + else: + prev.right = None + if prevVal != float("inf"): + res = min(res, cur.val - prevVal) + prevVal = cur.val + cur = cur.right + + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public int minDiffInBST(TreeNode root) { + int prevVal = Integer.MAX_VALUE, res = Integer.MAX_VALUE; + TreeNode cur = root; + + while (cur != null) { + if (cur.left == null) { + if (prevVal != Integer.MAX_VALUE) { + res = Math.min(res, cur.val - prevVal); + } + prevVal = cur.val; + cur = cur.right; + } else { + TreeNode prev = cur.left; + while (prev.right != null && prev.right != cur) { + prev = prev.right; + } + + if (prev.right == null) { + prev.right = cur; + cur = cur.left; + } else { + prev.right = null; + if (prevVal != Integer.MAX_VALUE) { + res = Math.min(res, cur.val - prevVal); + } + prevVal = cur.val; + cur = cur.right; + } + } + } + + return res; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + int minDiffInBST(TreeNode* root) { + int prevVal = INT_MAX, res = INT_MAX; + TreeNode* cur = root; + + while (cur) { + if (!cur->left) { + if (prevVal != INT_MAX) { + res = min(res, cur->val - prevVal); + } + prevVal = cur->val; + cur = cur->right; + } else { + TreeNode* prev = cur->left; + while (prev->right && prev->right != cur) { + prev = prev->right; + } + + if (!prev->right) { + prev->right = cur; + cur = cur->left; + } else { + prev->right = nullptr; + if (prevVal != INT_MAX) { + res = min(res, cur->val - prevVal); + } + prevVal = cur->val; + cur = cur->right; + } + } + } + + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + minDiffInBST(root) { + let prevVal = Infinity, res = Infinity; + let cur = root; + + while (cur !== null) { + if (cur.left === null) { + if (prevVal !== Infinity) { + res = Math.min(res, cur.val - prevVal); + } + prevVal = cur.val; + cur = cur.right; + } else { + let prev = cur.left; + while (prev.right !== null && prev.right !== cur) { + prev = prev.right; + } + + if (prev.right === null) { + prev.right = cur; + cur = cur.left; + } else { + prev.right = null; + if (prevVal !== Infinity) { + res = Math.min(res, cur.val - prevVal); + } + prevVal = cur.val; + cur = cur.right; + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/minimum-time-to-collect-all-apples-in-a-tree.md b/articles/minimum-time-to-collect-all-apples-in-a-tree.md new file mode 100644 index 000000000..2ea61fe99 --- /dev/null +++ b/articles/minimum-time-to-collect-all-apples-in-a-tree.md @@ -0,0 +1,312 @@ +## 1. Depth First Search + +::tabs-start + +```python +class Solution: + def minTime(self, n: int, edges: List[List[int]], hasApple: List[bool]) -> int: + adj = {i: [] for i in range(n)} + for par, child in edges: + adj[par].append(child) + adj[child].append(par) + + def dfs(cur, par): + time = 0 + for child in adj[cur]: + if child == par: + continue + childTime = dfs(child, cur) + if childTime > 0 or hasApple[child]: + time += 2 + childTime + return time + + return dfs(0, -1) +``` + +```java +public class Solution { + public int minTime(int n, int[][] edges, List hasApple) { + List[] adj = new ArrayList[n]; + for (int i = 0; i < n; i++) { + adj[i] = new ArrayList<>(); + } + for (int[] edge : edges) { + adj[edge[0]].add(edge[1]); + adj[edge[1]].add(edge[0]); + } + + return dfs(0, -1, adj, hasApple); + } + + private int dfs(int cur, int parent, List[] adj, List hasApple) { + int time = 0; + for (int child : adj[cur]) { + if (child == parent) continue; + int childTime = dfs(child, cur, adj, hasApple); + if (childTime > 0 || hasApple.get(child)) { + time += 2 + childTime; + } + } + return time; + } +} +``` + +```cpp +class Solution { +public: + int minTime(int n, vector>& edges, vector& hasApple) { + vector> adj(n); + for (const auto& edge : edges) { + adj[edge[0]].push_back(edge[1]); + adj[edge[1]].push_back(edge[0]); + } + + return dfs(0, -1, adj, hasApple); + } + +private: + int dfs(int cur, int parent, vector>& adj, vector& hasApple) { + int time = 0; + for (int child : adj[cur]) { + if (child == parent) continue; + int childTime = dfs(child, cur, adj, hasApple); + if (childTime > 0 || hasApple[child]) { + time += 2 + childTime; + } + } + return time; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} edges + * @param {boolean[]} hasApple + * @return {number} + */ + minTime(n, edges, hasApple) { + const adj = Array.from({ length: n }, () => []); + for (const [parent, child] of edges) { + adj[parent].push(child); + adj[child].push(parent); + } + + const dfs = (cur, parent) => { + let time = 0; + for (const child of adj[cur]) { + if (child === parent) continue; + const childTime = dfs(child, cur); + if (childTime > 0 || hasApple[child]) { + time += 2 + childTime; + } + } + return time; + }; + + return dfs(0, -1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. + +--- + +## 2. Topological Sort (Kahn's Algorithm) + +::tabs-start + +```python +class Solution: + def minTime(self, n: int, edges: List[List[int]], hasApple: List[bool]) -> int: + adj = defaultdict(list) + indegree = [0] * n + for u, v in edges: + adj[u].append(v) + adj[v].append(u) + indegree[u] += 1 + indegree[v] += 1 + + queue = deque() + for i in range(1, n): + if indegree[i] == 1: + queue.append(i) + indegree[i] = 0 + + time = [0] * n + while queue: + node = queue.popleft() + for nei in adj[node]: + if indegree[nei] <= 0: + continue + + indegree[nei] -= 1 + if hasApple[node] or time[node] > 0: + time[nei] += time[node] + 2 + if indegree[nei] == 1 and nei != 0: + queue.append(nei) + + return time[0] +``` + +```java +public class Solution { + public int minTime(int n, int[][] edges, List hasApple) { + List[] adj = new ArrayList[n]; + for (int i = 0; i < n; i++) { + adj[i] = new ArrayList<>(); + } + int[] indegree = new int[n]; + for (int[] edge : edges) { + adj[edge[0]].add(edge[1]); + adj[edge[1]].add(edge[0]); + indegree[edge[0]]++; + indegree[edge[1]]++; + } + + Queue queue = new LinkedList<>(); + for (int i = 1; i < n; i++) { + if (indegree[i] == 1) { + queue.offer(i); + indegree[i] = 0; + } + } + + int[] time = new int[n]; + while (!queue.isEmpty()) { + int node = queue.poll(); + for (int neighbor : adj[node]) { + if (indegree[neighbor] <= 0) { + continue; + } + + indegree[neighbor]--; + if (hasApple.get(node) || time[node] > 0) { + time[neighbor] += time[node] + 2; + } + if (indegree[neighbor] == 1 && neighbor != 0) { + queue.offer(neighbor); + } + } + } + + return time[0]; + } +} +``` + +```cpp +class Solution { +public: + int minTime(int n, vector>& edges, vector& hasApple) { + vector> adj(n); + vector indegree(n, 0); + + for (const auto& edge : edges) { + adj[edge[0]].push_back(edge[1]); + adj[edge[1]].push_back(edge[0]); + indegree[edge[0]]++; + indegree[edge[1]]++; + } + + queue q; + for (int i = 1; i < n; ++i) { + if (indegree[i] == 1) { + q.push(i); + indegree[i] = 0; + } + } + + vector time(n, 0); + while (!q.empty()) { + int node = q.front(); + q.pop(); + for (int neighbor : adj[node]) { + if (indegree[neighbor] <= 0) { + continue; + } + + indegree[neighbor]--; + if (hasApple[node] || time[node] > 0) { + time[neighbor] += time[node] + 2; + } + if (indegree[neighbor] == 1 && neighbor != 0) { + q.push(neighbor); + } + } + } + + return time[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} edges + * @param {boolean[]} hasApple + * @return {number} + */ + minTime(n, edges, hasApple) { + const adj = Array.from({ length: n }, () => []); + const indegree = Array(n).fill(0); + + for (const [u, v] of edges) { + adj[u].push(v); + adj[v].push(u); + indegree[u]++; + indegree[v]++; + } + + const queue = new Queue(); + for (let i = 1; i < n; i++) { + if (indegree[i] === 1) { + queue.push(i); + indegree[i] = 0; + } + } + + const time = Array(n).fill(0); + while (!queue.isEmpty()) { + const node = queue.pop(); + for (const neighbor of adj[node]) { + if (indegree[neighbor] <= 0) { + continue; + } + + indegree[neighbor]--; + if (hasApple[node] || time[node] > 0) { + time[neighbor] += time[node] + 2; + } + if (indegree[neighbor] === 1 && neighbor !== 0) { + queue.push(neighbor); + } + } + } + + return time[0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. \ No newline at end of file diff --git a/articles/path-sum.md b/articles/path-sum.md new file mode 100644 index 000000000..ce01351cf --- /dev/null +++ b/articles/path-sum.md @@ -0,0 +1,581 @@ +## 1. Depth First Search - I + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool: + def dfs(node, curSum): + if not node: + return False + + curSum += node.val + if not node.left and not node.right: + return curSum == targetSum + + return dfs(node.left, curSum) or dfs(node.right, curSum) + + return dfs(root, 0) +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public boolean hasPathSum(TreeNode root, int targetSum) { + return dfs(root, 0, targetSum); + } + + private boolean dfs(TreeNode node, int curSum, int targetSum) { + if (node == null) return false; + + curSum += node.val; + if (node.left == null && node.right == null) { + return curSum == targetSum; + } + + return dfs(node.left, curSum, targetSum) || dfs(node.right, curSum, targetSum); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + bool hasPathSum(TreeNode* root, int targetSum) { + return dfs(root, 0, targetSum); + } + +private: + bool dfs(TreeNode* node, int curSum, int targetSum) { + if (node == nullptr) return false; + + curSum += node->val; + if (node->left == nullptr && node->right == nullptr) { + return curSum == targetSum; + } + + return dfs(node->left, curSum, targetSum) || dfs(node->right, curSum, targetSum); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @param {number} targetSum + * @return {boolean} + */ + hasPathSum(root, targetSum) { + const dfs = (node, curSum) => { + if (!node) return false; + + curSum += node.val; + if (!node.left && !node.right) { + return curSum === targetSum; + } + + return dfs(node.left, curSum) || dfs(node.right, curSum); + }; + + return dfs(root, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Depth First Search - II + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool: + if not root: + return False + + targetSum -= root.val + return (self.hasPathSum(root.left, targetSum) or + self.hasPathSum(root.right, targetSum) or + (not targetSum and not root.left and not root.right)) +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public boolean hasPathSum(TreeNode root, int targetSum) { + if (root == null) return false; + targetSum -= root.val; + return hasPathSum(root.left, targetSum) || + hasPathSum(root.right, targetSum) || + (targetSum == 0 && root.left == null && root.right == null); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + bool hasPathSum(TreeNode* root, int targetSum) { + if (!root) return false; + targetSum -= root->val; + return hasPathSum(root->left, targetSum) || + hasPathSum(root->right, targetSum) || + (targetSum == 0 && !root->left && !root->right); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @param {number} targetSum + * @return {boolean} + */ + hasPathSum(root, targetSum) { + if (!root) return false; + targetSum -= root.val; + return this.hasPathSum(root.left, targetSum) || + this.hasPathSum(root.right, targetSum) || + (targetSum === 0 && !root.left && !root.right); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 3. Iterative DFS + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool: + if not root: + return False + + stack = [(root, targetSum - root.val)] + while stack: + node, curr_sum = stack.pop() + if not node.left and not node.right and curr_sum == 0: + return True + if node.right: + stack.append((node.right, curr_sum - node.right.val)) + if node.left: + stack.append((node.left, curr_sum - node.left.val)) + return False +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public boolean hasPathSum(TreeNode root, int targetSum) { + if (root == null) return false; + + Stack stack = new Stack<>(); + Stack sumStack = new Stack<>(); + stack.push(root); + sumStack.push(targetSum - root.val); + + while (!stack.isEmpty()) { + TreeNode node = stack.pop(); + int currSum = sumStack.pop(); + + if (node.left == null && node.right == null && currSum == 0) { + return true; + } + + if (node.right != null) { + stack.push(node.right); + sumStack.push(currSum - node.right.val); + } + + if (node.left != null) { + stack.push(node.left); + sumStack.push(currSum - node.left.val); + } + } + + return false; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + bool hasPathSum(TreeNode* root, int targetSum) { + if (!root) return false; + + stack> s; + s.push({root, targetSum - root->val}); + + while (!s.empty()) { + auto [node, currSum] = s.top(); + s.pop(); + + if (!node->left && !node->right && currSum == 0) { + return true; + } + + if (node->right) { + s.push({node->right, currSum - node->right->val}); + } + + if (node->left) { + s.push({node->left, currSum - node->left->val}); + } + } + + return false; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @param {number} targetSum + * @return {boolean} + */ + hasPathSum(root, targetSum) { + if (!root) return false; + + const stack = [[root, targetSum - root.val]]; + while (stack.length) { + const [node, currSum] = stack.pop(); + + if (!node.left && !node.right && currSum === 0) { + return true; + } + + if (node.right) { + stack.push([node.right, currSum - node.right.val]); + } + + if (node.left) { + stack.push([node.left, currSum - node.left.val]); + } + } + + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Breadth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool: + if not root: + return False + + queue = deque([(root, targetSum - root.val)]) + while queue: + node, curr_sum = queue.popleft() + if not node.left and not node.right and curr_sum == 0: + return True + if node.left: + queue.append((node.left, curr_sum - node.left.val)) + if node.right: + queue.append((node.right, curr_sum - node.right.val)) + return False +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public boolean hasPathSum(TreeNode root, int targetSum) { + if (root == null) return false; + + Queue nodeQueue = new LinkedList<>(); + Queue sumQueue = new LinkedList<>(); + nodeQueue.add(root); + sumQueue.add(targetSum - root.val); + + while (!nodeQueue.isEmpty()) { + TreeNode node = nodeQueue.poll(); + int currSum = sumQueue.poll(); + + if (node.left == null && node.right == null && currSum == 0) { + return true; + } + + if (node.left != null) { + nodeQueue.add(node.left); + sumQueue.add(currSum - node.left.val); + } + + if (node.right != null) { + nodeQueue.add(node.right); + sumQueue.add(currSum - node.right.val); + } + } + + return false; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + bool hasPathSum(TreeNode* root, int targetSum) { + if (!root) return false; + + queue> q; + q.push({root, targetSum - root->val}); + + while (!q.empty()) { + auto [node, currSum] = q.front(); + q.pop(); + + if (!node->left && !node->right && currSum == 0) { + return true; + } + + if (node->left) { + q.push({node->left, currSum - node->left->val}); + } + + if (node->right) { + q.push({node->right, currSum - node->right->val}); + } + } + + return false; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @param {number} targetSum + * @return {boolean} + */ + hasPathSum(root, targetSum) { + if (!root) return false; + + const queue = new Queue([[root, targetSum - root.val]]); + while (!queue.isEmpty()) { + const [node, currSum] = queue.pop(); + + if (!node.left && !node.right && currSum === 0) { + return true; + } + + if (node.left) { + queue.push([node.left, currSum - node.left.val]); + } + + if (node.right) { + queue.push([node.right, currSum - node.right.val]); + } + } + + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/range-sum-of-bst.md b/articles/range-sum-of-bst.md new file mode 100644 index 000000000..cd0873dec --- /dev/null +++ b/articles/range-sum-of-bst.md @@ -0,0 +1,408 @@ +## 1. Depth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def rangeSumBST(self, root: Optional[TreeNode], low: int, high: int) -> int: + if not root: + return 0 + + res = root.val if low <= root.val <= high else 0 + res += self.rangeSumBST(root.left, low, high) + res += self.rangeSumBST(root.right, low, high) + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public int rangeSumBST(TreeNode root, int low, int high) { + if (root == null) return 0; + + int res = (low <= root.val && root.val <= high) ? root.val : 0; + res += rangeSumBST(root.left, low, high); + res += rangeSumBST(root.right, low, high); + return res; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + int rangeSumBST(TreeNode* root, int low, int high) { + if (!root) return 0; + + int res = (low <= root->val && root->val <= high) ? root->val : 0; + res += rangeSumBST(root->left, low, high); + res += rangeSumBST(root->right, low, high); + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @param {number} low + * @param {number} high + * @return {number} + */ + rangeSumBST(root, low, high) { + if (!root) return 0; + + let res = (low <= root.val && root.val <= high) ? root.val : 0; + res += this.rangeSumBST(root.left, low, high); + res += this.rangeSumBST(root.right, low, high); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Depth First Search (Optimal) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def rangeSumBST(self, root: Optional[TreeNode], low: int, high: int) -> int: + if not root: + return 0 + + if root.val > high: + return self.rangeSumBST(root.left, low, high) + if root.val < low: + return self.rangeSumBST(root.right, low, high) + + return ( + root.val + + self.rangeSumBST(root.left, low, high) + + self.rangeSumBST(root.right, low, high) + ) +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public int rangeSumBST(TreeNode root, int low, int high) { + if (root == null) return 0; + + if (root.val > high) { + return rangeSumBST(root.left, low, high); + } + if (root.val < low) { + return rangeSumBST(root.right, low, high); + } + + return root.val + rangeSumBST(root.left, low, high) + + rangeSumBST(root.right, low, high); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + int rangeSumBST(TreeNode* root, int low, int high) { + if (!root) return 0; + + if (root->val > high) { + return rangeSumBST(root->left, low, high); + } + if (root->val < low) { + return rangeSumBST(root->right, low, high); + } + + return root->val + rangeSumBST(root->left, low, high) + + rangeSumBST(root->right, low, high); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @param {number} low + * @param {number} high + * @return {number} + */ + rangeSumBST(root, low, high) { + if (!root) return 0; + + if (root.val > high) { + return rangeSumBST(root.left, low, high); + } + if (root.val < low) { + return rangeSumBST(root.right, low, high); + } + + return root.val + this.rangeSumBST(root.left, low, high) + + this.rangeSumBST(root.right, low, high); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 3. Iterative DFS + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def rangeSumBST(self, root: Optional[TreeNode], low: int, high: int) -> int: + stack = [root] + res = 0 + + while stack: + node = stack.pop() + if not node: + continue + + if low <= node.val <= high: + res += node.val + if node.val > low: + stack.append(node.left) + if node.val < high: + stack.append(node.right) + + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public int rangeSumBST(TreeNode root, int low, int high) { + int res = 0; + Stack stack = new Stack<>(); + stack.push(root); + + while (!stack.isEmpty()) { + TreeNode node = stack.pop(); + if (node == null) continue; + + if (low <= node.val && node.val <= high) { + res += node.val; + } + if (node.val > low) { + stack.push(node.left); + } + if (node.val < high) { + stack.push(node.right); + } + } + + return res; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + int rangeSumBST(TreeNode* root, int low, int high) { + int res = 0; + stack stack; + stack.push(root); + + while (!stack.empty()) { + TreeNode* node = stack.top(); + stack.pop(); + if (!node) continue; + + if (low <= node->val && node->val <= high) { + res += node->val; + } + if (node->val > low) { + stack.push(node->left); + } + if (node->val < high) { + stack.push(node->right); + } + } + + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @param {number} low + * @param {number} high + * @return {number} + */ + rangeSumBST(root, low, high) { + let res = 0; + let stack = [root]; + + while (stack.length > 0) { + let node = stack.pop(); + if (!node) continue; + + if (low <= node.val && node.val <= high) { + res += node.val; + } + if (node.val > low) { + stack.push(node.left); + } + if (node.val < high) { + stack.push(node.right); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/same-binary-tree.md b/articles/same-binary-tree.md index 6cbfc12aa..aa0f1a59a 100644 --- a/articles/same-binary-tree.md +++ b/articles/same-binary-tree.md @@ -191,7 +191,7 @@ class Solution { ### Time & Space Complexity * Time complexity: $O(n)$ -* Space complexity: $O(h)$ +* Space complexity: $O(n)$ * Best Case ([balanced tree](https://www.geeksforgeeks.org/balanced-binary-tree/)): $O(log(n))$ * Worst Case ([degenerate tree](https://www.geeksforgeeks.org/introduction-to-degenerate-binary-tree/)): $O(n)$ @@ -199,7 +199,261 @@ class Solution { --- -## 2. Breadth First Search +## 2. Iterative DFS + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right + +class Solution: + def isSameTree(self, p: Optional[TreeNode], q: Optional[TreeNode]) -> bool: + stack = [(p, q)] + + while stack: + node1, node2 = stack.pop() + + if not node1 and not node2: + continue + if not node1 or not node2 or node1.val != node2.val: + return False + + stack.append((node1.right, node2.right)) + stack.append((node1.left, node2.left)) + + return True +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + public boolean isSameTree(TreeNode p, TreeNode q) { + Stack stack = new Stack<>(); + stack.push(new TreeNode[]{p, q}); + + while (!stack.isEmpty()) { + TreeNode[] nodes = stack.pop(); + TreeNode node1 = nodes[0], node2 = nodes[1]; + + if (node1 == null && node2 == null) continue; + if (node1 == null || node2 == null || node1.val != node2.val) { + return false; + } + stack.push(new TreeNode[]{node1.right, node2.right}); + stack.push(new TreeNode[]{node1.left, node2.left}); + } + + return true; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ + +class Solution { +public: + bool isSameTree(TreeNode* p, TreeNode* q) { + stack> stk; + stk.push({p, q}); + + while (!stk.empty()) { + auto [node1, node2] = stk.top(); + stk.pop(); + + if (!node1 && !node2) continue; + if (!node1 || !node2 || node1->val != node2->val) return false; + + stk.push({node1->right, node2->right}); + stk.push({node1->left, node2->left}); + } + + return true; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +class Solution { + /** + * @param {TreeNode} p + * @param {TreeNode} q + * @return {boolean} + */ + isSameTree(p, q) { + const stack = [[p, q]]; + + while (stack.length) { + const [node1, node2] = stack.pop(); + + if (!node1 && !node2) continue; + if (!node1 || !node2 || node1.val !== node2.val) { + return false; + } + stack.push([node1.right, node2.right]); + stack.push([node1.left, node2.left]); + } + + return true; + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + public bool IsSameTree(TreeNode p, TreeNode q) { + var stack = new Stack<(TreeNode, TreeNode)>(); + stack.Push((p, q)); + + while (stack.Count > 0) { + var (node1, node2) = stack.Pop(); + + if (node1 == null && node2 == null) continue; + if (node1 == null || node2 == null || node1.val != node2.val) { + return false; + } + stack.Push((node1.right, node2.right)); + stack.Push((node1.left, node2.left)); + } + + return true; + } +} +``` + +```go +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ +func isSameTree(p *TreeNode, q *TreeNode) bool { + type Pair struct { + first, second *TreeNode + } + + stack := []Pair{{p, q}} + + for len(stack) > 0 { + lastIdx := len(stack) - 1 + node1, node2 := stack[lastIdx].first, stack[lastIdx].second + stack = stack[:lastIdx] + + if node1 == nil && node2 == nil { + continue + } + if node1 == nil || node2 == nil || node1.Val != node2.Val { + return false + } + + stack = append(stack, Pair{node1.Right, node2.Right}) + stack = append(stack, Pair{node1.Left, node2.Left}) + } + + return true +} +``` + +```kotlin +/** + * Example: + * var ti = TreeNode(5) + * var v = ti.`val` + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ +class Solution { + fun isSameTree(p: TreeNode?, q: TreeNode?): Boolean { + val stack = ArrayDeque>() + stack.addLast(Pair(p, q)) + + while (stack.isNotEmpty()) { + val (node1, node2) = stack.removeLast() + + if (node1 == null && node2 == null) continue + if (node1 == null || node2 == null || node1.`val` != node2.`val`) { + return false + } + stack.addLast(Pair(node1.right, node2.right)) + stack.addLast(Pair(node1.left, node2.left)) + } + + return true + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Breadth First Search ::tabs-start @@ -217,20 +471,21 @@ class Solution: q2 = deque([q]) while q1 and q2: - nodeP = q1.popleft() - nodeQ = q2.popleft() + for _ in range(len(q1)): + nodeP = q1.popleft() + nodeQ = q2.popleft() - if nodeP is None and nodeQ is None: - continue - if nodeP is None or nodeQ is None or nodeP.val != nodeQ.val: - return False + if nodeP is None and nodeQ is None: + continue + if nodeP is None or nodeQ is None or nodeP.val != nodeQ.val: + return False - q1.append(nodeP.left) - q1.append(nodeP.right) - q2.append(nodeQ.left) - q2.append(nodeQ.right) + q1.append(nodeP.left) + q1.append(nodeP.right) + q2.append(nodeQ.left) + q2.append(nodeQ.right) - return True + return True ``` ```java @@ -258,17 +513,19 @@ public class Solution { q2.add(q); while (!q1.isEmpty() && !q2.isEmpty()) { - TreeNode nodeP = q1.poll(); - TreeNode nodeQ = q2.poll(); - - if (nodeP == null && nodeQ == null) continue; - if (nodeP == null || nodeQ == null || nodeP.val != nodeQ.val) - return false; - - q1.add(nodeP.left); - q1.add(nodeP.right); - q2.add(nodeQ.left); - q2.add(nodeQ.right); + for (int i = q1.size(); i > 0; i--) { + TreeNode nodeP = q1.poll(); + TreeNode nodeQ = q2.poll(); + + if (nodeP == null && nodeQ == null) continue; + if (nodeP == null || nodeQ == null || nodeP.val != nodeQ.val) + return false; + + q1.add(nodeP.left); + q1.add(nodeP.right); + q2.add(nodeQ.left); + q2.add(nodeQ.right); + } } return true; @@ -298,17 +555,19 @@ public: q2.push(q); while (!q1.empty() && !q2.empty()) { - TreeNode* nodeP = q1.front(); q1.pop(); - TreeNode* nodeQ = q2.front(); q2.pop(); - - if (!nodeP && !nodeQ) continue; - if (!nodeP || !nodeQ || nodeP->val != nodeQ->val) - return false; - - q1.push(nodeP->left); - q1.push(nodeP->right); - q2.push(nodeQ->left); - q2.push(nodeQ->right); + for (int i = q1.size(); i > 0; i--) { + TreeNode* nodeP = q1.front(); q1.pop(); + TreeNode* nodeQ = q2.front(); q2.pop(); + + if (!nodeP && !nodeQ) continue; + if (!nodeP || !nodeQ || nodeP->val != nodeQ->val) + return false; + + q1.push(nodeP->left); + q1.push(nodeP->right); + q2.push(nodeQ->left); + q2.push(nodeQ->right); + } } return true; @@ -341,17 +600,20 @@ class Solution { q2.push(q); while (!q1.isEmpty() && !q2.isEmpty()) { - let nodeP = q1.pop(); - let nodeQ = q2.pop(); - - if (nodeP === null && nodeQ === null) continue; - if (nodeP === null || nodeQ === null || nodeP.val !== nodeQ.val) - return false; - - q1.push(nodeP.left); - q1.push(nodeP.right); - q2.push(nodeQ.left); - q2.push(nodeQ.right); + for (let i = q1.size(); i > 0; i--) { + let nodeP = q1.pop(); + let nodeQ = q2.pop(); + + if (nodeP === null && nodeQ === null) continue; + if (nodeP === null || nodeQ === null || nodeP.val !== nodeQ.val) { + return false; + } + + q1.push(nodeP.left); + q1.push(nodeP.right); + q2.push(nodeQ.left); + q2.push(nodeQ.right); + } } return true; @@ -380,17 +642,20 @@ public class Solution { var q2 = new Queue(new[] { q }); while (q1.Count > 0 && q2.Count > 0) { - var nodeP = q1.Dequeue(); - var nodeQ = q2.Dequeue(); - - if (nodeP == null && nodeQ == null) continue; - if (nodeP == null || nodeQ == null || nodeP.val != nodeQ.val) - return false; - - q1.Enqueue(nodeP.left); - q1.Enqueue(nodeP.right); - q2.Enqueue(nodeQ.left); - q2.Enqueue(nodeQ.right); + for (int i = q1.Count; i > 0; i--) { + var nodeP = q1.Dequeue(); + var nodeQ = q2.Dequeue(); + + if (nodeP == null && nodeQ == null) continue; + if (nodeP == null || nodeQ == null || nodeP.val != nodeQ.val) { + return false; + } + + q1.Enqueue(nodeP.left); + q1.Enqueue(nodeP.right); + q2.Enqueue(nodeQ.left); + q2.Enqueue(nodeQ.right); + } } return true; @@ -412,20 +677,22 @@ func isSameTree(p *TreeNode, q *TreeNode) bool { queue2 := []*TreeNode{q} for len(queue1) > 0 && len(queue2) > 0 { - nodeP := queue1[0] - nodeQ := queue2[0] - queue1 = queue1[1:] - queue2 = queue2[1:] + for i := len(queue1); i > 0; i-- { + nodeP := queue1[0] + nodeQ := queue2[0] + queue1 = queue1[1:] + queue2 = queue2[1:] - if nodeP == nil && nodeQ == nil { - continue - } - if nodeP == nil || nodeQ == nil || nodeP.Val != nodeQ.Val { - return false - } + if nodeP == nil && nodeQ == nil { + continue + } + if nodeP == nil || nodeQ == nil || nodeP.Val != nodeQ.Val { + return false + } - queue1 = append(queue1, nodeP.Left, nodeP.Right) - queue2 = append(queue2, nodeQ.Left, nodeQ.Right) + queue1 = append(queue1, nodeP.Left, nodeP.Right) + queue2 = append(queue2, nodeQ.Left, nodeQ.Right) + } } return len(queue1) == 0 && len(queue2) == 0 @@ -452,20 +719,22 @@ class Solution { q2.add(q) while (q1.isNotEmpty() && q2.isNotEmpty()) { - val nodeP = q1.removeFirst() - val nodeQ = q2.removeFirst() - - if (nodeP == null && nodeQ == null) { - continue + for (i in q1.size downTo 1) { + val nodeP = q1.removeFirst() + val nodeQ = q2.removeFirst() + + if (nodeP == null && nodeQ == null) { + continue + } + if (nodeP == null || nodeQ == null || nodeP.`val` != nodeQ.`val`) { + return false + } + + q1.add(nodeP.left) + q1.add(nodeP.right) + q2.add(nodeQ.left) + q2.add(nodeQ.right) } - if (nodeP == null || nodeQ == null || nodeP.`val` != nodeQ.`val`) { - return false - } - - q1.add(nodeP.left) - q1.add(nodeP.right) - q2.add(nodeQ.left) - q2.add(nodeQ.right) } return true diff --git a/articles/sliding-window-median.md b/articles/sliding-window-median.md index e328fe000..b49ca4071 100644 --- a/articles/sliding-window-median.md +++ b/articles/sliding-window-median.md @@ -342,8 +342,12 @@ class Solution: ```java public class Solution { public double[] medianSlidingWindow(int[] nums, int k) { - TreeSet small = new TreeSet<>((a, b) -> nums[a] != nums[b] ? Integer.compare(nums[a], nums[b]) : Integer.compare(a, b)); - TreeSet large = new TreeSet<>((a, b) -> nums[a] != nums[b] ? Integer.compare(nums[a], nums[b]) : Integer.compare(a, b)); + TreeSet small = new TreeSet<>((a, b) -> + nums[a] != nums[b] ? Integer.compare(nums[a], nums[b]) : Integer.compare(a, b) + ); + TreeSet large = new TreeSet<>((a, b) -> + nums[a] != nums[b] ? Integer.compare(nums[a], nums[b]) : Integer.compare(a, b) + ); double[] res = new double[nums.length - k + 1]; for (int i = 0; i < nums.length; i++) { if (small.isEmpty() || nums[i] <= nums[small.last()]) small.add(i); @@ -355,7 +359,8 @@ public class Solution { while (small.size() > large.size() + 1) large.add(small.pollLast()); while (large.size() > small.size()) small.add(large.pollFirst()); if (i >= k - 1) { - res[i - k + 1] = k % 2 == 1 ? nums[small.last()] : (nums[small.last()] + 0L + nums[large.first()]) / 2.0; + res[i - k + 1] = k % 2 == 1 ? nums[small.last()] : + (nums[small.last()] + 0L + nums[large.first()]) / 2.0; } } return res; @@ -385,7 +390,10 @@ public: large.erase(large.begin()); } if (i >= k - 1) { - res.push_back(k % 2 == 1 ? *small.rbegin() : (*small.rbegin() + 0LL + *large.begin()) / 2.0); + res.push_back( + k % 2 == 1 ? *small.rbegin() : + ((*small.rbegin() + 0LL + *large.begin()) / 2.0) + ); } } return res; diff --git a/articles/symmetric-tree.md b/articles/symmetric-tree.md new file mode 100644 index 000000000..615d4dd34 --- /dev/null +++ b/articles/symmetric-tree.md @@ -0,0 +1,447 @@ +## 1. Depth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def isSymmetric(self, root: Optional[TreeNode]) -> bool: + def dfs(left, right): + if not left and not right: + return True + if not left or not right: + return False + return ( + left.val == right.val and + dfs(left.left, right.right) and + dfs(left.right, right.left) + ) + return dfs(root.left, root.right) +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public boolean isSymmetric(TreeNode root) { + return dfs(root.left, root.right); + } + + private boolean dfs(TreeNode left, TreeNode right) { + if (left == null && right == null) { + return true; + } + if (left == null || right == null) { + return false; + } + return left.val == right.val && + dfs(left.left, right.right) && + dfs(left.right, right.left); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + bool isSymmetric(TreeNode* root) { + return dfs(root->left, root->right); + } + +private: + bool dfs(TreeNode* left, TreeNode* right) { + if (!left && !right) { + return true; + } + if (!left || !right) { + return false; + } + return (left->val == right->val) && + dfs(left->left, right->right) && + dfs(left->right, right->left); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {boolean} + */ + isSymmetric(root) { + const dfs = (left, right) => { + if (!left && !right) { + return true; + } + if (!left || !right) { + return false; + } + return ( + left.val === right.val && + dfs(left.left, right.right) && + dfs(left.right, right.left) + ); + }; + return dfs(root.left, root.right); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Iterative DFS + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def isSymmetric(self, root: Optional[TreeNode]) -> bool: + if not root: + return True + + stack = [(root.left, root.right)] + while stack: + left, right = stack.pop() + if not left and not right: + continue + if not left or not right or left.val != right.val: + return False + stack.append((left.left, right.right)) + stack.append((left.right, right.left)) + return True +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public boolean isSymmetric(TreeNode root) { + if (root == null) return true; + + Stack stack = new Stack<>(); + stack.push(new TreeNode[]{root.left, root.right}); + + while (!stack.isEmpty()) { + TreeNode[] nodes = stack.pop(); + TreeNode left = nodes[0], right = nodes[1]; + + if (left == null && right == null) continue; + if (left == null || right == null || left.val != right.val) { + return false; + } + + stack.push(new TreeNode[]{left.left, right.right}); + stack.push(new TreeNode[]{left.right, right.left}); + } + + return true; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + bool isSymmetric(TreeNode* root) { + if (!root) return true; + + std::stack> stack; + stack.push({root->left, root->right}); + + while (!stack.empty()) { + auto [left, right] = stack.top(); + stack.pop(); + + if (!left && !right) continue; + if (!left || !right || left->val != right->val) { + return false; + } + stack.push({left->left, right->right}); + stack.push({left->right, right->left}); + } + + return true; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {boolean} + */ + isSymmetric(root) { + if (!root) return true; + + const stack = [[root.left, root.right]]; + + while (stack.length > 0) { + const [left, right] = stack.pop(); + + if (!left && !right) continue; + if (!left || !right || left.val !== right.val) { + return false; + } + + stack.push([left.left, right.right]); + stack.push([left.right, right.left]); + } + + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Breadth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def isSymmetric(self, root: Optional[TreeNode]) -> bool: + if not root: + return True + + queue = deque([(root.left, root.right)]) + while queue: + for _ in range(len(queue)): + left, right = queue.popleft() + if not left and not right: + continue + if not left or not right or left.val != right.val: + return False + queue.append((left.left, right.right)) + queue.append((left.right, right.left)) + + return True +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public boolean isSymmetric(TreeNode root) { + if (root == null) return true; + + Queue queue = new LinkedList<>(); + queue.add(new TreeNode[]{root.left, root.right}); + + while (!queue.isEmpty()) { + for (int i = queue.size(); i > 0; i--) { + TreeNode[] nodes = queue.poll(); + TreeNode left = nodes[0], right = nodes[1]; + + if (left == null && right == null) continue; + if (left == null || right == null || left.val != right.val) { + return false; + } + queue.add(new TreeNode[]{left.left, right.right}); + queue.add(new TreeNode[]{left.right, right.left}); + } + } + + return true; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + bool isSymmetric(TreeNode* root) { + if (!root) return true; + + queue> queue; + queue.push({root->left, root->right}); + + while (!queue.empty()) { + for (int i = queue.size(); i > 0; i--) { + auto [left, right] = queue.front(); + queue.pop(); + + if (!left && !right) continue; + if (!left || !right || left->val != right->val) { + return false; + } + queue.push({left->left, right->right}); + queue.push({left->right, right->left}); + } + } + + return true; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {boolean} + */ + isSymmetric(root) { + if (!root) return true; + + const queue = new Queue([[root.left, root.right]]); + + while (!queue.isEmpty()) { + for (let i = queue.size(); i > 0; i--) { + const [left, right] = queue.pop(); + + if (!left && !right) continue; + if (!left || !right || left.val !== right.val) { + return false; + } + queue.push([left.left, right.right]); + queue.push([left.right, right.left]); + } + } + + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/time-needed-to-inform-all-employees.md b/articles/time-needed-to-inform-all-employees.md new file mode 100644 index 000000000..7a80a7c02 --- /dev/null +++ b/articles/time-needed-to-inform-all-employees.md @@ -0,0 +1,479 @@ +## 1. Depth First Search + +::tabs-start + +```python +class Solution: + def numOfMinutes(self, n: int, headID: int, manager: List[int], informTime: List[int]) -> int: + adj = [[] for _ in range(n)] + for i in range(n): + if i != headID: + adj[manager[i]].append(i) + + def dfs(node): + res = 0 + for child in adj[node]: + res = max(res, informTime[node] + dfs(child)) + return res + + return dfs(headID) +``` + +```java +public class Solution { + public int numOfMinutes(int n, int headID, int[] manager, int[] informTime) { + List[] adj = new ArrayList[n]; + for (int i = 0; i < n; i++) { + adj[i] = new ArrayList<>(); + } + for (int i = 0; i < n; i++) { + if (i != headID) { + adj[manager[i]].add(i); + } + } + + return dfs(headID, adj, informTime); + } + + private int dfs(int node, List[] adj, int[] informTime) { + int res = 0; + for (int child : adj[node]) { + res = Math.max(res, informTime[node] + dfs(child, adj, informTime)); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int numOfMinutes(int n, int headID, vector& manager, vector& informTime) { + vector> adj(n); + for (int i = 0; i < n; i++) { + if (i != headID) { + adj[manager[i]].push_back(i); + } + } + return dfs(headID, adj, informTime); + } + +private: + int dfs(int node, vector>& adj, vector& informTime) { + int res = 0; + for (int child : adj[node]) { + res = max(res, informTime[node] + dfs(child, adj, informTime)); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} headID + * @param {number[]} manager + * @param {number[]} informTime + * @return {number} + */ + numOfMinutes(n, headID, manager, informTime) { + const adj = Array.from({ length: n }, () => []); + for (let i = 0; i < n; i++) { + if (i !== headID) { + adj[manager[i]].push(i); + } + } + + const dfs = (node) => { + let res = 0; + for (const child of adj[node]) { + res = Math.max(res, informTime[node] + dfs(child)); + } + return res; + }; + + return dfs(headID); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Breadth First Search + +::tabs-start + +```python +class Solution: + def numOfMinutes(self, n: int, headID: int, manager: List[int], informTime: List[int]) -> int: + adj = defaultdict(list) + for i in range(n): + adj[manager[i]].append(i) + + q = deque([(headID, 0)]) # (id, time) + res = 0 + + while q: + node, time = q.popleft() + res = max(res, time) + for emp in adj[node]: + q.append((emp, time + informTime[node])) + + return res +``` + +```java +public class Solution { + public int numOfMinutes(int n, int headID, int[] manager, int[] informTime) { + Map> adj = new HashMap<>(); + for (int i = 0; i < n; i++) { + adj.computeIfAbsent(manager[i], k -> new ArrayList<>()).add(i); + } + + Queue queue = new LinkedList<>(); + queue.add(new int[]{headID, 0}); + int res = 0; + + while (!queue.isEmpty()) { + int[] curr = queue.poll(); + int id = curr[0], time = curr[1]; + res = Math.max(res, time); + for (int emp : adj.getOrDefault(id, new ArrayList<>())) { + queue.add(new int[]{emp, time + informTime[id]}); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int numOfMinutes(int n, int headID, vector& manager, vector& informTime) { + unordered_map> adj; + for (int i = 0; i < n; ++i) { + adj[manager[i]].push_back(i); + } + + queue> q; // {id, time} + q.push({headID, 0}); + int res = 0; + + while (!q.empty()) { + auto [id, time] = q.front(); + q.pop(); + res = max(res, time); + for (int emp : adj[id]) { + q.push({emp, time + informTime[id]}); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} headID + * @param {number[]} manager + * @param {number[]} informTime + * @return {number} + */ + numOfMinutes(n, headID, manager, informTime) { + const adj = new Map(); + for (let i = 0; i < n; i++) { + if (!adj.has(manager[i])) adj.set(manager[i], []); + adj.get(manager[i]).push(i); + } + + const queue = new Queue([[headID, 0]]); // [id, time] + let res = 0; + + while (!queue.isEmpty()) { + const [id, time] = queue.pop(); + res = Math.max(res, time); + if (adj.has(id)) { + for (const emp of adj.get(id)) { + queue.push([emp, time + informTime[id]]); + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Topological Sort (Kahn's Algorithm) + +::tabs-start + +```python +class Solution: + def numOfMinutes(self, n: int, headID: int, manager: List[int], informTime: List[int]) -> int: + indegree = [0] * n + time = [0] * n + + for i in range(n): + if manager[i] != -1: + indegree[manager[i]] += 1 + + queue = deque() + for i in range(n): + if indegree[i] == 0: + queue.append(i) + + while queue: + node = queue.popleft() + time[node] += informTime[node] + if manager[node] != -1: + time[manager[node]] = max(time[manager[node]], time[node]) + indegree[manager[node]] -= 1 + if indegree[manager[node]] == 0: + queue.append(manager[node]) + + return time[headID] +``` + +```java +public class Solution { + public int numOfMinutes(int n, int headID, int[] manager, int[] informTime) { + int[] indegree = new int[n]; + int[] time = new int[n]; + + for (int i = 0; i < n; i++) { + if (manager[i] != -1) { + indegree[manager[i]]++; + } + } + + Queue queue = new LinkedList<>(); + for (int i = 0; i < n; i++) { + if (indegree[i] == 0) { + queue.add(i); + } + } + + while (!queue.isEmpty()) { + int node = queue.poll(); + time[node] += informTime[node]; + if (manager[node] != -1) { + time[manager[node]] = Math.max(time[manager[node]], time[node]); + indegree[manager[node]]--; + if (indegree[manager[node]] == 0) { + queue.add(manager[node]); + } + } + } + + return time[headID]; + } +} +``` + +```cpp +class Solution { +public: + int numOfMinutes(int n, int headID, vector& manager, vector& informTime) { + vector indegree(n, 0); + vector time(n, 0); + + for (int i = 0; i < n; ++i) { + if (manager[i] != -1) { + indegree[manager[i]]++; + } + } + + queue queue; + for (int i = 0; i < n; ++i) { + if (indegree[i] == 0) { + queue.push(i); + } + } + + while (!queue.empty()) { + int node = queue.front(); + queue.pop(); + time[node] += informTime[node]; + if (manager[node] != -1) { + time[manager[node]] = max(time[manager[node]], time[node]); + indegree[manager[node]]--; + if (indegree[manager[node]] == 0) { + queue.push(manager[node]); + } + } + } + + return time[headID]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} headID + * @param {number[]} manager + * @param {number[]} informTime + * @return {number} + */ + numOfMinutes(n, headID, manager, informTime) { + const indegree = Array(n).fill(0); + const time = Array(n).fill(0); + + for (let i = 0; i < n; i++) { + if (manager[i] !== -1) { + indegree[manager[i]]++; + } + } + + const queue = new Queue(); + for (let i = 0; i < n; i++) { + if (indegree[i] === 0) { + queue.push(i); + } + } + + while (!queue.isEmpty()) { + const node = queue.pop(); + time[node] += informTime[node]; + if (manager[node] !== -1) { + time[manager[node]] = Math.max(time[manager[node]], time[node]); + indegree[manager[node]]--; + if (indegree[manager[node]] === 0) { + queue.push(manager[node]); + } + } + } + + return time[headID]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Depth First Search (Optimal) + +::tabs-start + +```python +class Solution: + def numOfMinutes(self, n: int, headID: int, manager: List[int], informTime: List[int]) -> int: + def dfs(node): + if manager[node] != -1: + informTime[node] += dfs(manager[node]) + manager[node] = -1 + return informTime[node] + + res = 0 + for node in range(n): + res = max(res, dfs(node)) + return res +``` + +```java +public class Solution { + public int numOfMinutes(int n, int headID, int[] manager, int[] informTime) { + int res = 0; + for (int node = 0; node < n; node++) { + res = Math.max(res, dfs(node, manager, informTime)); + } + return res; + } + + private int dfs(int node, int[] manager, int[] informTime) { + if (manager[node] != -1) { + informTime[node] += dfs(manager[node], manager, informTime); + manager[node] = -1; + } + return informTime[node]; + } +} +``` + +```cpp +class Solution { +public: + int numOfMinutes(int n, int headID, vector& manager, vector& informTime) { + function dfs = [&](int node) { + if (manager[node] != -1) { + informTime[node] += dfs(manager[node]); + manager[node] = -1; + } + return informTime[node]; + }; + + int res = 0; + for (int node = 0; node < n; ++node) { + res = max(res, dfs(node)); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} headID + * @param {number[]} manager + * @param {number[]} informTime + * @return {number} + */ + numOfMinutes(n, headID, manager, informTime) { + const dfs = (node) => { + if (manager[node] !== -1) { + informTime[node] += dfs(manager[node]); + manager[node] = -1; + } + return informTime[node]; + }; + + let res = 0; + for (let node = 0; node < n; node++) { + res = Math.max(res, dfs(node)); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. \ No newline at end of file diff --git a/articles/unique-binary-search-trees.md b/articles/unique-binary-search-trees.md new file mode 100644 index 000000000..499868203 --- /dev/null +++ b/articles/unique-binary-search-trees.md @@ -0,0 +1,411 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def numTrees(self, n: int) -> int: + if n <= 1: + return 1 + + res = 0 + for i in range(1, n + 1): + res += self.numTrees(i - 1) * self.numTrees(n - i) + return res +``` + +```java +public class Solution { + public int numTrees(int n) { + if (n <= 1) { + return 1; + } + + int res = 0; + for (int i = 1; i <= n; i++) { + res += numTrees(i - 1) * numTrees(n - i); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int numTrees(int n) { + if (n <= 1) { + return 1; + } + + int res = 0; + for (int i = 1; i <= n; i++) { + res += numTrees(i - 1) * numTrees(n - i); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + numTrees(n) { + if (n <= 1) { + return 1; + } + + let res = 0; + for (let i = 1; i <= n; i++) { + res += this.numTrees(i - 1) * this.numTrees(n - i); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(3 ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + + def __init__(self): + self.dp = {} + + def numTrees(self, n: int) -> int: + if n <= 1: + return 1 + if n in self.dp: + return self.dp[n] + + res = 0 + for i in range(1, n + 1): + res += self.numTrees(i - 1) * self.numTrees(n - i) + + self.dp[n] = res + return res +``` + +```java +public class Solution { + private Map dp = new HashMap<>(); + + public int numTrees(int n) { + if (n <= 1) { + return 1; + } + if (dp.containsKey(n)) { + return dp.get(n); + } + + int res = 0; + for (int i = 1; i <= n; i++) { + res += numTrees(i - 1) * numTrees(n - i); + } + + dp.put(n, res); + return res; + } +} +``` + +```cpp +class Solution { +private: + unordered_map dp; + +public: + int numTrees(int n) { + if (n <= 1) { + return 1; + } + if (dp.find(n) != dp.end()) { + return dp[n]; + } + + int res = 0; + for (int i = 1; i <= n; i++) { + res += numTrees(i - 1) * numTrees(n - i); + } + + dp[n] = res; + return res; + } +}; +``` + +```javascript +class Solution { + constructor() { + this.dp = new Map(); + } + + /** + * @param {number} n + * @return {number} + */ + numTrees(n) { + if (n <= 1) { + return 1; + } + if (this.dp.has(n)) { + return this.dp.get(n); + } + + let res = 0; + for (let i = 1; i <= n; i++) { + res += this.numTrees(i - 1) * this.numTrees(n - i); + } + + this.dp.set(n, res); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def numTrees(self, n: int) -> int: + numTree = [1] * (n + 1) + + for nodes in range(2, n + 1): + total = 0 + for root in range(1, nodes + 1): + left = root - 1 + right = nodes - root + total += numTree[left] * numTree[right] + numTree[nodes] = total + + return numTree[n] +``` + +```java +public class Solution { + public int numTrees(int n) { + int[] numTree = new int[n + 1]; + numTree[0] = 1; + numTree[1] = 1; + + for (int nodes = 2; nodes <= n; nodes++) { + int total = 0; + for (int root = 1; root <= nodes; root++) { + int left = root - 1; + int right = nodes - root; + total += numTree[left] * numTree[right]; + } + numTree[nodes] = total; + } + + return numTree[n]; + } +} +``` + +```cpp +class Solution { +public: + int numTrees(int n) { + vector numTree(n + 1, 1); + + for (int nodes = 2; nodes <= n; ++nodes) { + int total = 0; + for (int root = 1; root <= nodes; ++root) { + int left = root - 1; + int right = nodes - root; + total += numTree[left] * numTree[right]; + } + numTree[nodes] = total; + } + + return numTree[n]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + numTrees(n) { + const numTree = Array(n + 1).fill(1); + + for (let nodes = 2; nodes <= n; nodes++) { + let total = 0; + for (let root = 1; root <= nodes; root++) { + let left = root - 1; + let right = nodes - root; + total += numTree[left] * numTree[right]; + } + numTree[nodes] = total; + } + + return numTree[n]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 4. Catalan Numbers - I + +::tabs-start + +```python +class Solution: + def numTrees(self, n: int) -> int: + res = 1 + for i in range(1, n): + res *= (n + i + 1) + res //= i + return res // n +``` + +```java +public class Solution { + public int numTrees(int n) { + long res = 1; + for (int i = 1; i < n; i++) { + res *= (n + i + 1); + res /= i; + } + return (int) (res / n); + } +} +``` + +```cpp +class Solution { +public: + int numTrees(int n) { + long long res = 1; + for (int i = 1; i < n; i++) { + res *= (n + i + 1); + res /= i; + } + return res / n; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + numTrees(n) { + let res = 1n; + for (let i = 1n; i < BigInt(n); i++) { + res *= BigInt(n) + i + 1n; + res /= i; + } + return Number(res / BigInt(n)); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 5. Catalan Numbers - II + +::tabs-start + +```python +class Solution: + def numTrees(self, n: int) -> int: + res = 1 + for i in range(n): + res *= (4 * i + 2) / (i + 2) + return int(res) +``` + +```java +public class Solution { + public int numTrees(int n) { + long res = 1; + for (int i = 0; i < n; i++) { + res *= (4 * i + 2) / (i + 2.0); + } + return (int) res; + } +} +``` + +```cpp +class Solution { +public: + int numTrees(int n) { + long long res = 1; + for (int i = 0; i < n; i++) { + res *= (4 * i + 2) / (i + 2.0); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + numTrees(n) { + let res = 1; + for (let i = 0; i < n; i++) { + res *= (4 * i + 2) / (i + 2); + } + return Math.floor(res); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/hints/design-twitter-feed.md b/hints/design-twitter-feed.md index 4dcbd38e4..8fc7ab4f8 100644 --- a/hints/design-twitter-feed.md +++ b/hints/design-twitter-feed.md @@ -2,7 +2,7 @@
Recommended Time & Space Complexity

- You should aim for a solution with O(n) time for each getNewsFeed() function call, O(1) time for the remaining methods, and O((N * m) + (N * M) + n) space, where n is the number of followeeIds associated with the userId, m is the maximum number of tweets by any user, N is the total number of userIds, and M is the maximum number of followees for any user. + You should aim for a solution with O(nlogn) time for each getNewsFeed() function call, O(1) time for the remaining methods, and O((N * m) + (N * M) + n) space, where n is the number of followeeIds associated with the userId, m is the maximum number of tweets by any user, N is the total number of userIds, and M is the maximum number of followees for any user.