From f5ebe964c1e6117761a59d2e98cd951f72ad5964 Mon Sep 17 00:00:00 2001 From: ranveersingh2718 Date: Sun, 7 Dec 2025 11:56:53 -0800 Subject: [PATCH 01/15] add binary-tree-longest-consecutive-sequence.md article --- ...inary-tree-longest-consecutive-sequence.md | 252 ++++++++++++++++++ 1 file changed, 252 insertions(+) create mode 100644 articles/binary-tree-longest-consecutive-sequence.md diff --git a/articles/binary-tree-longest-consecutive-sequence.md b/articles/binary-tree-longest-consecutive-sequence.md new file mode 100644 index 000000000..018635a7d --- /dev/null +++ b/articles/binary-tree-longest-consecutive-sequence.md @@ -0,0 +1,252 @@ +## 1. Top Down Depth-first Search + +::tabs-start + +```python +class Solution: + def longestConsecutive(self, root: Optional[TreeNode]) -> int: + self.max_length = 0 + self.dfs(root, None, 0) + return self.max_length + + def dfs(self, p: TreeNode, parent: TreeNode, length: int) -> None: + if p is None: + return + + length = length + 1 if parent is not None and p.val == parent.val + 1 else 1 + self.max_length = max(self.max_length, length) + + self.dfs(p.left, p, length) + self.dfs(p.right, p, length) +``` + +```java +class Solution { + private int maxLength = 0; + + public int longestConsecutive(TreeNode root) { + dfs(root, null, 0); + return maxLength; + } + + private void dfs(TreeNode p, TreeNode parent, int length) { + if (p == null) return; + length = (parent != null && p.val == parent.val + 1) ? length + 1 : 1; + maxLength = Math.max(maxLength, length); + dfs(p.left, p, length); + dfs(p.right, p, length); + } +} +``` + +```cpp +class Solution { +private: + int maxLength = 0; + + void dfs(TreeNode* p, TreeNode* parent, int length) { + if (p == nullptr) return; + + length = (parent != nullptr && p->val == parent->val + 1) ? length + 1 : 1; + maxLength = max(maxLength, length); + + dfs(p->left, p, length); + dfs(p->right, p, length); + } + +public: + int longestConsecutive(TreeNode* root) { + dfs(root, nullptr, 0); + return maxLength; + } +}; +``` + +```javascript +class Solution { + constructor() { + this.maxLength = 0; + } + + /** + * @param {TreeNode} root + * @return {number} + */ + longestConsecutive(root) { + this.maxLength = 0; + this.dfs(root, null, 0); + return this.maxLength; + } + + /** + * @param {TreeNode} p + * @param {TreeNode} parent + * @param {number} length + * @return {void} + */ + dfs(p, parent, length) { + if (p === null) return; + + length = (parent !== null && p.val === parent.val + 1) ? length + 1 : 1; + this.maxLength = Math.max(this.maxLength, length); + + this.dfs(p.left, p, length); + this.dfs(p.right, p, length); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(n)$ +- Space complexity: $O(n)$ + +> Where $n$ is the number of nodes in the input tree + +--- + +## 2. Bottom Up Depth-first Search + +::tabs-start + +```python +class Solution: + def longestConsecutive(self, root: Optional[TreeNode]) -> int: + self.max_length = 0 + self.dfs(root) + return self.max_length + + def dfs(self, p: Optional[TreeNode]) -> int: + if p is None: + return 0 + + L = self.dfs(p.left) + 1 + R = self.dfs(p.right) + 1 + + if p.left is not None and p.val + 1 != p.left.val: + L = 1 + + if p.right is not None and p.val + 1 != p.right.val: + R = 1 + + length = max(L, R) + self.max_length = max(self.max_length, length) + + return length +``` + +```java +public class Solution { + private int maxLength = 0; + public int longestConsecutive(TreeNode root) { + dfs(root); + return maxLength; + } + + private int dfs(TreeNode p) { + if (p == null) return 0; + + int L = dfs(p.left) + 1; + int R = dfs(p.right) + 1; + + if (p.left != null && p.val + 1 != p.left.val) { + L = 1; + } + + if (p.right != null && p.val + 1 != p.right.val) { + R = 1; + } + + int length = Math.max(L, R); + maxLength = Math.max(maxLength, length); + + return length; + } +} +``` + +```cpp +class Solution { +private: + int maxLength = 0; + + int dfs(TreeNode* p) { + if (p == nullptr) return 0; + + int L = dfs(p->left) + 1; + int R = dfs(p->right) + 1; + + if (p->left != nullptr && p->val + 1 != p->left->val) { + L = 1; + } + + if (p->right != nullptr && p->val + 1 != p->right->val) { + R = 1; + } + + int length = max(L, R); + maxLength = max(maxLength, length); + + return length; + } + +public: + int longestConsecutive(TreeNode* root) { + dfs(root); + return maxLength; + } +}; +``` + +```javascript +class Solution { + constructor() { + this.maxLength = 0; + } + + /** + * @param {TreeNode} root + * @return {number} + */ + longestConsecutive(root) { + this.maxLength = 0; + this.dfs(root); + return this.maxLength; + } + + /** + * @param {TreeNode} p + * @return {number} + */ + dfs(p) { + if (p === null) return 0; + + let L = this.dfs(p.left) + 1; + let R = this.dfs(p.right) + 1; + + if (p.left !== null && p.val + 1 !== p.left.val) { + L = 1; + } + + if (p.right !== null && p.val + 1 !== p.right.val) { + R = 1; + } + + let length = Math.max(L, R); + this.maxLength = Math.max(this.maxLength, length); + + return length; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(n)$ +- Space complexity: $O(n)$ + +> Where $n$ is the number of nodes in the input tree From 604bd6ae4f50062954477b21110b52a29c36d191 Mon Sep 17 00:00:00 2001 From: ranveersingh2718 Date: Sun, 7 Dec 2025 12:56:45 -0800 Subject: [PATCH 02/15] add binary-tree-longest-consecutive-sequence-ii.md article --- ...ry-tree-longest-consecutive-sequence-ii.md | 190 ++++++++++++++++++ 1 file changed, 190 insertions(+) create mode 100644 articles/binary-tree-longest-consecutive-sequence-ii.md diff --git a/articles/binary-tree-longest-consecutive-sequence-ii.md b/articles/binary-tree-longest-consecutive-sequence-ii.md new file mode 100644 index 000000000..db3d66143 --- /dev/null +++ b/articles/binary-tree-longest-consecutive-sequence-ii.md @@ -0,0 +1,190 @@ +## 1. Brute Force (Time Limit Exceeded) + +### Time & Space Complexity + +- Time complexity: $O(n^3)$ +- Space complexity: $O(n^3)$ + +> Where $n$ is the number of nodes in the input tree + +--- + +## 2. Single traversal + +::tabs-start + +```python +class Solution: + def longestConsecutive(self, root: TreeNode) -> int: + + def longest_path(root: TreeNode) -> List[int]: + nonlocal maxval + + if not root: + return [0, 0] + + inr = dcr = 1 + if root.left: + left = longest_path(root.left) + if (root.val == root.left.val + 1): + dcr = left[1] + 1 + elif (root.val == root.left.val - 1): + inr = left[0] + 1 + + if root.right: + right = longest_path(root.right) + if (root.val == root.right.val + 1): + dcr = max(dcr, right[1] + 1) + elif (root.val == root.right.val - 1): + inr = max(inr, right[0] + 1) + + maxval = max(maxval, dcr + inr - 1) + return [inr, dcr] + + maxval = 0 + longest_path(root) + return maxval +``` + +```java +class Solution { + int maxval = 0; + + public int longestConsecutive(TreeNode root) { + longestPath(root); + return maxval; + } + + public int[] longestPath(TreeNode root) { + if (root == null) { + return new int[] {0,0}; + } + + int inr = 1, dcr = 1; + if (root.left != null) { + int[] left = longestPath(root.left); + if (root.val == root.left.val + 1) { + dcr = left[1] + 1; + } else if (root.val == root.left.val - 1) { + inr = left[0] + 1; + } + } + + if (root.right != null) { + int[] right = longestPath(root.right); + if (root.val == root.right.val + 1) { + dcr = Math.max(dcr, right[1] + 1); + } else if (root.val == root.right.val - 1) { + inr = Math.max(inr, right[0] + 1); + } + } + + maxval = Math.max(maxval, dcr + inr - 1); + return new int[] {inr, dcr}; + } +} +``` + +```cpp +class Solution { +private: + int maxval = 0; + + vector longestPath(TreeNode* root) { + if (root == nullptr) { + return {0, 0}; + } + + int inr = 1, dcr = 1; + + if (root->left != nullptr) { + vector left = longestPath(root->left); + if (root->val == root->left->val + 1) { + dcr = left[1] + 1; + } else if (root->val == root->left->val - 1) { + inr = left[0] + 1; + } + } + + if (root->right != nullptr) { + vector right = longestPath(root->right); + if (root->val == root->right->val + 1) { + dcr = max(dcr, right[1] + 1); + } else if (root->val == root->right->val - 1) { + inr = max(inr, right[0] + 1); + } + } + + maxval = max(maxval, dcr + inr - 1); + + return {inr, dcr}; + } + +public: + int longestConsecutive(TreeNode* root) { + longestPath(root); + return maxval; + } +}; +``` + +```javascript +class Solution { + constructor() { + this.maxval = 0; + } + + /** + * @param {TreeNode} root + * @return {number} + */ + longestConsecutive(root) { + this.maxval = 0; + this.longestPath(root); + return this.maxval; + } + + /** + * @param {TreeNode} root + * @return {number[]} + */ + longestPath(root) { + if (root === null) { + return [0, 0]; + } + + let inr = 1, dcr = 1; + + if (root.left !== null) { + let left = this.longestPath(root.left); + if (root.val === root.left.val + 1) { + dcr = left[1] + 1; + } else if (root.val === root.left.val - 1) { + inr = left[0] + 1; + } + } + + if (root.right !== null) { + let right = this.longestPath(root.right); + if (root.val === root.right.val + 1) { + dcr = Math.max(dcr, right[1] + 1); + } else if (root.val === root.right.val - 1) { + inr = Math.max(inr, right[0] + 1); + } + } + + this.maxval = Math.max(this.maxval, dcr + inr - 1); + + return [inr, dcr]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(n)$ +- Space complexity: $O(n)$ + +> Where $n$ is the number of nodes in the input tree From 99c033a4360adeb5209fb8e48737238f72b11d38 Mon Sep 17 00:00:00 2001 From: ranveersingh2718 Date: Sun, 7 Dec 2025 14:14:41 -0800 Subject: [PATCH 03/15] add count-univalue-subtrees.md article --- articles/count-univalue-subtrees.md | 310 ++++++++++++++++++++++++++++ 1 file changed, 310 insertions(+) create mode 100644 articles/count-univalue-subtrees.md diff --git a/articles/count-univalue-subtrees.md b/articles/count-univalue-subtrees.md new file mode 100644 index 000000000..cd40434f6 --- /dev/null +++ b/articles/count-univalue-subtrees.md @@ -0,0 +1,310 @@ +## 1. Depth First Search + +::tabs-start + +```python +class Solution: + def countUnivalSubtrees(self, root: Optional[TreeNode]) -> int: + self.count = 0 + + def dfs(node): + if node is None: + return True + + isLeftUniValue = dfs(node.left) + isRightUniValue = dfs(node.right) + + # If both the children form uni-value subtrees, we compare the value of + # chidrens node with the node value. + if isLeftUniValue and isRightUniValue: + if node.left and node.val != node.left.val: + return False + if node.right and node.val != node.right.val: + return False + + self.count += 1 + return True + # Else if any of the child does not form a uni-value subtree, the subtree + # rooted at node cannot be a uni-value subtree. + return False + + dfs(root) + return self.count +``` + +```java +class Solution { + int count = 0; + + public boolean dfs(TreeNode node) { + if (node == null) { + return true; + } + + boolean left = dfs(node.left); + boolean right = dfs(node.right); + + // If both the children form uni-value subtrees, we compare the value of + // chidren's node with the node value. + if (left && right) { + if (node.left != null && node.left.val != node.val) { + return false; + } + if (node.right != null && node.right.val != node.val) { + return false; + } + count++; + return true; + } + // Else if any of the child does not form a uni-value subtree, the subtree + // rooted at node cannot be a uni-value subtree. + return false; + } + + public int countUnivalSubtrees(TreeNode root) { + dfs(root); + return count; + } +} +``` + +```cpp +class Solution { +public: + int count = 0; + + bool dfs(TreeNode* node) { + if (node == nullptr) { + return true; + } + + bool isLeftUniValue = dfs(node->left); + bool isRightUniValue = dfs(node->right); + + // If both the children form uni-value subtrees, we compare the value of + // chidren's node with the node value. + if (isLeftUniValue && isRightUniValue) { + if (node->left != nullptr && node->left->val != node->val) { + return false; + } + if (node->right != nullptr && node->right->val != node->val) { + return false; + } + count++; + return true; + } + // Else if any of the child does not form a uni-value subtree, the subtree + // rooted at node cannot be a uni-value subtree. + return false; + } + + int countUnivalSubtrees(TreeNode* root) { + dfs(root); + return count; + } +}; +``` + +```javascript +class Solution { + constructor() { + this.count = 0; + } + + /** + * @param {TreeNode} node + * @return {boolean} + */ + dfs(node) { + if (node === null) { + return true; + } + + let left = this.dfs(node.left); + let right = this.dfs(node.right); + + // If both the children form uni-value subtrees, we compare the value of + // children's node with the node value. + if (left && right) { + if (node.left !== null && node.left.val !== node.val) { + return false; + } + if (node.right !== null && node.right.val !== node.val) { + return false; + } + this.count++; + return true; + } + + // Else if any of the child does not form a uni-value subtree, the subtree + // rooted at node cannot be a uni-value subtree. + return false; + } + + /** + * @param {TreeNode} root + * @return {number} + */ + countUnivalSubtrees(root) { + this.count = 0; + this.dfs(root); + return this.count; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(n)$ +- Space complexity: $O(n)$ + +> Where $n$ is the number of nodes in the given binary tree + +--- + +## 2. Depth First Search Without Using The Global Variable + +::tabs-start + +```python +class Solution: + def countUnivalSubtrees(self, root: Optional[TreeNode]) -> int: + def dfs(node, count): + if node is None: + return True + + isLeftUniValue = dfs(node.left, count) + isRightUniValue = dfs(node.right, count) + + # If both the children form uni-value subtrees, we compare the value of + # chidrens node with the node value. + if isLeftUniValue and isRightUniValue: + if node.left and node.val != node.left.val: + return False + if node.right and node.val != node.right.val: + return False + + count[0] += 1 + return True + # Else if any of the child does not form a uni-value subtree, the subtree + # rooted at node cannot be a uni-value subtree. + return False + + count = [0] + dfs(root, count) + return count[0] +``` + +```java +class Solution { + private boolean dfs(TreeNode root, int[] count) { + if (root == null) { + return true; + } + + boolean isLeftUnival = dfs(root.left, count); + boolean isRightUnival = dfs(root.right, count); + + if (isLeftUnival && isRightUnival && + (root.left == null || root.left.val == root.val) && + (root.right == null || root.right.val == root.val) + ) { + count[0]++; + return true; + } + + return false; + } + + public int countUnivalSubtrees(TreeNode root) { + int[] count = new int[1]; + dfs(root, count); + return count[0]; + } +} +``` + +```cpp +class Solution { +public: + bool dfs(TreeNode* node, int& count) { + if (node == nullptr) { + return true; + } + + bool isLeftUniValue = dfs(node->left, count); + bool isRightUniValue = dfs(node->right, count); + + // If both the children form uni-value subtrees, we compare the value of + // chidren's node with the node value. + if (isLeftUniValue && isRightUniValue) { + if (node->left != nullptr && node->left->val != node->val) { + return false; + } + if (node->right != nullptr && node->right->val != node->val) { + return false; + } + count++; + return true; + } + // Else if any of the child does not form a uni-value subtree, the subtree + // rooted at node cannot be a uni-value subtree. + return false; + } + + int countUnivalSubtrees(TreeNode* root) { + int count = 0; + dfs(root, count); + return count; + } +}; +``` + +```javascript +class Solution { + /** + * @param {TreeNode} root + * @param {number[]} count + * @return {boolean} + */ + dfs(root, count) { + if (root === null) { + return true; + } + + let isLeftUnival = this.dfs(root.left, count); + let isRightUnival = this.dfs(root.right, count); + + if (isLeftUnival && isRightUnival && + (root.left === null || root.left.val === root.val) && + (root.right === null || root.right.val === root.val) + ) { + count[0]++; + return true; + } + + return false; + } + + /** + * @param {TreeNode} root + * @return {number} + */ + countUnivalSubtrees(root) { + let count = [0]; + this.dfs(root, count); + return count[0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(n)$ +- Space complexity: $O(n)$ + +> Where $n$ is the number of nodes in the given binary tree From c230bd0835c5b35128be80bc55089ed8ac1148e7 Mon Sep 17 00:00:00 2001 From: ranveersingh2718 Date: Mon, 8 Dec 2025 14:33:22 -0800 Subject: [PATCH 04/15] add maximum-average-subtree.md article --- articles/maximum-average-subtree.md | 175 ++++++++++++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 articles/maximum-average-subtree.md diff --git a/articles/maximum-average-subtree.md b/articles/maximum-average-subtree.md new file mode 100644 index 000000000..2b6abc93e --- /dev/null +++ b/articles/maximum-average-subtree.md @@ -0,0 +1,175 @@ +## 1. Postorder Traversal + +::tabs-start + +```python +class Solution: + # for each node in the tree, we will maintain three values + class State: + def __init__(self, nodes, sum_val, max_average): + # count of nodes in the subtree + self.node_count = nodes + # sum of values in the subtree + self.value_sum = sum_val + # max average found in the subtree + self.max_average = max_average + + def maximumAverageSubtree(self, root: Optional[TreeNode]) -> float: + return self.max_average(root).max_average + + def max_average(self, root): + if root is None: + return self.State(0, 0, 0) + + # postorder traversal, solve for both child nodes first. + left = self.max_average(root.left) + right = self.max_average(root.right) + + # now find nodeCount, valueSum and maxAverage for current node `root` + node_count = left.node_count + right.node_count + 1 + sum_val = left.value_sum + right.value_sum + root.val + max_average = max( + (1.0 * sum_val) / node_count, # average for current node + max(right.max_average, left.max_average) # max average from child nodes + ) + + return self.State(node_count, sum_val, max_average) +``` + +```java +class Solution { + // for each node in the tree, we will maintain three values + class State { + // count of nodes in the subtree + int nodeCount; + + // sum of values in the subtree + int valueSum; + + // max average found in the subtree + double maxAverage; + + State(int nodes, int sum, double maxAverage) { + this.nodeCount = nodes; + this.valueSum = sum; + this.maxAverage = maxAverage; + } + } + + public double maximumAverageSubtree(TreeNode root) { + return maxAverage(root).maxAverage; + } + + State maxAverage(TreeNode root) { + if (root == null) { + return new State(0, 0, 0); + } + + // postorder traversal, solve for both child nodes first. + State left = maxAverage(root.left); + State right = maxAverage(root.right); + + // now find nodeCount, valueSum and maxAverage for current node `root` + int nodeCount = left.nodeCount + right.nodeCount + 1; + int sum = left.valueSum + right.valueSum + root.val; + double maxAverage = Math.max( + (1.0 * (sum)) / nodeCount, // average for current node + Math.max(right.maxAverage, left.maxAverage) // max average from child nodes + ); + + return new State(nodeCount, sum, maxAverage); + } +} +``` + +```cpp +class Solution { +public: + double maximumAverageSubtree(TreeNode* root) { + return maxAverage(root).maxAverage; + } + +private: + struct State { + // count of nodes in the subtree + int nodeCount; + + // sum of values in the subtree + int valueSum; + + // max average found in the subtree + double maxAverage; + }; + + State maxAverage(TreeNode* root) { + if (!root) return {0, 0, 0}; + + // postorder traversal, solve for both child nodes first. + State left = maxAverage(root->left); + State right = maxAverage(root->right); + + // now find nodeCount, valueSum and maxAverage for current node `root` + int nodeCount = left.nodeCount + right.nodeCount + 1; + int sum = left.valueSum + right.valueSum + root->val; + double maxAverage = max( + (1.0 * (sum)) / nodeCount, // average for current node + max(right.maxAverage, left.maxAverage) // max average from child nodes + ); + + return {nodeCount, sum, maxAverage}; + } +}; +``` + +```javascript +// for each node in the tree, we will maintain three values +class State { + constructor(nodes, sum, maxAverage) { + // count of nodes in the subtree + this.nodeCount = nodes; + // sum of values in the subtree + this.valueSum = sum; + // max average found in the subtree + this.maxAverage = maxAverage; + } +} + +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + maximumAverageSubtree(root) { + return this.maxAverage(root).maxAverage; + } + + maxAverage(root) { + if (root === null) { + return new State(0, 0, 0); + } + + // postorder traversal, solve for both child nodes first. + const left = this.maxAverage(root.left); + const right = this.maxAverage(root.right); + + // now find nodeCount, valueSum and maxAverage for current node `root` + const nodeCount = left.nodeCount + right.nodeCount + 1; + const sum = left.valueSum + right.valueSum + root.val; + const maxAverage = Math.max( + (1.0 * sum) / nodeCount, // average for current node + Math.max(right.maxAverage, left.maxAverage) // max average from child nodes + ); + + return new State(nodeCount, sum, maxAverage); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(N)$ +- Space complexity: $O(N)$ + +> Where $N$ is the number of nodes in the tree From 5538e13926de7864c6e190680fa595fa72b9cc8d Mon Sep 17 00:00:00 2001 From: ranveersingh2718 Date: Mon, 8 Dec 2025 16:18:33 -0800 Subject: [PATCH 05/15] add boundary-of-binary-tree.md article --- articles/boundary-of-binary-tree.md | 524 ++++++++++++++++++++++++++++ 1 file changed, 524 insertions(+) create mode 100644 articles/boundary-of-binary-tree.md diff --git a/articles/boundary-of-binary-tree.md b/articles/boundary-of-binary-tree.md new file mode 100644 index 000000000..4a811acdd --- /dev/null +++ b/articles/boundary-of-binary-tree.md @@ -0,0 +1,524 @@ +## 1. Simple Solution + +::tabs-start + +```python +class Solution: + def isLeaf(self, t: Optional[TreeNode]) -> bool: + return t.left is None and t.right is None + + def addLeaves(self, res: List[int], root: Optional[TreeNode]) -> None: + if self.isLeaf(root): + res.append(root.val) + else: + if root.left is not None: + self.addLeaves(res, root.left) + if root.right is not None: + self.addLeaves(res, root.right) + + def boundaryOfBinaryTree(self, root: Optional[TreeNode]) -> List[int]: + res = [] + if root is None: + return res + + if not self.isLeaf(root): + res.append(root.val) + + t = root.left + while t is not None: + if not self.isLeaf(t): + res.append(t.val) + if t.left is not None: + t = t.left + else: + t = t.right + + self.addLeaves(res, root) + + stack = [] + t = root.right + while t is not None: + if not self.isLeaf(t): + stack.append(t.val) + if t.right is not None: + t = t.right + else: + t = t.left + + while stack: + res.append(stack.pop()) + + return res +``` + +```java +class Solution { + public boolean isLeaf(TreeNode t) { + return t.left == null && t.right == null; + } + + public void addLeaves(List res, TreeNode root) { + if (isLeaf(root)) { + res.add(root.val); + } else { + if (root.left != null) { + addLeaves(res, root.left); + } + if (root.right != null) { + addLeaves(res, root.right); + } + } + } + + public List boundaryOfBinaryTree(TreeNode root) { + ArrayList res = new ArrayList<>(); + + if (root == null) { + return res; + } + + if (!isLeaf(root)) { + res.add(root.val); + } + + TreeNode t = root.left; + while (t != null) { + if (!isLeaf(t)) { + res.add(t.val); + } + if (t.left != null) { + t = t.left; + } else { + t = t.right; + } + + } + + addLeaves(res, root); + + Stack s = new Stack<>(); + t = root.right; + while (t != null) { + if (!isLeaf(t)) { + s.push(t.val); + } + if (t.right != null) { + t = t.right; + } else { + t = t.left; + } + } + while (!s.empty()) { + res.add(s.pop()); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + bool isLeaf(TreeNode* t) { + return t->left == nullptr && t->right == nullptr; + } + + void addLeaves(vector& res, TreeNode* root) { + if (isLeaf(root)) { + res.push_back(root->val); + } else { + if (root->left != nullptr) { + addLeaves(res, root->left); + } + if (root->right != nullptr) { + addLeaves(res, root->right); + } + } + } + + vector boundaryOfBinaryTree(TreeNode* root) { + vector res; + if (root == nullptr) { + return res; + } + + if (!isLeaf(root)) { + res.push_back(root->val); + } + + TreeNode* t = root->left; + while (t != nullptr) { + if (!isLeaf(t)) { + res.push_back(t->val); + } + if (t->left != nullptr) { + t = t->left; + } else { + t = t->right; + } + } + + addLeaves(res, root); + + stack s; + t = root->right; + while (t != nullptr) { + if (!isLeaf(t)) { + s.push(t->val); + } + if (t->right != nullptr) { + t = t->right; + } else { + t = t->left; + } + } + + while (!s.empty()) { + res.push_back(s.top()); + s.pop(); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {TreeNode} root + * @return {number[]} + */ + isLeaf(t) { + return t.left === null && t.right === null; + } + + addLeaves(res, root) { + if (this.isLeaf(root)) { + res.push(root.val); + } else { + if (root.left !== null) { + this.addLeaves(res, root.left); + } + if (root.right !== null) { + this.addLeaves(res, root.right); + } + } + } + + boundaryOfBinaryTree(root) { + const res = []; + if (root === null) { + return res; + } + + if (!this.isLeaf(root)) { + res.push(root.val); + } + + let t = root.left; + while (t !== null) { + if (!this.isLeaf(t)) { + res.push(t.val); + } + if (t.left !== null) { + t = t.left; + } else { + t = t.right; + } + } + + this.addLeaves(res, root); + + const stack = []; + t = root.right; + while (t !== null) { + if (!this.isLeaf(t)) { + stack.push(t.val); + } + if (t.right !== null) { + t = t.right; + } else { + t = t.left; + } + } + + while (stack.length > 0) { + res.push(stack.pop()); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(n)$ +- Space complexity: $O(n)$ + +> Where $n$ is the number of nodes in the tree + +--- + +## 2. Using PreOrder Traversal + +::tabs-start + +```python +class Solution: + def boundaryOfBinaryTree(self, root: Optional[TreeNode]) -> List[int]: + left_boundary, right_boundary, leaves = [], [], [] + self.preorder(root, left_boundary, right_boundary, leaves, 0) + left_boundary.extend(leaves) + left_boundary.extend(right_boundary) + return left_boundary + + def is_leaf(self, cur): + return cur.left is None and cur.right is None + + def is_right_boundary(self, flag): + return flag == 2 + + def is_left_boundary(self, flag): + return flag == 1 + + def is_root(self, flag): + return flag == 0 + + def left_child_flag(self, cur, flag): + if self.is_left_boundary(flag) or self.is_root(flag): + return 1 + elif self.is_right_boundary(flag) and cur.right is None: + return 2 + else: + return 3 + + def right_child_flag(self, cur, flag): + if self.is_right_boundary(flag) or self.is_root(flag): + return 2 + elif self.is_left_boundary(flag) and cur.left is None: + return 1 + else: + return 3 + + def preorder(self, cur, left_boundary, right_boundary, leaves, flag): + if cur is None: + return + + if self.is_right_boundary(flag): + right_boundary.insert(0, cur.val) + elif self.is_left_boundary(flag) or self.is_root(flag): + left_boundary.append(cur.val) + elif self.is_leaf(cur): + leaves.append(cur.val) + + self.preorder(cur.left, left_boundary, right_boundary, leaves, self.left_child_flag(cur, flag)) + self.preorder(cur.right, left_boundary, right_boundary, leaves, self.right_child_flag(cur, flag)) +``` + +```java +class Solution { + public List < Integer > boundaryOfBinaryTree(TreeNode root) { + List < Integer > left_boundary = new LinkedList < > (), right_boundary = new LinkedList < > (), leaves = new LinkedList < > (); + preorder(root, left_boundary, right_boundary, leaves, 0); + left_boundary.addAll(leaves); + left_boundary.addAll(right_boundary); + return left_boundary; + } + + public boolean isLeaf(TreeNode cur) { + return (cur.left == null && cur.right == null); + } + + public boolean isRightBoundary(int flag) { + return (flag == 2); + } + + public boolean isLeftBoundary(int flag) { + return (flag == 1); + } + + public boolean isRoot(int flag) { + return (flag == 0); + } + + public int leftChildFlag(TreeNode cur, int flag) { + if (isLeftBoundary(flag) || isRoot(flag)) + return 1; + else if (isRightBoundary(flag) && cur.right == null) + return 2; + else return 3; + } + + public int rightChildFlag(TreeNode cur, int flag) { + if (isRightBoundary(flag) || isRoot(flag)) + return 2; + else if (isLeftBoundary(flag) && cur.left == null) + return 1; + else return 3; + } + + public void preorder(TreeNode cur, List < Integer > left_boundary, List < Integer > right_boundary, List < Integer > leaves, int flag) { + if (cur == null) + return; + + if (isRightBoundary(flag)) + right_boundary.add(0, cur.val); + else if (isLeftBoundary(flag) || isRoot(flag)) + left_boundary.add(cur.val); + else if (isLeaf(cur)) + leaves.add(cur.val); + + preorder(cur.left, left_boundary, right_boundary, leaves, leftChildFlag(cur, flag)); + preorder(cur.right, left_boundary, right_boundary, leaves, rightChildFlag(cur, flag)); + } +} +``` + +```cpp +class Solution { +public: + vector boundaryOfBinaryTree(TreeNode* root) { + vector left_boundary, right_boundary, leaves; + preorder(root, left_boundary, right_boundary, leaves, 0); + left_boundary.insert(left_boundary.end(), leaves.begin(), leaves.end()); + left_boundary.insert(left_boundary.end(), right_boundary.begin(), right_boundary.end()); + return left_boundary; + } + +private: + bool isLeaf(TreeNode* cur) { + return cur->left == nullptr && cur->right == nullptr; + } + + bool isRightBoundary(int flag) { + return flag == 2; + } + + bool isLeftBoundary(int flag) { + return flag == 1; + } + + bool isRoot(int flag) { + return flag == 0; + } + + int leftChildFlag(TreeNode* cur, int flag) { + if (isLeftBoundary(flag) || isRoot(flag)) { + return 1; + } else if (isRightBoundary(flag) && cur->right == nullptr) { + return 2; + } else { + return 3; + } + } + + int rightChildFlag(TreeNode* cur, int flag) { + if (isRightBoundary(flag) || isRoot(flag)) { + return 2; + } else if (isLeftBoundary(flag) && cur->left == nullptr) { + return 1; + } else { + return 3; + } + } + + void preorder(TreeNode* cur, vector& left_boundary, vector& right_boundary, vector& leaves, int flag) { + if (cur == nullptr) { + return; + } + + if (isRightBoundary(flag)) { + right_boundary.insert(right_boundary.begin(), cur->val); + } else if (isLeftBoundary(flag) || isRoot(flag)) { + left_boundary.push_back(cur->val); + } else if (isLeaf(cur)) { + leaves.push_back(cur->val); + } + + preorder(cur->left, left_boundary, right_boundary, leaves, leftChildFlag(cur, flag)); + preorder(cur->right, left_boundary, right_boundary, leaves, rightChildFlag(cur, flag)); + } +}; +``` + +```javascript +class Solution { + /** + * @param {TreeNode} root + * @return {number[]} + */ + boundaryOfBinaryTree(root) { + const left_boundary = [], right_boundary = [], leaves = []; + this.preorder(root, left_boundary, right_boundary, leaves, 0); + left_boundary.push(...leaves); + left_boundary.push(...right_boundary); + return left_boundary; + } + + isLeaf(cur) { + return cur.left === null && cur.right === null; + } + + isRightBoundary(flag) { + return flag === 2; + } + + isLeftBoundary(flag) { + return flag === 1; + } + + isRoot(flag) { + return flag === 0; + } + + leftChildFlag(cur, flag) { + if (this.isLeftBoundary(flag) || this.isRoot(flag)) { + return 1; + } else if (this.isRightBoundary(flag) && cur.right === null) { + return 2; + } else { + return 3; + } + } + + rightChildFlag(cur, flag) { + if (this.isRightBoundary(flag) || this.isRoot(flag)) { + return 2; + } else if (this.isLeftBoundary(flag) && cur.left === null) { + return 1; + } else { + return 3; + } + } + + preorder(cur, left_boundary, right_boundary, leaves, flag) { + if (cur === null) { + return; + } + + if (this.isRightBoundary(flag)) { + right_boundary.unshift(cur.val); + } else if (this.isLeftBoundary(flag) || this.isRoot(flag)) { + left_boundary.push(cur.val); + } else if (this.isLeaf(cur)) { + leaves.push(cur.val); + } + + this.preorder(cur.left, left_boundary, right_boundary, leaves, this.leftChildFlag(cur, flag)); + this.preorder(cur.right, left_boundary, right_boundary, leaves, this.rightChildFlag(cur, flag)); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(n)$ +- Space complexity: $O(n)$ + +> Where $n$ is the number of nodes in the tree From e1561c5371e965986e065316db763084a91a5e2c Mon Sep 17 00:00:00 2001 From: ranveersingh2718 Date: Mon, 8 Dec 2025 23:17:28 -0800 Subject: [PATCH 06/15] add find-leaves-of-binary-tree.md article --- articles/find-leaves-of-binary-tree.md | 346 +++++++++++++++++++++++++ 1 file changed, 346 insertions(+) create mode 100644 articles/find-leaves-of-binary-tree.md diff --git a/articles/find-leaves-of-binary-tree.md b/articles/find-leaves-of-binary-tree.md new file mode 100644 index 000000000..1d6aa13ba --- /dev/null +++ b/articles/find-leaves-of-binary-tree.md @@ -0,0 +1,346 @@ +## 1. DFS (Depth-First Search) with sorting + +::tabs-start + +```python +class Solution: + def findLeaves(self, root: Optional[TreeNode]) -> List[List[int]]: + self.pairs = [] + + def getHeight(node): + if node is None: + return -1 + + # first calculate the height of the left and right children + leftHeight = getHeight(node.left) + rightHeight = getHeight(node.right) + + # based on the height of the left and right children, obtain the height of the current (parent) node + currHeight = max(leftHeight, rightHeight) + 1 + + # collect the pair -> (height, val) + self.pairs.append((currHeight, node.val)) + + # return the height of the current node + return currHeight + + getHeight(root) + + # sort all the (height, val) pairs + self.pairs.sort(key=lambda p: p[0]) + + n = len(self.pairs) + height = 0 + i = 0 + solution = [] + + while i < n: + nums = [] + while i < n and self.pairs[i][0] == height: + nums.append(self.pairs[i][1]) + i += 1 + solution.append(nums) + height += 1 + + return solution +``` + +```java +class Solution { + private List> pairs; + + private int getHeight(TreeNode root) { + if (root == null) return -1; + + // first calculate the height of the left and right children + int leftHeight = getHeight(root.left); + int rightHeight = getHeight(root.right); + + // based on the height of the left and right children, obtain the height of the current (parent) node + int currHeight = Math.max(leftHeight, rightHeight) + 1; + + // collect the pair -> (height, val) + this.pairs.add(new Pair(currHeight, root.val)); + + // return the height of the current node + return currHeight; + } + + public List> findLeaves(TreeNode root) { + this.pairs = new ArrayList<>(); + + getHeight(root); + + // sort all the (height, val) pairs + Collections.sort(this.pairs, Comparator.comparing(p -> p.getKey())); + + int n = this.pairs.size(), height = 0, i = 0; + + List> solution = new ArrayList<>(); + + while (i < n) { + List nums = new ArrayList<>(); + while (i < n && this.pairs.get(i).getKey() == height) { + nums.add(this.pairs.get(i).getValue()); + i++; + } + solution.add(nums); + height++; + } + return solution; + } +} +``` + +```cpp +class Solution { +public: + + vector> pairs; + + int getHeight(TreeNode *root) { + + // return -1 for null nodes + if (!root) return -1; + + // first calculate the height of the left and right children + int leftHeight = getHeight(root->left); + int rightHeight = getHeight(root->right); + + // based on the height of the left and right children, obtain the height of the current (parent) node + int currHeight = max(leftHeight, rightHeight) + 1; + + // collect the pair -> (height, val) + this->pairs.push_back({currHeight, root->val}); + + // return the height of the current node + return currHeight; + } + + vector> findLeaves(TreeNode* root) { + this->pairs.clear(); + + getHeight(root); + + // sort all the (height, val) pairs + sort(this->pairs.begin(), this->pairs.end()); + + int n = this->pairs.size(), height = 0, i = 0; + vector> solution; + while (i < n) { + vector nums; + while (i < n && this->pairs[i].first == height) { + nums.push_back(this->pairs[i].second); + i++; + } + solution.push_back(nums); + height++; + } + return solution; + } +}; +``` + +```javascript +class Solution { + /** + * @param {TreeNode} root + * @return {number[][]} + */ + findLeaves(root) { + this.pairs = []; + + const getHeight = (node) => { + if (node === null) { + return -1; + } + + // first calculate the height of the left and right children + const leftHeight = getHeight(node.left); + const rightHeight = getHeight(node.right); + + // based on the height of the left and right children, obtain the height of the current (parent) node + const currHeight = Math.max(leftHeight, rightHeight) + 1; + + // collect the pair -> (height, val) + this.pairs.push([currHeight, node.val]); + + // return the height of the current node + return currHeight; + }; + + getHeight(root); + + // sort all the (height, val) pairs + this.pairs.sort((a, b) => a[0] - b[0]); + + const n = this.pairs.length; + let height = 0; + let i = 0; + const solution = []; + + while (i < n) { + const nums = []; + while (i < n && this.pairs[i][0] === height) { + nums.push(this.pairs[i][1]); + i++; + } + solution.push(nums); + height++; + } + + return solution; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(N \log N)$ +- Space complexity: $O(N)$ + +> Where $N$ is the total number of nodes in the binary tree + +--- + +## 2. DFS (Depth-First Search) without sorting + +::tabs-start + +```python +class Solution: + def findLeaves(self, root: Optional[TreeNode]) -> List[List[int]]: + self.solution = [] + + def getHeight(node): + if node is None: + return -1 + + leftHeight = getHeight(node.left) + rightHeight = getHeight(node.right) + currHeight = max(leftHeight, rightHeight) + 1 + + if len(self.solution) == currHeight: + self.solution.append([]) + + self.solution[currHeight].append(node.val) + return currHeight + + getHeight(root) + return self.solution +``` + +```java +class Solution { + + private List> solution; + + private int getHeight(TreeNode root) { + if (root == null) { + return -1; + } + + int leftHeight = getHeight(root.left); + int rightHeight = getHeight(root.right); + + int currHeight = Math.max(leftHeight, rightHeight) + 1; + + if (this.solution.size() == currHeight) { + this.solution.add(new ArrayList<>()); + } + + this.solution.get(currHeight).add(root.val); + + return currHeight; + } + + public List> findLeaves(TreeNode root) { + this.solution = new ArrayList<>(); + + getHeight(root); + + return this.solution; + } +} +``` + +```cpp +class Solution { +private: + + vector> solution; + +public: + + int getHeight(TreeNode *root) { + + if (!root) { + return -1; + } + + int leftHeight = getHeight(root->left); + int rightHeight = getHeight(root->right); + + int currHeight = max(leftHeight, rightHeight) + 1; + + if (this->solution.size() == currHeight) { + this->solution.push_back({}); + } + + this->solution[currHeight].push_back(root->val); + + return currHeight; + } + + vector> findLeaves(TreeNode* root) { + this->solution.clear(); + + getHeight(root); + + return this->solution; + } +}; +``` + +```javascript +class Solution { + /** + * @param {TreeNode} root + * @return {number[][]} + */ + findLeaves(root) { + this.solution = []; + + const getHeight = (node) => { + if (node === null) { + return -1; + } + + const leftHeight = getHeight(node.left); + const rightHeight = getHeight(node.right); + const currHeight = Math.max(leftHeight, rightHeight) + 1; + + if (this.solution.length === currHeight) { + this.solution.push([]); + } + + this.solution[currHeight].push(node.val); + return currHeight; + }; + + getHeight(root); + return this.solution; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(N)$ +- Space complexity: $O(N)$ + +> Where $N$ is the total number of nodes in the binary tree From 1eadbf96f9cb469c9045e05581bf3b85e1cbd3f0 Mon Sep 17 00:00:00 2001 From: ranveersingh2718 Date: Tue, 9 Dec 2025 00:13:41 -0800 Subject: [PATCH 07/15] add closest-binary-search-tree-value.md article --- articles/closest-binary-search-tree-value.md | 186 +++++++++++++++++++ 1 file changed, 186 insertions(+) create mode 100644 articles/closest-binary-search-tree-value.md diff --git a/articles/closest-binary-search-tree-value.md b/articles/closest-binary-search-tree-value.md new file mode 100644 index 000000000..ef049bc93 --- /dev/null +++ b/articles/closest-binary-search-tree-value.md @@ -0,0 +1,186 @@ +## 1. Recursive Inorder + Linear search, O(N) time + +::tabs-start + +```python +class Solution: + def closestValue(self, root: TreeNode, target: float) -> int: + def inorder(r: TreeNode): + return inorder(r.left) + [r.val] + inorder(r.right) if r else [] + + return min(inorder(root), key = lambda x: abs(target - x)) +``` + +```java +class Solution { + public void inorder(TreeNode root, List nums) { + if (root == null) return; + inorder(root.left, nums); + nums.add(root.val); + inorder(root.right, nums); + } + + public int closestValue(TreeNode root, double target) { + List nums = new ArrayList(); + inorder(root, nums); + return Collections.min(nums, new Comparator() { + @Override + public int compare(Integer o1, Integer o2) { + return Math.abs(o1 - target) < Math.abs(o2 - target) ? -1 : 1; + } + }); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(N)$ +- Space complexity: $O(N)$ + +> Where $N$ is the total number of nodes in the binary tree + +--- + +## 2. Iterative Inorder, O(k) time + +::tabs-start + +```python +class Solution: + def closestValue(self, root: TreeNode, target: float) -> int: + stack, pred = [], float('-inf') + + while stack or root: + while root: + stack.append(root) + root = root.left + root = stack.pop() + + if pred <= target and target < root.val: + return min(pred, root.val, key = lambda x: abs(target - x)) + + pred = root.val + root = root.right + + return pred +``` + +```java +class Solution { + public int closestValue(TreeNode root, double target) { + LinkedList stack = new LinkedList(); + long pred = Long.MIN_VALUE; + + while (!stack.isEmpty() || root != null) { + while (root != null) { + stack.add(root); + root = root.left; + } + root = stack.removeLast(); + + if (pred <= target && target < root.val) + return Math.abs(pred - target) <= Math.abs(root.val - target) ? (int) pred : root.val; + + pred = root.val; + root = root.right; + } + return (int) pred; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(k)$ in the average case and $O(H+k)$ in the worst case, +- Space complexity: $O(H)$ + +> Where $k$ is an index of the closest element and $H$ is the height of the tree. + +--- + +## 3. Binary Search, O(H) time + +::tabs-start + +```python +class Solution: + def closestValue(self, root: TreeNode, target: float) -> int: + closest = root.val + + while root: + closest = min(root.val, closest, key = lambda x: (abs(target - x), x)) + root = root.left if target < root.val else root.right + + return closest +``` + +```java +class Solution { + public int closestValue(TreeNode root, double target) { + int val, closest = root.val; + + while (root != null) { + val = root.val; + closest = Math.abs(val - target) < Math.abs(closest - target) + || (Math.abs(val - target) == Math.abs(closest - target) && val < closest) ? val : closest; + root = target < root.val ? root.left : root.right; + } + + return closest; + } +} +``` + +```cpp +class Solution { +public: + int closestValue(TreeNode* root, double target) { + int val, closest = root->val; + + while (root != nullptr) { + val = root->val; + closest = abs(val - target) < abs(closest - target) + || (abs(val - target) == abs(closest - target) && val < closest) ? val : closest; + root = target < root->val ? root->left : root->right; + } + + return closest; + } +}; +``` + +```javascript +class Solution { + /** + * @param {TreeNode} root + * @param {number} target + * @return {number} + */ + closestValue(root, target) { + let val, closest = root.val; + + while (root !== null) { + val = root.val; + closest = Math.abs(val - target) < Math.abs(closest - target) + || (Math.abs(val - target) === Math.abs(closest - target) && val < closest) ? val : closest; + root = target < root.val ? root.left : root.right; + } + + return closest; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(H)$ +- Space complexity: $O(1)$ + +> Where $H$ is the height of the tree. From 317cfb9fc0137df429a0c93422c6fb1193715727 Mon Sep 17 00:00:00 2001 From: ranveersingh2718 Date: Wed, 10 Dec 2025 16:16:13 -0800 Subject: [PATCH 08/15] add closest-binary-search-tree-value-ii.md article --- .../closest-binary-search-tree-value-ii.md | 432 ++++++++++++++++++ 1 file changed, 432 insertions(+) create mode 100644 articles/closest-binary-search-tree-value-ii.md diff --git a/articles/closest-binary-search-tree-value-ii.md b/articles/closest-binary-search-tree-value-ii.md new file mode 100644 index 000000000..b5fa1fac6 --- /dev/null +++ b/articles/closest-binary-search-tree-value-ii.md @@ -0,0 +1,432 @@ +## 1. Sort With Custom Comparator + +::tabs-start + +```python +class Solution: + def closestKValues(self, root: TreeNode, target: float, k: int) -> List[int]: + def dfs(node, arr): + if not node: + return + + arr.append(node.val) + dfs(node.left, arr) + dfs(node.right, arr) + + arr = [] + dfs(root, arr) + + arr.sort(key = lambda x: (abs(x - target), x)) + return arr[:k] +``` + +```java +class Solution { + public List closestKValues(TreeNode root, double target, int k) { + List arr = new ArrayList<>(); + dfs(root, arr); + + Collections.sort(arr, (o1, o2) -> Math.abs(o1 - target) <= Math.abs(o2 - target) ? -1 : 1); + + return arr.subList(0, k); + + } + + public void dfs(TreeNode node, List arr) { + if (node == null) { + return; + } + + arr.add(node.val); + dfs(node.left, arr); + dfs(node.right, arr); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(n \cdot \log n)$ +- Space complexity: $O(n)$ + +> Where $n$ is the number of nodes in the tree + +--- + +## 2. Traverse With Heap + +::tabs-start + +```python +class Solution: + def closestKValues(self, root: TreeNode, target: float, k: int) -> List[int]: + def dfs(node, heap): + if not node: + return + + if len(heap) < k: + heappush(heap, (-abs(node.val - target), node.val)) + else: + if abs(node.val - target) <= abs(heap[0][0]): + heappop(heap) + heappush(heap, (-abs(node.val - target), node.val)) + + dfs(node.left, heap) + dfs(node.right, heap) + + heap = [] + dfs(root, heap) + return [x[1] for x in heap] +``` + +```java +class Solution { + public List closestKValues(TreeNode root, double target, int k) { + Queue heap = new PriorityQueue<>((a, b) -> Math.abs(a - target) > Math.abs(b - target) ? -1: 1); + dfs(root, heap, k); + + return new ArrayList<>(heap); + } + + public void dfs(TreeNode node, Queue heap, int k) { + if (node == null) { + return; + } + + heap.add(node.val); + if (heap.size() > k) { + heap.remove(); + } + + dfs(node.left, heap, k); + dfs(node.right, heap, k); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(n \cdot \log k)$ +- Space complexity: $O(n+k)$ + +> Where $n$ is the number of nodes in the tree and $k$ is the size of our heap + +--- + +## 3. Inorder Traversal + Sliding Window + +::tabs-start + +```python +class Solution: + def closestKValues(self, root: TreeNode, target: float, k: int) -> List[int]: + def dfs(node, arr): + if not node: + return + + dfs(node.left, arr) + arr.append(node.val) + dfs(node.right, arr) + + arr = [] + dfs(root, arr) + + left = bisect_left(arr, target) - 1 + right = left + 1 + ans = [] + + while len(ans) < k: + if right == len(arr) or abs(arr[left] - target) <= abs(arr[right] - target): + ans.append(arr[left]) + left -= 1 + else: + ans.append(arr[right]) + right += 1 + + return ans +``` + +```java +class Solution { + public List closestKValues(TreeNode root, double target, int k) { + List arr = new ArrayList<>(); + dfs(root, arr); + + int start = 0; + double minDiff = Double.MAX_VALUE; + + for (int i = 0; i < arr.size(); i++) { + if (Math.abs(arr.get(i) - target) < minDiff) { + minDiff = Math.abs(arr.get(i) - target); + start = i; + } + } + + int left = start; + int right = start + 1; + + while (right - left - 1 < k) { + // Be careful to not go out of bounds + if (left < 0) { + right += 1; + continue; + } + + if (right == arr.size() || Math.abs(arr.get(left) - target) <= Math.abs(arr.get(right) - target)) { + left -= 1; + } else { + right += 1; + } + } + + return arr.subList(left + 1, right); + } + + public void dfs(TreeNode node, List arr) { + if (node == null) { + return; + } + + dfs(node.left, arr); + arr.add(node.val); + dfs(node.right, arr); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(n+k)$ +- Space complexity: $O(n)$ + +> Where $n$ is the number of nodes in the tree and $k$ is the size of our sliding window + +--- + +## 4. Binary Search The Left Bound + +::tabs-start + +```python +class Solution: + def closestKValues(self, root: TreeNode, target: float, k: int) -> List[int]: + def dfs(node, arr): + if not node: + return + + dfs(node.left, arr) + arr.append(node.val) + dfs(node.right, arr) + + arr = [] + dfs(root, arr) + + left = 0 + right = len(arr) - k + + while left < right: + mid = (left + right) // 2 + if abs(target - arr[mid + k]) < abs(target - arr[mid]): + left = mid + 1 + else: + right = mid + + return arr[left:left + k] +``` + +```java +class Solution { + public List closestKValues(TreeNode root, double target, int k) { + List arr = new ArrayList<>(); + dfs(root, arr); + + int left = 0; + int right = arr.size() - k; + + while (left < right) { + int mid = (left + right) / 2; + if (Math.abs(target - arr.get(mid + k)) < Math.abs(target - arr.get(mid))) { + left = mid + 1; + } else { + right = mid; + } + } + + return arr.subList(left, left + k); + } + + public void dfs(TreeNode node, List arr) { + if (node == null) { + return; + } + + dfs(node.left, arr); + arr.add(node.val); + dfs(node.right, arr); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: + - $O(n)$ in Java + - $O(n+k)$ in Python +- Space complexity: $O(n)$ + +> Where $n$ is the number of nodes in the tree and $k$ is the number of closest values to return + +--- + +## 5. Build The Window With Deque + +::tabs-start + +```python +class Solution: + def closestKValues(self, root: TreeNode, target: float, k: int) -> List[int]: + def dfs(node, queue): + if not node: + return + + dfs(node.left, queue) + queue.append(node.val) + if len(queue) > k: + if (abs(target - queue[0]) <= abs(target - queue[-1])): + queue.pop() + return + else: + queue.popleft() + + dfs(node.right, queue) + + queue = deque() + dfs(root, queue) + return list(queue) +``` + +```java +class Solution { + public List closestKValues(TreeNode root, double target, int k) { + Deque queue = new LinkedList<>(); + dfs(root, queue, k, target); + return new ArrayList<>(queue); + } + + public void dfs(TreeNode node, Deque queue, int k, double target) { + if (node == null) { + return; + } + + dfs(node.left, queue, k, target); + queue.add(node.val); + if (queue.size() > k) { + if (Math.abs(target - queue.peekFirst()) <= Math.abs(target - queue.peekLast())) { + queue.removeLast(); + return; + } else { + queue.removeFirst(); + } + } + + dfs(node.right, queue, k, target); + } +} +``` + +```cpp +class Solution { +public: + vector closestKValues(TreeNode* root, double target, int k) { + deque queue; + dfs(root, queue, k, target); + return vector(queue.begin(), queue.end()); + } + +private: + void dfs(TreeNode* node, deque& queue, int k, double target) { + if (node == nullptr) { + return; + } + + dfs(node->left, queue, k, target); + + queue.push_back(node->val); + if (queue.size() > k) { + if (abs(target - queue.front()) <= abs(target - queue.back())) { + queue.pop_back(); + return; + } else { + queue.pop_front(); + } + } + + dfs(node->right, queue, k, target); + } +}; +``` + +```javascript +class Solution { + /** + * @param {TreeNode} root + * @param {number} target + * @param {number} k + * @return {number[]} + */ + closestKValues(root, target, k) { + const queue = new Deque(); + this.dfs(root, queue, k, target); + + return queue.toArray(); + } + + /** + * @param {TreeNode} node + * @param {Deque} queue + * @param {number} k + * @param {number} target + * @return {void} + */ + dfs(node, queue, k, target) { + if (node === null) { + return; + } + + this.dfs(node.left, queue, k, target); + + queue.pushBack(node.val); + + if (queue.size() > k) { + const first = queue.front(); + const last = queue.back(); + + if (Math.abs(target - first) <= Math.abs(target - last)) { + queue.popBack(); + return; + } else { + queue.popFront(); + } + } + + this.dfs(node.right, queue, k, target); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(n)$ +- Space complexity: $O(n+k)$ + +> Where $n$ is the number of nodes in the tree and $k$ is the number of closest values to return From 59e7a32daa711fbbfae2dbc0c89fa85e0bd6edcc Mon Sep 17 00:00:00 2001 From: ranveersingh2718 Date: Wed, 10 Dec 2025 19:07:01 -0800 Subject: [PATCH 09/15] add verify-preorder-sequence-in-binary-search-tree.md --- ...preorder-sequence-in-binary-search-tree.md | 283 ++++++++++++++++++ 1 file changed, 283 insertions(+) create mode 100644 articles/verify-preorder-sequence-in-binary-search-tree.md diff --git a/articles/verify-preorder-sequence-in-binary-search-tree.md b/articles/verify-preorder-sequence-in-binary-search-tree.md new file mode 100644 index 000000000..965feece0 --- /dev/null +++ b/articles/verify-preorder-sequence-in-binary-search-tree.md @@ -0,0 +1,283 @@ +## 1. Monotonic Stack + +::tabs-start + +```python +class Solution: + def verifyPreorder(self, preorder: List[int]) -> bool: + min_limit = float('-inf') + stack = [] + + for num in preorder: + while stack and stack[-1] < num: + min_limit = stack.pop() + + if num <= min_limit: + return False + + stack.append(num) + + return True +``` + +```java +class Solution { + public boolean verifyPreorder(int[] preorder) { + int minLimit = Integer.MIN_VALUE; + Stack stack = new Stack<>(); + + for (int num: preorder) { + while (!stack.isEmpty() && stack.peek() < num) { + minLimit = stack.pop(); + } + + if (num <= minLimit) { + return false; + } + + stack.push(num); + } + + return true; + } +} +``` + +```cpp +class Solution { +public: + bool verifyPreorder(vector& preorder) { + int minLimit = INT_MIN; + stack stack; + + for (int num: preorder) { + while (!stack.empty() && stack.top() < num) { + minLimit = stack.top(); + stack.pop(); + } + + if (num <= minLimit) { + return false; + } + + stack.push(num); + } + + return true; + } +}; +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(n)$ +- Space complexity: $O(n)$ + +> Where $n$ is the length of `preorder` + +--- + +## 2. Constant Auxiliary Space + +::tabs-start + +```python +class Solution: + def verifyPreorder(self, preorder: List[int]) -> bool: + min_limit = float('-inf') + i = 0 + + for num in preorder: + while i > 0 and preorder[i - 1] < num: + min_limit = preorder[i - 1] + i -= 1 + + if num <= min_limit: + return False + + preorder[i] = num + i += 1 + + return True +``` + +```java +class Solution { + public boolean verifyPreorder(int[] preorder) { + int minLimit = Integer.MIN_VALUE; + int i = 0; + + for (int num: preorder) { + while (i > 0 && preorder[i - 1] < num) { + minLimit = preorder[i - 1]; + i--; + } + + if (num <= minLimit) { + return false; + } + + preorder[i] = num; + i++; + } + + return true; + } +} +``` + +```cpp +class Solution { +public: + bool verifyPreorder(vector& preorder) { + int minLimit = INT_MIN; + int i = 0; + + for (int num: preorder) { + while (i > 0 && preorder[i - 1] < num) { + minLimit = preorder[i - 1]; + i--; + } + + if (num <= minLimit) { + return false; + } + + preorder[i] = num; + i++; + } + + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} preorder + * @return {boolean} + */ + verifyPreorder(preorder) { + let minLimit = -Infinity; + let i = 0; + + for (let num of preorder) { + while (i > 0 && preorder[i - 1] < num) { + minLimit = preorder[i - 1]; + i--; + } + + if (num <= minLimit) { + return false; + } + + preorder[i] = num; + i++; + } + + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(n)$ +- Space complexity: $O(1)$ auxiliary + + - A common misconception is that modifying an input array for use in an algorithm leads to an $O(1)$ space complexity. In reality, you are still using $O(n)$ space, but $O(1)$ **auxiliary** space. + + - Because we are modifying the input to directly use in the algorithm, we must count it as part of the space complexity. However, we are not using any auxiliary space other than a few integers. + The exception to this is in-place algorithms where the input is also returned as the output. For example: sorting algorithms. + +> Where $n$ is the length of `preorder` + +--- + +## 3. Recursion + +::tabs-start + +```python +class Solution: + def verifyPreorder(self, preorder: List[int]) -> bool: + def helper(i, min_limit, max_limit): + if i[0] == len(preorder): + return True + + root = preorder[i[0]] + if not min_limit < root < max_limit: + return False + + i[0] += 1 + left = helper(i, min_limit, root) + right = helper(i, root, max_limit) + return left or right + + return helper([0], float('-inf'), float('inf')) +``` + +```java +class Solution { + public boolean verifyPreorder(int[] preorder) { + int[] i = {0}; + return helper(preorder, i, Integer.MIN_VALUE, Integer.MAX_VALUE); + } + + public boolean helper(int[] preorder, int[] i, int minLimit, int maxLimit) { + if (i[0] == preorder.length) { + return true; + } + + int root = preorder[i[0]]; + if (root <= minLimit || root >= maxLimit) { + return false; + } + + i[0]++; + boolean left = helper(preorder, i, minLimit, root); + boolean right = helper(preorder, i, root, maxLimit); + return left || right; + } +} +``` + +```cpp +class Solution { +public: + bool verifyPreorder(vector& preorder) { + int i = 0; + return helper(preorder, i, INT_MIN, INT_MAX); + } + + bool helper(vector& preorder, int& i, int minLimit, int maxLimit) { + if (i == preorder.size()) { + return true; + } + + int root = preorder[i]; + if (root <= minLimit || root >= maxLimit) { + return false; + } + + i++; + bool left = helper(preorder, i, minLimit, root); + bool right = helper(preorder, i, root, maxLimit); + return left || right; + } +}; +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(n)$ +- Space complexity: $O(n)$ + +> Where $n$ is the length of `preorder` From ab8fc56633959aa830951b2e30c06466f8cce406 Mon Sep 17 00:00:00 2001 From: ranveersingh2718 Date: Wed, 10 Dec 2025 21:12:28 -0800 Subject: [PATCH 10/15] add two-sum-bsts.md article --- articles/two-sum-bsts.md | 640 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 640 insertions(+) create mode 100644 articles/two-sum-bsts.md diff --git a/articles/two-sum-bsts.md b/articles/two-sum-bsts.md new file mode 100644 index 000000000..0d584c192 --- /dev/null +++ b/articles/two-sum-bsts.md @@ -0,0 +1,640 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def twoSumBSTs(self, root1: Optional[TreeNode], root2: Optional[TreeNode], target: int) -> bool: + def dfs(curr_node, node_list): + if not curr_node: + return + node_list.append(curr_node.val) + dfs(curr_node.left, node_list) + dfs(curr_node.right, node_list) + + node_list1, node_list2 = [], [] + dfs(root1, node_list1) + dfs(root2, node_list2) + + for a in node_list1: + for b in node_list2: + if a + b == target: + return True + return False +``` + +```java +class Solution { + private void dfs(TreeNode currNode, List nodeList) { + if (currNode == null) { + return; + } + node_list.add(currNode.val); + dfs(currNode.left, nodeList); + dfs(currNode.right, nodeList); + } + + public boolean twoSumBSTs(TreeNode root1, TreeNode root2, int target) { + List nodeList1 = new ArrayList<>(); + List nodeList2 = new ArrayList<>(); + dfs(root1, nodeList1); + dfs(root2, nodeList2); + + for (int a : nodeList1) { + for (int b : nodeList2) { + if (a + b == target) { + return true; + } + } + } + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(m \cdot n)$ +- Space complexity: $O(m+n)$ + +> Where $m$ and $n$ are the number of nodes in the two trees. + +--- + +## 2. Binary Search + +::tabs-start + +```python +class Solution: + def twoSumBSTs(self, root1: Optional[TreeNode], root2: Optional[TreeNode], target: int) -> bool: + def binarySearch(root2, target2): + if not root2: + return False + if root2.val == target2: + return True + elif root2.val > target2: + return binarySearch(root2.left, target2) + else: + return binarySearch(root2.right, target2) + + def dfs(root, target): + if not root: + return False + if binarySearch(root2, target - root.val): + return True + return dfs(root.left, target) or dfs(root.right, target) + + return dfs(root1, target) +``` + +```java +class Solution { + private boolean binarySearch(TreeNode root2, int target2) { + if (root2 == null) { + return false; + } + if (root2.val == target2) { + return true; + } else if (root2.val > target2) { + return binarySearch(root2.left, target2); + } else { + return binarySearch(root2.right, target2); + } + } + + private boolean dfs(TreeNode root1, TreeNode root2, int target) { + if (root1 == null) { + return false; + } + if (binarySearch(root2, target - root1.val)) { + return true; + } + return dfs(root1.left, root2, target) || dfs(root1.right, root2, target); + } + + public boolean twoSumBSTs(TreeNode root1, TreeNode root2, int target) { + return dfs(root1, root2, target); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(m \cdot \log n)$ +- Space complexity: $O(\log m + \log n)$ + +> Where $m$ and $n$ are the number of nodes in the two trees. + +--- + +## 3. Hash Set + +::tabs-start + +```python +class Solution: + def twoSumBSTs(self, root1: Optional[TreeNode], root2: Optional[TreeNode], target: int) -> bool: + def dfs(curr_node, node_set): + if not curr_node: + return + dfs(curr_node.left, node_set) + node_set.add(curr_node.val) + dfs(curr_node.right, node_set) + + node_set1, node_set2 = set(), set() + dfs(root1, node_set1) + dfs(root2, node_set2) + + for value1 in node_set1: + if target - value1 in node_set2: + return True + return False +``` + +```java +class Solution { + private void dfs(TreeNode currNode, Set nodeSet) { + if (currNode == null) { + return; + } + dfs(currNode.left, nodeSet); + nodeSet.add(currNode.val); + dfs(currNode.right, nodeSet); + } + + public boolean twoSumBSTs(TreeNode root1, TreeNode root2, int target) { + Set nodeSet1 = new HashSet<>(); + Set nodeSet2 = new HashSet<>(); + dfs(root1, nodeSet1); + dfs(root2, nodeSet2); + + for (int value1 : nodeSet1) { + if (nodeSet2.contains(target - value1)) { + return true; + } + } + + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(m + n)$ +- Space complexity: $O(m + n)$ + +> Where $m$ and $n$ are the number of nodes in the two trees. + +--- + +## 4. Two Pointers + +::tabs-start + +```python +class Solution: + def twoSumBSTs(self, root1: Optional[TreeNode], root2: Optional[TreeNode], target: int) -> bool: + def dfs(curr_node, node_list): + if not curr_node: + return + dfs(curr_node.left, node_list) + node_list.append(curr_node.val) + dfs(curr_node.right, node_list) + + node_list1, node_list2 = [], [] + dfs(root1, node_list1) + dfs(root2, node_list2) + + pointer1 = 0 + pointer2 = len(node_list2) - 1 + while pointer1 < len(node_list1) and pointer2 >= 0: + if node_list1[pointer1] + node_list2[pointer2] == target: + return True + elif node_list1[pointer1] + node_list2[pointer2] < target: + pointer1 += 1 + else: + pointer2 -= 1 + return False +``` + +```java +class Solution { + private void dfs(TreeNode currNode, List nodeList) { + if (currNode == null) { + return; + } + dfs(currNode.left, nodeList); + nodeList.add(currNode.val); + dfs(currNode.right, nodeList); + } + + public boolean twoSumBSTs(TreeNode root1, TreeNode root2, int target) { + List nodeList1 = new ArrayList<>(); + List nodeList2 = new ArrayList<>(); + dfs(root1, nodeList1); + dfs(root2, nodeList2); + + int pointer1 = 0, pointer2 = nodeList2.size() - 1; + while (pointer1 < nodeList1.size() && pointer2 >= 0) { + if (nodeList1.get(pointer1) + nodeList2.get(pointer2) == target) { + return true; + } else if (nodeList1.get(pointer1) + nodeList2.get(pointer2) < target) { + pointer1++; + } else { + pointer2--; + } + } + + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(m + n)$ +- Space complexity: $O(m + n)$ + +> Where $m$ and $n$ are the number of nodes in the two trees. + +--- + +## 5. Morris Traversal + +::tabs-start + +```python +class Solution: + def twoSumBSTs(self, root1: Optional[TreeNode], root2: Optional[TreeNode], target: int) -> bool: + def morris_traversal(root): + current = root + while current: + if not current.left: + yield current.val + current = current.right + else: + pre = current.left + while pre.right and pre.right != current: + pre = pre.right + if not pre.right: + pre.right = current + current = current.left + else: + pre.right = None + yield current.val + current = current.right + + def reversed_morris_traversal(root): + current = root + while current: + if not current.right: + yield current.val + current = current.left + else: + pre = current.right + while pre.left and pre.left != current: + pre = pre.left + if not pre.left: + pre.left = current + current = current.right + else: + pre.left = None + yield current.val + current = current.left + + iterater1 = morris_traversal(root1) + iterater2 = reversed_morris_traversal(root2) + value1 = next(iterater1, None) + value2 = next(iterater2, None) + + while value1 is not None and value2 is not None: + if value1 + value2 == target: + return True + elif value1 + value2 < target: + value1 = next(iterater1, None) + else: + value2 = next(iterater2, None) + return False +``` + +```java +class MorrisIterator implements Iterator { + private TreeNode current; + private TreeNode pre; + + public MorrisIterator(TreeNode root) { + current = root; + pre = null; + } + + public boolean hasNext() { + return current != null; + } + + public Integer next() { + Integer val = null; + while (current != null) { + if (current.left == null) { + val = current.val; + current = current.right; + break; + } else { + pre = current.left; + while (pre.right != null && pre.right != current) { + pre = pre.right; + } + if (pre.right == null) { + pre.right = current; + current = current.left; + } else { + pre.right = null; + val = current.val; + current = current.right; + break; + } + } + } + return val; + } +} + +class ReversedMorrisIterator implements Iterator { + private TreeNode current; + private TreeNode pre; + + public ReversedMorrisIterator(TreeNode root) { + current = root; + pre = null; + } + + public boolean hasNext() { + return current != null; + } + + public Integer next() { + Integer val = null; + while (current != null) { + if (current.right == null) { + val = current.val; + current = current.left; + break; + } else { + pre = current.right; + while (pre.left != null && pre.left != current) { + pre = pre.left; + } + if (pre.left == null) { + pre.left = current; + current = current.right; + } else { + pre.left = null; + val = current.val; + current = current.left; + break; + } + } + } + return val; + } +} + +class Solution { + public boolean twoSumBSTs(TreeNode root1, TreeNode root2, int target) { + MorrisIterator iterator1 = new MorrisIterator(root1); + ReversedMorrisIterator iterator2 = new ReversedMorrisIterator(root2); + + int value1 = iterator1.next(), value2 = iterator2.next(); + + while (value1 != Integer.MIN_VALUE && value2 != Integer.MIN_VALUE) { + if (value1 + value2 == target) { + return true; + } else if (value1 + value2 < target) { + if (iterator1.hasNext()) { + value1 = iterator1.next(); + } else { + value1 = Integer.MIN_VALUE; + } + } else { + if (iterator2.hasNext()) { + value2 = iterator2.next(); + } else { + value2 = Integer.MIN_VALUE; + } + } + } + + return false; + } +} +``` + +```cpp +class MorrisIterator { +private: + TreeNode* current; + TreeNode* pre; + +public: + MorrisIterator(TreeNode* root) : current(root), pre(nullptr) {} + + bool hasNext() { + return current != nullptr; + } + + int next() { + int val = INT_MIN; + while (current != nullptr) { + if (current->left == nullptr) { + val = current->val; + current = current->right; + break; + } else { + pre = current->left; + while (pre->right != nullptr && pre->right != current) { + pre = pre->right; + } + if (pre->right == nullptr) { + pre->right = current; + current = current->left; + } else { + pre->right = nullptr; + val = current->val; + current = current->right; + break; + } + } + } + return val; + } +}; + +class ReversedMorrisIterator { +private: + TreeNode* current; + TreeNode* pre; + +public: + ReversedMorrisIterator(TreeNode* root) : current(root), pre(nullptr) {} + + bool hasNext() { + return current != nullptr; + } + + int next() { + int val = INT_MIN; + while (current != nullptr) { + if (current->right == nullptr) { + val = current->val; + current = current->left; + break; + } else { + pre = current->right; + while (pre->left != nullptr && pre->left != current) { + pre = pre->left; + } + if (pre->left == nullptr) { + pre->left = current; + current = current->right; + } else { + pre->left = nullptr; + val = current->val; + current = current->left; + break; + } + } + } + return val; + } +}; + +class Solution { +public: + bool twoSumBSTs(TreeNode* root1, TreeNode* root2, int target) { + MorrisIterator iterator1(root1); + ReversedMorrisIterator iterator2(root2); + + int value1 = iterator1.next(); + int value2 = iterator2.next(); + + while (value1 != INT_MIN && value2 != INT_MIN) { + if (value1 + value2 == target) { + return true; + } else if (value1 + value2 < target) { + if (iterator1.hasNext()) { + value1 = iterator1.next(); + } else { + value1 = INT_MIN; + } + } else { + if (iterator2.hasNext()) { + value2 = iterator2.next(); + } else { + value2 = INT_MIN; + } + } + } + + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {TreeNode} root1 + * @param {TreeNode} root2 + * @param {number} target + * @return {boolean} + */ + twoSumBSTs(root1, root2, target) { + function* morrisTraversal(root) { + let current = root; + while (current) { + if (!current.left) { + yield current.val; + current = current.right; + } else { + let pre = current.left; + while (pre.right && pre.right !== current) { + pre = pre.right; + } + if (!pre.right) { + pre.right = current; + current = current.left; + } else { + pre.right = null; + yield current.val; + current = current.right; + } + } + } + } + + function* reversedMorrisTraversal(root) { + let current = root; + while (current) { + if (!current.right) { + yield current.val; + current = current.left; + } else { + let pre = current.right; + while (pre.left && pre.left !== current) { + pre = pre.left; + } + if (!pre.left) { + pre.left = current; + current = current.right; + } else { + pre.left = null; + yield current.val; + current = current.left; + } + } + } + } + + const iterator1 = morrisTraversal(root1); + const iterator2 = reversedMorrisTraversal(root2); + + let result1 = iterator1.next(); + let result2 = iterator2.next(); + + let value1 = result1.done ? null : result1.value; + let value2 = result2.done ? null : result2.value; + + while (value1 !== null && value2 !== null) { + if (value1 + value2 === target) { + return true; + } else if (value1 + value2 < target) { + result1 = iterator1.next(); + value1 = result1.done ? null : result1.value; + } else { + result2 = iterator2.next(); + value2 = result2.done ? null : result2.value; + } + } + + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(m + n)$ +- Space complexity: $O(1)$ + +> Where $m$ and $n$ are the number of nodes in the two trees. From bf0e55c9013927ccb56aece984df7e76f9706dfa Mon Sep 17 00:00:00 2001 From: ranveersingh2718 Date: Wed, 10 Dec 2025 22:58:40 -0800 Subject: [PATCH 11/15] add largest-bst-subtree.md article --- articles/largest-bst-subtree.md | 730 ++++++++++++++++++++++++++++++++ 1 file changed, 730 insertions(+) create mode 100644 articles/largest-bst-subtree.md diff --git a/articles/largest-bst-subtree.md b/articles/largest-bst-subtree.md new file mode 100644 index 000000000..8aff0f84b --- /dev/null +++ b/articles/largest-bst-subtree.md @@ -0,0 +1,730 @@ +## 1. Pre-Order Traversal + +::tabs-start + +```python +class Solution: + def is_valid_bst(self, root: Optional[TreeNode]) -> bool: + """Check if given tree is a valid Binary Search Tree.""" + # An empty tree is a valid Binary Search Tree. + if not root: + return True + + # Find the max node in the left subtree of current node. + left_max = self.find_max(root.left) + + # If the left subtree has a node greater than or equal to the current node, + # then it is not a valid Binary Search Tree. + if left_max >= root.val: + return False + + # Find the min node in the right subtree of current node. + right_min = self.find_min(root.right) + + # If the right subtree has a value less than or equal to the current node, + # then it is not a valid Binary Search Tree. + if right_min <= root.val: + return False + + # If the left and right subtrees of current tree are also valid BST. + # then the whole tree is a BST. + return self.is_valid_bst(root.left) and self.is_valid_bst(root.right) + + def find_max(self, root: Optional[TreeNode]) -> int: + # Max node in a empty tree should be smaller than parent. + if not root: + return float('-inf') + + # Check the maximum node from the current node, left and right subtree of the current tree. + return max(root.val, self.find_max(root.left), self.find_max(root.right)) + + def find_min(self, root: Optional[TreeNode]) -> int: + # Min node in a empty tree should be larger than parent. + if not root: + return float('inf') + + # Check the minimum node from the current node, left and right subtree of the current tree + return min(root.val, self.find_min(root.left), self.find_min(root.right)) + + def count_nodes(self, root: Optional[TreeNode]) -> int: + if not root: + return 0 + + # Add nodes in left and right subtree. + # Add 1 and return total size. + return 1 + self.count_nodes(root.left) + self.count_nodes(root.right) + + def largestBSTSubtree(self, root: Optional[TreeNode]) -> int: + if not root: + return 0 + + # If current subtree is a validBST, its children will have smaller size BST. + if self.is_valid_bst(root): + return self.count_nodes(root) + + # Find BST in left and right subtrees of current nodes. + return max(self.largestBSTSubtree(root.left), self.largestBSTSubtree(root.right)) +``` + +```java +class Solution { + // Function to check if given tree is a valid Binary Search Tree or not. + private boolean isValidBST(TreeNode root) { + // An empty tree is a valid Binary Search Tree. + if (root == null) { + return true; + } + + // Find the max node in the left subtree of current node. + int leftMax = findMax(root.left); + + // If the left subtree has a node greater than or equal to the current node, + // then it is not a valid Binary Search Tree. + if (leftMax >= root.val) { + return false; + } + + // Find the min node in the right subtree of current node. + int rightMin = findMin(root.right); + + // If the right subtree has a value less than or equal to the current node, + // then it is not a valid Binary Search Tree. + if (rightMin <= root.val) { + return false; + } + + // If the left and right subtrees of current tree are also valid BST. + // then the whole tree is a BST. + if (isValidBST(root.left) && isValidBST(root.right)) { + return true; + } + + return false; + } + + private int findMax(TreeNode root) { + // Max node in a empty tree should be smaller than parent. + if (root == null) { + return Integer.MIN_VALUE; + } + + // Check the maximum node from the current node, left and right subtree of the current tree + return Math.max(Math.max(root.val, findMax(root.left)), findMax(root.right)); + } + + private int findMin(TreeNode root) { + // Min node in a empty tree should be larger than parent. + if (root == null) { + return Integer.MAX_VALUE; + } + + // Check the minimum node from the current node, left and right subtree of the current tree + return Math.min(Math.min(root.val, findMin(root.left)), findMin(root.right)); + } + + private int countNodes(TreeNode root) { + // Empty tree has 0 nodes. + if (root == null) { + return 0; + } + + // Add nodes in left and right subtree. + // Add 1 and return total size. + return 1 + countNodes(root.left) + countNodes(root.right); + } + + public int largestBSTSubtree(TreeNode root) { + if (root == null) { + return 0; + } + + // If current subtree is a validBST, its children will have smaller size BST. + if (isValidBST(root)) { + return countNodes(root); + } + + // Find BST in left and right subtrees of current nodes. + return Math.max(largestBSTSubtree(root.left), largestBSTSubtree(root.right)); + } +} +``` + +```cpp +class Solution { +public: + // Function to check if given tree is a valid Binary Search Tree or not. + bool isValidBST(TreeNode* root) { + // An empty tree is a valid Binary Search Tree. + if (!root) { + return true; + } + + // Find the max node in the left subtree of current node. + int leftMax = findMax(root->left); + + // If the left subtree has a node greater than or equal to the current node, + // then it is not a valid Binary Search Tree. + if (leftMax >= root->val) { + return false; + } + + // Find the min node in the right subtree of current node. + int rightMin = findMin(root->right); + + // If the right subtree has a value less than or equal to the current node, + // then it is not a valid Binary Search Tree. + if (rightMin <= root->val) { + return false; + } + + // If the left and right subtrees of current tree are also valid BST. + // then the whole tree is a BST. + if (isValidBST(root->left) && isValidBST(root->right)) { + return true; + } + + return false; + } + + int findMax(TreeNode* root) { + // Max node in a empty tree should be smaller than parent. + if (!root) { + return INT_MIN; + } + + // Check the maximum node from the current node, left and right subtree of the current tree + return max({ root->val, findMax(root->left), findMax(root->right) }); + } + + int findMin(TreeNode* root) { + // Min node in a empty tree should be larger than parent. + if (!root) { + return INT_MAX; + } + + // Check the minimum node from the current node, left and right subtree of the current tree + return min({ root->val, findMin(root->left), findMin(root->right) }); + } + + int countNodes(TreeNode* root) { + // Empty tree has 0 nodes. + if (!root) { + return 0; + } + + // Add nodes in left and right subtree. + // Add 1 and return total size. + return 1 + countNodes(root->left) + countNodes(root->right); + } + + int largestBSTSubtree(TreeNode* root) { + if (!root) { + return 0; + } + + // If current subtree is a validBST, its children will have smaller size BST. + if (isValidBST(root)) { + return countNodes(root); + } + + // Find BST in left and right subtrees of current nodes. + return max(largestBSTSubtree(root->right), largestBSTSubtree(root->left)); + } +}; +``` + +```javascript +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + largestBSTSubtree(root) { + if (!root) { + return 0; + } + + // If current subtree is a validBST, its children will have smaller size BST. + if (this.isValidBST(root)) { + return this.countNodes(root); + } + // Find BST in left and right subtrees of current nodes. + return Math.max(this.largestBSTSubtree(root.left), this.largestBSTSubtree(root.right)); + } + + // Function to check if given tree is a valid Binary Search Tree or not. + isValidBST(root) { + // An empty tree is a valid Binary Search Tree. + if (!root) { + return true; + } + // Find the max node in the left subtree of current node. + let leftMax = this.findMax(root.left); + // If the left subtree has a node greater than or equal to the current node, + // then it is not a valid Binary Search Tree. + if (leftMax >= root.val) { + return false; + } + // Find the min node in the right subtree of current node. + let rightMin = this.findMin(root.right); + // If the right subtree has a value less than or equal to the current node, + // then it is not a valid Binary Search Tree. + if (rightMin <= root.val) { + return false; + } + // If the left and right subtrees of current tree are also valid BST. + // then the whole tree is a BST. + if (this.isValidBST(root.left) && this.isValidBST(root.right)) { + return true; + } + return false; + } + + findMax(root) { + // Max node in a empty tree should me smaller than parent. + if (!root) { + return Number.MIN_SAFE_INTEGER; + } + + // Check the maximum node from the current node, left and right subtree of the current tree + return Math.max(root.val, this.findMax(root.left), this.findMax(root.right)); + } + + findMin(root) { + // Min node in a empty tree should me larger than parent. + if (!root) { + return Number.MAX_SAFE_INTEGER; + } + + // Check the minimum node from the current node, left and right subtree of the current tree + return Math.min(root.val, this.findMin(root.left), this.findMin(root.right)); + } + + countNodes(root) { + // Empty tree has 0 nodes. + if (!root) { + return 0; + } + // Add nodes in left and right subtree. + // Add 1 and return total size. + return 1 + this.countNodes(root.left) + this.countNodes(root.right); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(N^3)$ +- Space complexity: $O(N)$ + - The recursion call stack can take at most $O(H)$ space; in the worst-case scenario, the height of the tree will equal $N$. + +> Where $N$ and $H$ are the number of nodes and the max height of the given tree respectively + +--- + +## 2. Pre-Order Traversal Optimized + +::tabs-start + +```python +class Solution: + def is_valid_bst(self, root: Optional[TreeNode]) -> bool: + """Check if given tree is a valid BST using in-order traversal.""" + # An empty tree is a valid Binary Search Tree. + if not root: + return True + + # If left subtree is not a valid BST return false. + if not self.is_valid_bst(root.left): + return False + + # If current node's value is not greater than the previous + # node's value in the in-order traversal return false. + if self.previous and self.previous.val >= root.val: + return False + + # Update previous node to current node. + self.previous = root + + # If right subtree is not a valid BST return false. + return self.is_valid_bst(root.right) + + # Count nodes in current tree. + def count_nodes(self, root: Optional[TreeNode]) -> int: + if not root: + return 0 + + # Add nodes in left and right subtree. + # Add 1 and return total size. + return 1 + self.count_nodes(root.left) + self.count_nodes(root.right) + + def largestBSTSubtree(self, root: Optional[TreeNode]) -> int: + if not root: + return 0 + + # Previous node is initially null. + self.previous = None + + # If current subtree is a validBST, its children will have smaller size BST. + if self.is_valid_bst(root): + return self.count_nodes(root) + + # Find BST in left and right subtrees of current nodes. + return max(self.largestBSTSubtree(root.left), self.largestBSTSubtree(root.right)) +``` + +```java +class Solution { + // Track previous node while doing inorder traversal. + private TreeNode previous; + + // Function to check if given tree is a valid Binary Search Tree or not. + private boolean isValidBST(TreeNode root) { + // An empty tree is a valid Binary Search Tree. + if (root == null) { + return true; + } + + // If left subtree is not a valid BST return false. + if(!isValidBST(root.left)) { + return false; + } + + // If current node's value is not greater than the previous + // node's value in the in-order traversal return false. + if (previous != null && previous.val >= root.val) { + return false; + } + + // Update previous node to current node. + previous = root; + + // If right subtree is not a valid BST return false. + return isValidBST(root.right); + } + + private int countNodes(TreeNode root) { + if (root == null) { + return 0; + } + + // Add nodes in left and right subtree. + // Add 1 and return total size. + return 1 + countNodes(root.left) + countNodes(root.right); + } + + public int largestBSTSubtree(TreeNode root) { + if (root == null) { + return 0; + } + + // Set previous node to NULL initially. + previous = null; + + // If current subtree is a validBST, its children will have smaller size BST. + if (isValidBST(root)) { + return countNodes(root); + } + + // Find BST in left and right subtrees of current nodes. + return Math.max(largestBSTSubtree(root.left), largestBSTSubtree(root.right)); + } +} +``` + +```cpp +class Solution { +public: + // Track previous node while doing inorder traversal. + TreeNode* previous = NULL; + + // Function to check if given tree is a valid Binary Search Tree or not. + bool isValidBST(TreeNode* root) { + // An empty tree is a valid Binary Search Tree. + if (!root) { + return true; + } + + // If left subtree is not a valid BST return false. + if(!isValidBST(root->left)) { + return false; + } + + // If current node's value is not greater than the previous + // node's value in the in-order traversal return false. + if (previous && previous->val >= root->val) { + return false; + } + + // Update previous node to current node. + previous = root; + + // If right subtree is not a valid BST return false. + return isValidBST(root->right); + } + + int countNodes(TreeNode* root) { + if (!root) { + return 0; + } + + // Add nodes in left and right subtree. + // Add 1 and return total size. + return 1 + countNodes(root->left) + countNodes(root->right); + } + + int largestBSTSubtree(TreeNode* root) { + if (!root) { + return 0; + } + + // Set previous node to NULL initially. + previous = NULL; + + // If current subtree is a validBST, its children will have smaller size BST. + if (isValidBST(root)) { + return countNodes(root); + } + + // Find BST in left and right subtrees of current nodes. + return max(largestBSTSubtree(root->left), largestBSTSubtree(root->right)); + } +}; +``` + +```javascript +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + largestBSTSubtree(root) { + if (!root) { + return 0; + } + // Set previous node to NULL initially. + this.previous = null; + // If current subtree is a validBST, its children will have smaller size BST. + if (this.isValidBST(root)) { + return this.countNodes(root); + } + // Find BST in left and right subtrees of current nodes. + return Math.max(this.largestBSTSubtree(root.left), this.largestBSTSubtree(root.right)); + } + + // Function to check if given tree is a valid Binary Search Tree or not. + isValidBST(root) { + // An empty tree is a valid Binary Search Tree. + if (!root) { + return true; + } + // If left subtree is not a valid BST return false. + if (!this.isValidBST(root.left)) { + return false; + } + // If current node's value is not greater than the previous + // node's value in the in-order traversal return false. + if (this.previous && this.previous.val >= root.val) { + return false; + } + // Update previous node to current node. + this.previous = root; + // If right subtree is not a valid BST return false. + return this.isValidBST(root.right); + } + + countNodes(root) { + if (!root) { + return 0; + } + // Add nodes in left and right subtree. + // Add 1 and return total size. + return 1 + this.countNodes(root.left) + this.countNodes(root.right); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(N^2)$ +- Space complexity: $O(N)$ + - The recursion call stack can take at most $O(H)$ space; in the worst-case scenario, the height of the tree will equal $N$. + +> Where $N$ and $H$ are the number of nodes and the max height of the given tree respectively + +--- + +## 3. Post-Order Traversal + +::tabs-start + +```python +# Each node will return min node value, max node value, size +class NodeValue: + def __init__(self, min_node, max_node, max_size): + self.max_node = max_node + self.min_node = min_node + self.max_size = max_size + +class Solution: + def largest_bst_subtree_helper(self, root): + # An empty tree is a BST of size 0. + if not root: + return NodeValue(float('inf'), float('-inf'), 0) + + # Get values from left and right subtree of current tree. + left = self.largest_bst_subtree_helper(root.left) + right = self.largest_bst_subtree_helper(root.right) + + # Current node is greater than max in left AND smaller than min in right, it is a BST. + if left.max_node < root.val < right.min_node: + # It is a BST. + return NodeValue(min(root.val, left.min_node), max(root.val, right.max_node), + left.max_size + right.max_size + 1) + + # Otherwise, return [-inf, inf] so that parent can't be valid BST + return NodeValue(float('-inf'), float('inf'), max(left.max_size, right.max_size)) + + def largestBSTSubtree(self, root: Optional[TreeNode]) -> int: + return self.largest_bst_subtree_helper(root).max_size +``` + +```java +// Each node will return min node value, max node value, max size +class NodeValue { + public int maxNode, minNode, maxSize; + + NodeValue(int minNode, int maxNode, int maxSize) { + this.maxNode = maxNode; + this.minNode = minNode; + this.maxSize = maxSize; + } +}; + +class Solution { + public NodeValue largestBSTSubtreeHelper(TreeNode root) { + // An empty tree is a BST of size 0. + if (root == null) { + return new NodeValue(Integer.MAX_VALUE, Integer.MIN_VALUE, 0); + } + + // Get values from left and right subtree of current tree. + NodeValue left = largestBSTSubtreeHelper(root.left); + NodeValue right = largestBSTSubtreeHelper(root.right); + + // Current node is greater than max in left AND smaller than min in right, it is a BST. + if (left.maxNode < root.val && root.val < right.minNode) { + // It is a BST. + return new NodeValue(Math.min(root.val, left.minNode), Math.max(root.val, right.maxNode), + left.maxSize + right.maxSize + 1); + } + + // Otherwise, return [-inf, inf] so that parent can't be valid BST + return new NodeValue(Integer.MIN_VALUE, Integer.MAX_VALUE, + Math.max(left.maxSize, right.maxSize)); + } + + public int largestBSTSubtree(TreeNode root) { + return largestBSTSubtreeHelper(root).maxSize; + } +} +``` + +```cpp +// Each node will return min node value, max node value, max size +class NodeValue { +public: + int maxNode, minNode, maxSize; + + NodeValue(int minNode, int maxNode, int maxSize) { + this->maxNode = maxNode; + this->minNode = minNode; + this->maxSize = maxSize; + } +}; + +class Solution { +public: + NodeValue largestBSTSubtreeHelper(TreeNode* root) { + // An empty tree is a BST of size 0. + if (!root) { + return NodeValue(INT_MAX, INT_MIN, 0); + } + + // Get values from left and right subtree of current tree. + auto left = largestBSTSubtreeHelper(root->left); + auto right = largestBSTSubtreeHelper(root->right); + + // Current node is greater than max in left AND smaller than min in right, it is a BST. + if (left.maxNode < root->val && root->val < right.minNode) { + // It is a BST. + return NodeValue(min(root->val, left.minNode), max(root->val, right.maxNode), + left.maxSize + right.maxSize + 1); + } + + // Otherwise, return [-inf, inf] so that parent can't be valid BST + return NodeValue(INT_MIN, INT_MAX, max(left.maxSize, right.maxSize)); + } + + int largestBSTSubtree(TreeNode* root) { + return largestBSTSubtreeHelper(root).maxSize; + } +}; +``` + +```javascript +// Each node will return isBST, max node value, min node value, size +class NodeValue { + constructor(minNode, maxNode, maxSize) { + this.maxNode = maxNode; + this.minNode = minNode; + this.maxSize = maxSize; + } +}; + +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + largestBSTSubtree(root) { + return largestBSTSubtreeHelper(root).maxSize; + } + + largestBSTSubtreeHelper(root) { + // An empty tree is a BST of size 0. + if (!root) { + return new NodeValue(Number.MAX_SAFE_INTEGER, Number.MIN_SAFE_INTEGER, 0); + } + + // Get values from left and right subtree of current tree. + let left = largestBSTSubtreeHelper(root.left); + let right = largestBSTSubtreeHelper(root.right); + + // Current node is greater than max in left AND smaller than min in right, it is a BST. + if (left.maxNode < root.val && root.val < right.minNode) { + // It is a BST. + return new NodeValue(Math.min(root.val, left.minNode), Math.max(root.val, right.maxNode), + left.maxSize + right.maxSize + 1); + } + + // Otherwise, return [-inf, inf] so that parent can't be valid BST + return new NodeValue(Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER, + Math.max(left.maxSize, right.maxSize)); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(N)$ +- Space complexity: $O(N)$ + - The recursion call stack can take at most $O(H)$ space; in the worst-case scenario, the height of the tree will equal $N$. + +> Where $N$ and $H$ are the number of nodes and the max height of the given tree respectively From d8b0dd9a7d1bf8786462494e63c3e3201798e402 Mon Sep 17 00:00:00 2001 From: ranveersingh2718 Date: Thu, 11 Dec 2025 15:29:17 -0800 Subject: [PATCH 12/15] add clone-n-ary-tree.md article --- articles/clone-n-ary-tree.md | 380 +++++++++++++++++++++++++++++++++++ 1 file changed, 380 insertions(+) create mode 100644 articles/clone-n-ary-tree.md diff --git a/articles/clone-n-ary-tree.md b/articles/clone-n-ary-tree.md new file mode 100644 index 000000000..9709f365a --- /dev/null +++ b/articles/clone-n-ary-tree.md @@ -0,0 +1,380 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def cloneTree(self, root: 'Node') -> 'Node': + + # Base case: empty node. + if not root: + return root + + # First, copy the node itself. + node_copy = Node(root.val) + + # Then, recursively clone the sub-trees. + for child in root.children: + node_copy.children.append(self.cloneTree(child)) + + return node_copy +``` + +```java +class Solution { + public Node cloneTree(Node root) { + // Base case: empty node. + if (root == null) { + return root; + } + + // First, copy the node itself. + Node nodeCopy = new Node(root.val); + + // Then, recursively clone the sub-trees. + for (Node child : root.children) { + nodeCopy.children.add(this.cloneTree(child)); + } + + return nodeCopy; + } +} +``` + +```cpp +class Solution { +public: + Node* cloneTree(Node* root) { + // Base case: empty node. + if (!root) { + return root; + } + + // First, copy the node itself. + Node* node_copy = new Node(root->val); + + // Then, recursively clone the sub-trees. + for (Node* child : root->children) { + node_copy->children.push_back(cloneTree(child)); + } + + return node_copy; + } +}; +``` + +```javascript +class Solution { + /** + * @param {_Node|null} root + * @return {_Node|null} + */ + cloneTree(root) { + // Base case: empty node. + if (!root) { + return root; + } + + // First, copy the node itself. + const node_copy = new Node(root.val); + + // Then, recursively clone the sub-trees. + for (const child of root.children) { + node_copy.children.push(this.cloneTree(child)); + } + + return node_copy; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(M)$ +- Space complexity: $O(M)$ + +> Where $M$ is the number of nodes in the input tree. + +--- + +## 2. DFS with Iteration + +::tabs-start + +```python +class Solution: + def cloneTree(self, root: 'Node') -> 'Node': + + if not root: + return root + + new_root = Node(root.val) + # Starting point to kick off the DFS visits. + stack = [(root, new_root)] + + while stack: + old_node, new_node = stack.pop() + for child_node in old_node.children: + new_child_node = Node(child_node.val) + + # Make a copy for each child node. + new_node.children.append(new_child_node) + + # Schedule a visit to copy the child nodes of each child node. + stack.append((child_node, new_child_node)) + + return new_root +``` + +```java +class Solution { + public Node cloneTree(Node root) { + if (root == null) { + return root; + } + + Node newRoot = new Node(root.val); + + // Here we used the ArrayDeque instead of the Queue class, + // which is a more efficient implementation of queue data structure. + Deque stack = new ArrayDeque<>(); + + // Starting point to kick off the DFS visits. + stack.addLast(new Node[]{root, newRoot}); + + while (!stack.isEmpty()) { + Node[] nodePair = stack.pop(); + Node oldNode = nodePair[0]; + Node newNode = nodePair[1]; + for (Node childNode : oldNode.children) { + Node newChildNode = new Node(childNode.val); + + // Make a copy for each child node. + newNode.children.add(newChildNode); + + // Schedule a visit to copy the child nodes of each child node. + stack.push(new Node[]{childNode, newChildNode}); + } + } + + return newRoot; + } +} +``` + +```cpp +class Solution { +public: + Node* cloneTree(Node* root) { + if (!root) { + return root; + } + + Node* new_root = new Node(root->val); + // Starting point to kick off the DFS visits. + stack> st; + st.push({root, new_root}); + + while (!st.empty()) { + auto [old_node, new_node] = st.top(); + st.pop(); + + for (Node* child_node : old_node->children) { + Node* new_child_node = new Node(child_node->val); + // Make a copy for each child node. + new_node->children.push_back(new_child_node); + // Schedule a visit to copy the child nodes of each child node. + st.push({child_node, new_child_node}); + } + } + + return new_root; + } +}; +``` + +```javascript +class Solution { + /** + * @param {_Node|null} node + * @return {_Node|null} + */ + cloneTree(root) { + if (!root) { + return root; + } + + const new_root = new Node(root.val); + // Starting point to kick off the DFS visits. + const stack = [[root, new_root]]; + + while (stack.length > 0) { + const [old_node, new_node] = stack.pop(); + + for (const child_node of old_node.children) { + const new_child_node = new Node(child_node.val); + // Make a copy for each child node. + new_node.children.push(new_child_node); + // Schedule a visit to copy the child nodes of each child node. + stack.push([child_node, new_child_node]); + } + } + + return new_root; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(M)$ +- Space complexity: $O(\log_{n}{M})$ + +> Where $M$ is the number of nodes in the input tree and $N$ is the maximum number of children that a node can have + +--- + +## 3. BFS + +::tabs-start + +```python +class Solution: + def cloneTree(self, root: 'Node') -> 'Node': + + if not root: + return root + + new_root = Node(root.val) + # Starting point to kick off the BFS visits. + queue = deque([(root, new_root)]) + + while queue: + # Get the element from the head of the queue. + old_node, new_node = queue.popleft() + + for child_node in old_node.children: + new_child_node = Node(child_node.val) + + # Make a copy for each child node. + new_node.children.append(new_child_node) + + # Schedule a visit to copy the child nodes of each child node. + queue.append((child_node, new_child_node)) + + return new_root +``` + +```java +class Solution { + public Node cloneTree(Node root) { + if (root == null) { + return root; + } + + Node newRoot = new Node(root.val); + + // Starting point to kick off the BFS visits. + // Here we used the ArrayDeque instead of the Queue class, + // which is a more efficient implementation of queue data structure. + Deque queue = new ArrayDeque<>(); + queue.addLast(new Node[]{root, newRoot}); + + while (!queue.isEmpty()) { + Node[] nodePair = queue.removeFirst(); + + Node oldNode = nodePair[0]; + Node newNode = nodePair[1]; + for (Node childNode : oldNode.children) { + Node newChildNode = new Node(childNode.val); + + // Make a copy for each child node. + newNode.children.add(newChildNode); + + // Schedule a visit to copy the child nodes of each child node. + queue.addLast(new Node[]{childNode, newChildNode}); + } + } + + return newRoot; + } +} +``` + +```cpp +class Solution { +public: + Node* cloneTree(Node* root) { + if (!root) { + return root; + } + + Node* new_root = new Node(root->val); + // Starting point to kick off the BFS visits. + queue> q; + q.push({root, new_root}); + + while (!q.empty()) { + // Get the element from the head of the queue. + auto [old_node, new_node] = q.front(); + q.pop(); + + for (Node* child_node : old_node->children) { + Node* new_child_node = new Node(child_node->val); + // Make a copy for each child node. + new_node->children.push_back(new_child_node); + // Schedule a visit to copy the child nodes of each child node. + q.push({child_node, new_child_node}); + } + } + + return new_root; + } +}; +``` + +```javascript +class Solution { + /** + * @param {_Node|null} root + * @return {_Node|null} + */ + cloneTree(root) { + if (!root) { + return root; + } + + const new_root = new Node(root.val); + // Starting point to kick off the BFS visits. + const queue = [[root, new_root]]; + + while (queue.length > 0) { + // Get the element from the head of the queue. + const [old_node, new_node] = queue.shift(); + + for (const child_node of old_node.children) { + const new_child_node = new Node(child_node.val); + // Make a copy for each child node. + new_node.children.push(new_child_node); + // Schedule a visit to copy the child nodes of each child node. + queue.push([child_node, new_child_node]); + } + } + + return new_root; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(M)$ +- Space complexity: $O(M)$ + +> Where $M$ is the number of nodes in the input tree. From ecb5a629882558a76ac04f3fd369901e1c6ad3bf Mon Sep 17 00:00:00 2001 From: ranveersingh2718 Date: Thu, 11 Dec 2025 16:18:42 -0800 Subject: [PATCH 13/15] add find-root-of-n-ary-tree.md article --- articles/find-root-of-n-ary-tree.md | 232 ++++++++++++++++++++++++++++ 1 file changed, 232 insertions(+) create mode 100644 articles/find-root-of-n-ary-tree.md diff --git a/articles/find-root-of-n-ary-tree.md b/articles/find-root-of-n-ary-tree.md new file mode 100644 index 000000000..56cbdbd0f --- /dev/null +++ b/articles/find-root-of-n-ary-tree.md @@ -0,0 +1,232 @@ +## 1. O(n) Space + +::tabs-start + +```python +class Solution: + def findRoot(self, tree: List['Node']) -> 'Node': + # set that contains all the child nodes. + seen = set() + + # add all the child nodes into the set + for node in tree: + for child in node.children: + # we could either add the value or the node itself. + seen.add(child.val) + + # find the node that is not in the child node set. + for node in tree: + if node.val not in seen: + return node +``` + +```java +class Solution { + public Node findRoot(List tree) { + // set that contains all the child nodes. + HashSet seen = new HashSet(); + + // add all the child nodes into the set + for (Node node : tree) { + for (Node child : node.children) + // we could either add the value or the node itself. + seen.add(child.val); + } + + Node root = null; + // find the node that is not in the child node set. + for (Node node : tree) { + if (!seen.contains(node.val)) { + root = node; + break; + } + } + return root; + } +} +``` + +```cpp +class Solution { +public: + Node* findRoot(vector tree) { + // set that contains all the child nodes. + unordered_set seen; + + // add all the child nodes into the set + for (Node* node : tree) { + for (Node* child : node->children) { + // we could either add the value or the node itself. + seen.insert(child->val); + } + } + + Node* root = nullptr; + // find the node that is not in the child node set. + for (Node* node : tree) { + if (seen.find(node->val) == seen.end()) { + root = node; + break; + } + } + + return root; + } +}; +``` + +```javascript +class Solution { + /** + * @param {_Node[]} tree + * @return {_Node} + */ + findRoot(tree) { + // set that contains all the child nodes. + const seen = new Set(); + + // add all the child nodes into the set + for (const node of tree) { + for (const child of node.children) { + // we could either add the value or the node itself. + seen.add(child.val); + } + } + + let root = null; + // find the node that is not in the child node set. + for (const node of tree) { + if (!seen.has(node.val)) { + root = node; + break; + } + } + + return root; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(N)$ +- Space complexity: $O(N)$ + +> Where $N$ is the length of the input list, which is also the number of nodes in the N-ary tree. + +--- + +## 2. O(1) Space + +::tabs-start + +```python +class Solution: + def findRoot(self, tree: List['Node']) -> 'Node': + value_sum = 0 + + for node in tree: + # the value is added as a parent node + value_sum += node.val + for child in node.children: + # the value is deducted as a child node. + value_sum -= child.val + + # the value of the root node is `value_sum` + for node in tree: + if node.val == value_sum: + return node +``` + +```java +class Solution { + public Node findRoot(List tree) { + + Integer valueSum = 0; + + for (Node node : tree) { + // the value is added as a parent node + valueSum += node.val; + for (Node child : node.children) + // the value is deducted as a child node. + valueSum -= child.val; + } + + Node root = null; + // the value of the root node is `valueSum` + for (Node node : tree) { + if (node.val == valueSum) { + root = node; + break; + } + } + return root; + } +} +``` + +```cpp +class Solution { +public: + Node* findRoot(vector tree) { + int value_sum = 0; + + for (Node* node : tree) { + // the value is added as a parent node + value_sum += node->val; + for (Node* child : node->children) { + // the value is deducted as a child node. + value_sum -= child->val; + } + } + + // the value of the root node is `value_sum` + for (Node* node : tree) { + if (node->val == value_sum) { + return node; + } + } + return nullptr; + } +}; +``` + +```javascript +class Solution { + /** + * @param {_Node[]} tree + * @return {_Node} + */ + findRoot(tree) { + let value_sum = 0; + + for (let node of tree) { + // the value is added as a parent node + value_sum += node.val; + for (let child of node.children) { + // the value is deducted as a child node. + value_sum -= child.val; + } + } + + // the value of the root node is `value_sum` + for (let node of tree) { + if (node.val === value_sum) { + return node; + } + } + return null; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(N)$ +- Space complexity: $O(1)$ + +> Where $N$ is the length of the input list, which is also the number of nodes in the N-ary tree. From c2d434cfffad6a52459c88c05ad8d064e33a0c22 Mon Sep 17 00:00:00 2001 From: ranveersingh2718 Date: Thu, 11 Dec 2025 17:39:09 -0800 Subject: [PATCH 14/15] add diameter-of-n-ary-tree.md article --- articles/diameter-of-n-ary-tree.md | 250 +++++++++++++++++++++++++++++ 1 file changed, 250 insertions(+) create mode 100644 articles/diameter-of-n-ary-tree.md diff --git a/articles/diameter-of-n-ary-tree.md b/articles/diameter-of-n-ary-tree.md new file mode 100644 index 000000000..b7ec12826 --- /dev/null +++ b/articles/diameter-of-n-ary-tree.md @@ -0,0 +1,250 @@ +## 1. Distance with Height + +The **height** of a node is defined as the length of the longest downward path to a leaf node from that node. + +::tabs-start + +```python +class Solution: + def diameter(self, root: 'Node') -> int: + diameter = 0 + + def height(node): + """ return the height of the node """ + nonlocal diameter + + if len(node.children) == 0: + return 0 + + # select the top two heights + max_height_1, max_height_2 = 0, 0 + for child in node.children: + parent_height = height(child) + 1 + if parent_height > max_height_1: + max_height_1, max_height_2 = parent_height, max_height_1 + elif parent_height > max_height_2: + max_height_2 = parent_height + + # calculate the distance between the two farthest leaves nodes. + distance = max_height_1 + max_height_2 + diameter = max(diameter, distance) + + return max_height_1 + + height(root) + return diameter +``` + +```java +class Solution { + protected int diameter = 0; + + /** + * return the height of the node + */ + protected int height(Node node) { + if (node.children.size() == 0) + return 0; + + // select the top two largest heights + int maxHeight1 = 0, maxHeight2 = 0; + for (Node child : node.children) { + int parentHeight = height(child) + 1; + if (parentHeight > maxHeight1) { + maxHeight2 = maxHeight1; + maxHeight1 = parentHeight; + } else if (parentHeight > maxHeight2) { + maxHeight2 = parentHeight; + } + // calculate the distance between the two farthest leaves nodes. + int distance = maxHeight1 + maxHeight2; + this.diameter = Math.max(this.diameter, distance); + } + + return maxHeight1; + } + + public int diameter(Node root) { + this.diameter = 0; + height(root); + return diameter; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(N)$ +- Space complexity: $O(N)$ + +> Where $N$ is the number of nodes in the tree. + +--- + +## 2. Distance with Depth + +The **depth** of a node is the length of the path to the **root** node. + +::tabs-start + +```python + +class Solution: + def diameter(self, root: 'Node') -> int: + diameter = 0 + + def maxDepth(node, curr_depth): + """ return the maximum depth of leaves nodes + descending from the current node + """ + nonlocal diameter + + if len(node.children) == 0: + return curr_depth + + # select the top 2 depths from its children + max_depth_1, max_depth_2 = curr_depth, 0 + for child in node.children: + depth = maxDepth(child, curr_depth+1) + if depth > max_depth_1: + max_depth_1, max_depth_2 = depth, max_depth_1 + elif depth > max_depth_2: + max_depth_2 = depth + + # calculate the distance between the two farthest leaves nodes + distance = max_depth_1 + max_depth_2 - 2 * curr_depth + diameter = max(diameter, distance) + + return max_depth_1 + + maxDepth(root, 0) + return diameter +``` + +```java +class Solution { + protected int diameter = 0; + + /** + * return the maximum depth of leaves nodes descending from the given node + */ + protected int maxDepth(Node node, int currDepth) { + if (node.children.size() == 0) + return currDepth; + + // select the top two largest depths + int maxDepth1 = currDepth, maxDepth2 = 0; + for (Node child : node.children) { + int depth = maxDepth(child, currDepth + 1); + if (depth > maxDepth1) { + maxDepth2 = maxDepth1; + maxDepth1 = depth; + } else if (depth > maxDepth2) { + maxDepth2 = depth; + } + // calculate the distance between the two farthest leaves nodes. + int distance = maxDepth1 + maxDepth2 - 2 * currDepth; + this.diameter = Math.max(this.diameter, distance); + } + + return maxDepth1; + } + + public int diameter(Node root) { + this.diameter = 0; + maxDepth(root, 0); + return diameter; + } +} +``` + +```cpp +class Solution { +protected: + int diameter = 0; + + /** + * return the maximum depth of leaves nodes descending from the given node + */ + int maxDepth(Node* node, int currDepth) { + if (node->children.size() == 0) + return currDepth; + + // select the top two largest depths + int maxDepth1 = currDepth, maxDepth2 = 0; + for (Node* child : node->children) { + int depth = maxDepth(child, currDepth + 1); + if (depth > maxDepth1) { + maxDepth2 = maxDepth1; + maxDepth1 = depth; + } else if (depth > maxDepth2) { + maxDepth2 = depth; + } + // calculate the distance between the two farthest leaves nodes. + int distance = maxDepth1 + maxDepth2 - 2 * currDepth; + this->diameter = max(this->diameter, distance); + } + return maxDepth1; + } + +public: + int diameter(Node* root) { + this->diameter = 0; + maxDepth(root, 0); + return diameter; + } +}; +``` + +```javascript +class Solution { + /** + * @param {_Node} root + * @return {number} + */ + diameter(root) { + let diameter = 0; + + const maxDepth = (node, curr_depth) => { + /* return the maximum depth of leaves nodes + descending from the current node + */ + if (node.children.length === 0) { + return curr_depth; + } + + // select the top 2 depths from its children + let max_depth_1 = curr_depth, max_depth_2 = 0; + for (const child of node.children) { + const depth = maxDepth(child, curr_depth + 1); + if (depth > max_depth_1) { + max_depth_2 = max_depth_1; + max_depth_1 = depth; + } else if (depth > max_depth_2) { + max_depth_2 = depth; + } + } + + // calculate the distance between the two farthest leaves nodes + const distance = max_depth_1 + max_depth_2 - 2 * curr_depth; + diameter = Math.max(diameter, distance); + + return max_depth_1; + }; + + maxDepth(root, 0); + return diameter; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +- Time complexity: $O(N)$ +- Space complexity: $O(N)$ + +> Where $N$ is the number of nodes in the tree. From 7d32447cff17e8c62da26d31c62624cccffce931 Mon Sep 17 00:00:00 2001 From: ranveersingh2718 Date: Thu, 11 Dec 2025 19:18:46 -0800 Subject: [PATCH 15/15] add find-the-celebrity.md article --- articles/find-the-celebrity.md | 336 +++++++++++++++++++++++++++++++++ 1 file changed, 336 insertions(+) create mode 100644 articles/find-the-celebrity.md diff --git a/articles/find-the-celebrity.md b/articles/find-the-celebrity.md new file mode 100644 index 000000000..1dbcaf89d --- /dev/null +++ b/articles/find-the-celebrity.md @@ -0,0 +1,336 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def findCelebrity(self, n: int) -> int: + self.n = n + for i in range(n): + if self.is_celebrity(i): + return i + return -1 + + def is_celebrity(self, i): + for j in range(self.n): + if i == j: continue # Don't ask if they know themselves. + if knows(i, j) or not knows(j, i): + return False + return True +``` + +```java +public class Solution extends Relation { + + private int numberOfPeople; + + public int findCelebrity(int n) { + numberOfPeople = n; + for (int i = 0; i < n; i++) { + if (isCelebrity(i)) { + return i; + } + } + return -1; + } + + private boolean isCelebrity(int i) { + for (int j = 0; j < numberOfPeople; j++) { + if (i == j) continue; // Don't ask if they know themselves. + if (knows(i, j) || !knows(j, i)) { + return false; + } + } + return true; + } +} +``` + +```javascript +function solution(knows) { + function isCelebrity(i, n) { + for (let j = 0; j < n; j++) { + if (i === j) continue; + if (knows(i, j) || !knows(j, i)) { + return false; + } + } + return true; + } + + return function findCelebrity(n) { + for (let i = 0; i < n; i++) { + if (isCelebrity(i, n)) { + return i; + } + } + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +We don't know what time and space the `knows(...)` API uses. Because it's not our concern, we'll assume it's `O(1)` for the purpose of analysing our algorithm. + +- Time complexity: $O(n^2)$ +- Space complexity: $O(1)$ + +> Where $n$ is the number of nodes in the graph. + +--- + +## 2. Logical Deduction + +::tabs-start + +```python +class Solution: + def findCelebrity(self, n: int) -> int: + self.n = n + celebrity_candidate = 0 + for i in range(1, n): + if knows(celebrity_candidate, i): + celebrity_candidate = i + if self.is_celebrity(celebrity_candidate): + return celebrity_candidate + return -1 + + def is_celebrity(self, i): + for j in range(self.n): + if i == j: continue + if knows(i, j) or not knows(j, i): + return False + return True +``` + +```java +public class Solution extends Relation { + + private int numberOfPeople; + + public int findCelebrity(int n) { + numberOfPeople = n; + int celebrityCandidate = 0; + for (int i = 0; i < n; i++) { + if (knows(celebrityCandidate, i)) { + celebrityCandidate = i; + } + } + if (isCelebrity(celebrityCandidate)) { + return celebrityCandidate; + } + return -1; + } + + private boolean isCelebrity(int i) { + for (int j = 0; j < numberOfPeople; j++) { + if (i == j) continue; // Don't ask if they know themselves. + if (knows(i, j) || !knows(j, i)) { + return false; + } + } + return true; + } +} +``` + +```cpp +class Solution { +private: + int n; + + bool is_celebrity(int i) { + for (int j = 0; j < n; j++) { + if (i == j) continue; + if (knows(i, j) || !knows(j, i)) { + return false; + } + } + return true; + } + +public: + int findCelebrity(int n) { + this->n = n; + int celebrity_candidate = 0; + + for (int i = 1; i < n; i++) { + if (knows(celebrity_candidate, i)) { + celebrity_candidate = i; + } + } + + if (is_celebrity(celebrity_candidate)) { + return celebrity_candidate; + } + return -1; + } +}; +``` + +```javascript +function solution(knows) { + function isCelebrity(i, n) { + for (let j = 0; j < n; j++) { + if (i === j) continue; + if (knows(i, j) || !knows(j, i)) { + return false; + } + } + return true; + } + + return function findCelebrity(n) { + let celebrityCandidate = 0; + for (let i = 0; i < n; i++) { + if (knows(celebrityCandidate, i)) { + celebrityCandidate = i; + } + } + if (isCelebrity(celebrityCandidate, n)) { + return celebrityCandidate; + } + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +We don't know what time and space the `knows(...)` API uses. Because it's not our concern, we'll assume it's `O(1)` for the purpose of analysing our algorithm. + +- Time complexity: $O(n)$ +- Space complexity: $O(1)$ + +> Where $n$ is the number of nodes in the graph. + +--- + +## 3. Logical Deduction with Caching + +::tabs-start + +```python +from functools import lru_cache + +class Solution: + + @lru_cache(maxsize=None) + def cachedKnows(self, a, b): + return knows(a, b) + + def findCelebrity(self, n: int) -> int: + self.n = n + celebrity_candidate = 0 + for i in range(1, n): + if self.cachedKnows(celebrity_candidate, i): + celebrity_candidate = i + if self.is_celebrity(celebrity_candidate): + return celebrity_candidate + return -1 + + def is_celebrity(self, i): + for j in range(self.n): + if i == j: continue + if self.cachedKnows(i, j) or not self.cachedKnows(j, i): + return False + return True +``` + +```java +public class Solution extends Relation { + + private int numberOfPeople; + private Map, Boolean> cache = new HashMap<>(); + + @Override + public boolean knows(int a, int b) { + if (!cache.containsKey(new Pair(a, b))) { + cache.put(new Pair(a, b), super.knows(a, b)); + } + return cache.get(new Pair(a, b)); + } + + public int findCelebrity(int n) { + numberOfPeople = n; + int celebrityCandidate = 0; + for (int i = 0; i < n; i++) { + if (knows(celebrityCandidate, i)) { + celebrityCandidate = i; + } + } + if (isCelebrity(celebrityCandidate)) { + return celebrityCandidate; + } + return -1; + } + + private boolean isCelebrity(int i) { + for (int j = 0; j < numberOfPeople; j++) { + if (i == j) continue; // Don't ask if they know themselves. + if (knows(i, j) || !knows(j, i)) { + return false; + } + } + return true; + } +} +``` + +```javascript +function cached(f) { + const cache = new Map(); + return function(...args) { + const cacheKey = args.join(','); + if (!cache.has(cacheKey)) { + const value = f(...args); + cache.set(cacheKey, value); + } + + return cache.get(cacheKey); + } +} + +function solution(knows) { + knows = cached(knows); + + function isCelebrity(i, n) { + for (let j = 0; j < n; j++) { + if (i === j) continue; + if (knows(i, j) || !knows(j, i)) { + return false; + } + } + return true; + } + + return function findCelebrity(n) { + let celebrityCandidate = 0; + for (let i = 0; i < n; i++) { + if (knows(celebrityCandidate, i)) { + celebrityCandidate = i; + } + } + if (isCelebrity(celebrityCandidate, n)) { + return celebrityCandidate; + } + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +We don't know what time and space the `knows(...)` API uses. Because it's not our concern, we'll assume it's `O(1)` for the purpose of analysing our algorithm. + +- Time complexity: $O(n)$ +- Space complexity: $O(n)$ + +> Where $n$ is the number of nodes in the graph.