Tree first 50
108. Convert Sorted Array to Binary Search Tree Given an array where elements are sorted in ascending order, convert it to a height balanced BST. For this problem, a height-balanced binary tree is defined as a binary tree in which the depth of the two subtrees of every node never differ by more than 1. Given the sorted array: [-10,-3,0,5,9], One possible answer is: [0,-3,9,-10,null,5]
recursively build the balanced tree. class Solution { public: TreeNode* sortedArrayToBST(vector<int>& nums) { int size = nums.size(); return build(nums, 0, size-1); } TreeNode* build(vector<int>& nums, int start, int end) { if(start > end) return NULL; if(start == end) return new TreeNode(nums[start]); int mid = (start + end) / 2; TreeNode* root = new TreeNode(nums[mid]); root ->left = build(nums, start, mid-1); root ->right = build(nums, mid+1, end); return root; } };
99. Recover Binary Search Tree Two elements of a binary search tree (BST) are swapped by mistake. Recover the tree without changing its structure. nput: [1,3,null,null,2] 1 / 3 \ 2 Output: [3,1,null,null,2]
1. use map to record the correct order with pairs of ori index and node. 2. inorder traversal, then find the wrong ordered node and then reconstruct. 3. use stack to find the two wrong node. /** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} * }; */ class Solution { public: void recoverTree(TreeNode* root) { stack<TreeNode*> s; TreeNode* a = NULL; TreeNode* b = NULL; TreeNode* prev = NULL; TreeNode* curr = root; while(curr != NULL) { s.push(curr); curr = curr->left; } while(! s.empty()) { TreeNode* node = s.top(); s.pop(); if(node == NULL) continue; if(prev != NULL && node->val < prev->val) { b = node; if(a == NULL) a = prev; } prev = node; if(node->right!= NULL) { node = node->right; while(node != NULL) { s.push(node); node = node->left; } } } swap(a, b); } void swap(TreeNode*& n1, TreeNode*& n2) { int v1 = n1->val; n1->val = n2->val; n2->val = v1; } void recoverTree1(TreeNode* root) { //inorder vector<int> arr; inorder(root, arr); int x, y; findElement(arr, x, y); construct(root, 2, x ,y); } void construct(TreeNode* root, int count, int x, int y) { if(root == NULL) return; if(root->val == x || root->val == y) { root ->val = (root->val == x? y: x); count --; if(count == 0) return; } construct(root->left, count, x, y); construct(root->right, count, x, y); } void findElement(vector<int>& arr, int& x, int& y) { x = -1; y = -1; for(int i = 0; i < arr.size(); i ++) { if(i + 1 < arr.size() && arr[i+1] < arr[i]) { y = arr[i+1]; if(x == -1) x = arr[i]; } } } void inorder(TreeNode* root, vector<int>& arr) { if(root == NULL) return; inorder(root->left, arr); arr.push_back(root->val); inorder(root->right, arr); } };
101. Symmetric Tree Given a binary tree, check whether it is a mirror of itself (ie, symmetric around its center). For example, this binary tree [1,2,2,3,4,4,3] is symmetric: 1 / \ 2 2 / \ / \ 3 4 4 3
2 ways: 1. recursively. 2. iteratively. class Solution { public: bool isSymmetric(TreeNode* root) { if(root == NULL) return true; stack<TreeNode*> s; s.push(root->left); s.push(root->right); while(! s.empty()) { TreeNode* n1 = s.top(); s.pop(); TreeNode* n2 = s.top(); s.pop(); if(n1 == NULL ^ n2 == NULL) return false; if(n1 == NULL && n2 == NULL) continue; if(n1->val != n2->val) return false; s.push(n1->left); s.push(n2->right); s.push(n1->right); s.push(n2->left); } return true; } bool isSymmetric1(TreeNode* root) { //recursively if(root == NULL) return true; return isSame(root->left, root->right); } bool isSame(TreeNode* n1, TreeNode* n2) { if(n1 == NULL ^ n2 == NULL) return false; if(n1 == NULL && n2 == NULL) return true; if(n1->val != n2->val) return false; if(! isSame(n1->left, n2->right)) return false; if(! isSame(n1->right, n2->left)) return false; return true; } };
156. Binary Tree Upside Down Given a binary tree where all the right nodes are either leaf nodes with a sibling (a left node that shares the same parent node) or empty, flip it upside down and turn it into a tree where the original right nodes turned into left leaf nodes. Return the new root. 1 / \ 2 3 / \ 4 5 Output: return the root of the binary tree [4,5,2,#,#,3,1] 4 / \ 5 2 / \ 3 1
2 ways: 1. use stack and keep prev node. 2. recursively class Solution { public: TreeNode* upsideDownBinaryTree(TreeNode* root) { if(root == NULL) return NULL; return solve(root).first; } pair<TreeNode*, TreeNode*> solve(TreeNode* root) { if(root == NULL) return pair(root, root); if(root ->left == NULL && root -> right == NULL) return pair(root, root); pair<TreeNode*, TreeNode*> l = solve(root->left); l.second->right = root; l.second ->left = root->right; root -> left = NULL; root ->right = NULL; return pair(l.first, root); } TreeNode* upsideDownBinaryTree1(TreeNode* root) { //use stack if(root == NULL) return NULL; stack<TreeNode*> s; TreeNode* curr = root; while(curr != NULL) { s.push(curr); curr = curr->left; } TreeNode* res = s.top(); TreeNode* prev = NULL; while(!s.empty()) { TreeNode* node = s.top(); s.pop(); if(prev != NULL) { prev -> right = node; prev -> left = node->right; } prev = node; prev -> left = NULL; prev -> right = NULL; } return res; } };
437. Path Sum III Find the number of paths that sum to a given value. The path does not need to start or end at the root or a leaf, but it must go downwards (traveling only from parent nodes to child nodes). 10 / \ 5 -3 / \ \ 3 2 11 / \ \ 3 -2 1 Return 3. The paths that sum to 8 are: 1. 5 -> 3 2. 5 -> 2 -> 1 3. -3 -> 11
both set/ multiset/ vector have function: count to calc the count of val. class Solution { private: int ans = 0; public: int pathSum(TreeNode* root, int sum) { // multiset<int> sums = {0}; vector<int> sums = {0}; solve(root, sum, 0, sums); return ans; } void solve(TreeNode* root, int target, int sum, vector<int>& sums) { if(root == NULL) return; int v = root->val; sum += v; ans += count(sums.begin(), sums.end(), sum - target); sums.push_back(sum); solve(root->left, target, sum, sums); solve(root->right, target, sum, sums); sums.pop_back(); } };
98. Validate Binary Search Tree Given a binary tree, determine if it is a valid binary search tree (BST). Assume a BST is defined as follows: The left subtree of a node contains only nodes with keys less than the node's key. The right subtree of a node contains only nodes with keys greater than the node's key. Both the left and right subtrees must also be binary search trees. 2 / \ 1 3 Input: [2,1,3] Output: true
inorder traversal and check whether ordered. class Solution { public: bool isValidBST(TreeNode* root) { vector<int> arr; inorder(root, arr); if(arr.size() < 2) return true; for(int i = 1; i < arr.size(); i ++) { if(arr[i] <= arr[i-1]) return false; } return true; } void inorder(TreeNode* root, vector<int>& res) { if(root == NULL) return ; inorder(root->left, res); res.push_back(root->val); inorder(root->right, res); } };
100. Same Tree Given two binary trees, write a function to check if they are the same or not. Two binary trees are considered the same if they are structurally identical and the nodes have the same value. [1,2,3], [1,2,3]
recursively check val== val, left == left, right == right. class Solution { public: bool isSameTree(TreeNode* p, TreeNode* q) { return isSame(p, q); } bool isSame(TreeNode* p, TreeNode* q) { if( (p == NULL ^q == NULL)) return false; else if(p == NULL && q == NULL) return true; if(!isSame(p->left, q->left)) return false; if(p->val != q->val) return false; if(!isSame(p->right, q->right)) return false; return true; } };
226. Invert Binary Tree / \ 2 7 / \ / \ 1 3 6 9 Output: 4 / \ 7 2 / \ / \ 9 6 3 1
recursively invert. class Solution { public: TreeNode* invertTree(TreeNode* root) { if(root == NULL) return NULL; if(root->left == NULL && root -> right == NULL) return root; invertTree(root->left); invertTree(root->right); TreeNode* left = root->left; root ->left = root -> right; root->right = left; return root; } };
117. Populating Next Right Pointers in Each Node II Populate each next pointer to point to its next right node. If there is no next right node, the next pointer should be set to NULL.
same as #116. class Solution { public: Node* connect(Node* root) { unordered_map<int, vector<Node*> > levelMap; queue< pair<int, Node*> > q; q.push(pair(0, root)); while(! q.empty()) { pair<int, Node*> curr = q.front(); q.pop(); int level = curr.first; Node* node = curr.second; if(node == NULL) continue; levelMap[level].push_back(node); q.push(pair(level+1, node->left)); q.push(pair(level+1, node->right)); } for(auto& [level, arr]: levelMap) { for(int i = 1; i < arr.size(); i ++) { arr[i-1]->next = arr[i]; } } return root; } };
107. Binary Tree Level Order Traversal II Given a binary tree, return the bottom-up level order traversal of its nodes' values. (ie, from left to right, level by level from leaf to root). For example: Given binary tree [3,9,20,null,null,15,7], 3 / \ 9 20 / \ 15 7
same as level order. use map with comparer greater<int> class Solution { private: map<int, vector<int>, greater<int>> levelMap; public: vector<vector<int>> levelOrderBottom(TreeNode* root) { if(root == NULL) return {}; queue<pair<int, TreeNode*>> q; q.push(pair(0, root)); while(! q.empty()) { pair<int, TreeNode*> curr = q.front(); q.pop(); int level = curr.first; TreeNode* node = curr.second; if(node == NULL) continue; levelMap[level].push_back(node->val); q.push(pair(level+1, node->left)); q.push(pair(level+1, node->right)); } vector<vector<int>> res; for(auto& [level, arr]: levelMap) { res.push_back(arr); } return res; } };
111. Minimum Depth of Binary Tree The minimum depth is the number of nodes along the shortest path from the root node down to the nearest leaf node. 3 / \ 9 20 / \ 15 7 return its minimum depth = 2.
same as max depth. class Solution { public: int minDepth(TreeNode* root) { if(root == NULL) return 0; return solve(root); } int solve(TreeNode* root) { if(root == NULL) return INT_MAX; if(root ->left == NULL && root-> right == NULL) return 1; int l = solve(root->left); int r = solve(root->right); return 1 + min(l, r); } };
113. Path Sum II Given a binary tree and a sum, find all root-to-leaf paths where each path's sum equals the given sum. 5 / \ 4 8 / / \ 11 13 4 / \ / \ 7 2 5 1 [ [5,4,11,2], [5,8,4,5] ]
similar to #112 path sum class Solution { private: vector<vector<int>> res; public: vector<vector<int>> pathSum(TreeNode* root, int sum) { vector<int> tmp; solve(root, sum, tmp); return res; } void solve(TreeNode* root, int sum, vector<int>& tmp) { if(root == NULL) return; if(root -> left == NULL && root->right == NULL) { if(root->val == sum) { tmp.push_back(sum); res.push_back(tmp); tmp.pop_back(); } return; } tmp.push_back(root->val); solve(root->left, sum - root->val, tmp); solve(root->right, sum - root->val, tmp); tmp.pop_back(); } };
428. Serialize and Deserialize N-ary Tree Design an algorithm to serialize and deserialize an N-ary tree. An N-ary tree is a rooted tree in which each node has no more than N children. There is no restriction on how your serialization/deserialization algorithm should work. You just need to ensure that an N-ary tree can be serialized to a string and this string can be deserialized to the original tree structure. as [1 [3[5 6] 2 4]]. You do not necessarily need to follow this format, so please be creative and come up with different approaches yourself.
the key is, n-ary tree don't have confirmed children. during serialization, we use val$count to stand for node. class Codec { public: // Encodes a tree to a single string. string serialize(Node* root) { string res; if(root == NULL) return "#"; queue<Node*> q; q.push(root); while(! q.empty()) { Node* curr = q.front(); q.pop(); vector<Node*> children = curr->children; res += to_string(curr->val) + "$" + to_string(children.size()) + " "; for(auto& child: children) { q.push(child); } } return res; } // Decodes your encoded data to tree. Node* deserialize(string data) { if(data == "#") return NULL; vector<pair<int,int>> tokens = getTokens(data); unordered_map<Node*, int> childCount; int ind = 0; Node* root = new Node(tokens[ind].first); childCount[root] = tokens[ind].second; queue<Node*> q; q.push(root); ind ++; while(!q.empty()) { Node* curr = q.front(); q.pop(); int cnt = childCount[curr]; for(int i = 0; i < cnt; i ++) { Node* node = new Node(tokens[ind].first); childCount[node] = tokens[ind].second; ind ++; curr->children.push_back(node); q.push(node); } } return root; } vector<pair<int,int>> getTokens(string s) { vector<pair<int,int>> res; int pos = s.find(" "); while(pos != -1) { string sub = s.substr(0, pos); int pos1 = sub.find("$"); int val = stoi(sub.substr(0, pos1)); int cnt = stoi(sub.substr(pos1+1)); res.push_back(pair(val, cnt)); s.erase(0, pos+1); pos = s.find(" "); } return res; } };
235. Lowest Common Ancestor of a Binary Search Tree Example 1: Input: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8 Output: 6 Explanation: The LCA of nodes 2 and 8 is 6.
the tree is BST we can compare and return. class Solution { public: TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) { if(root == NULL) return root; int v1 = p->val; int v2 = q->val; if(v1 > v2) { int tmp = v1; v1 = v2; v2 = tmp; } return solve(root, v1, v2); } TreeNode* solve(TreeNode* root, int v1, int v2) { if(root == NULL) return NULL; int v = root->val; if(v == v1 || v == v2) return root; if(v1 < v && v2 > v) return root; if(v1 < v && v2 < v) return solve(root-> left, v1, v2); else if(v1 > v && v2 > v) return solve(root->right, v1, v2); return NULL; } };
129. Sum Root to Leaf Numbers Given a binary tree containing digits from 0-9 only, each root-to-leaf path could represent a number. An example is the root-to-leaf path 1->2->3 which represents the number 123. Find the total sum of all root-to-leaf numbers. Input: [1,2,3] 1 / \ 2 3 Output: 25
use depth search, until find leaf, add up class Solution { private: long res = 0; public: int sumNumbers(TreeNode* root) { long val = 0; if(root == NULL) return 0; solve(root, val); return res; } void solve(TreeNode* root, long val) { if(root == NULL) return; if(root->left == NULL && root->right == NULL) { val = val*10+ root->val; res += val; } solve(root->left, val*10 + root->val); solve(root->right, val*10+ root->val); } };
429. N-ary Tree Level Order Traversal Given an n-ary tree, return the level order traversal of its nodes' values. (ie, from left to right, level by level). For example, given a 3-ary tree [ [1], [3,2,4], [5,6] ]
use level map class Solution { public: vector<vector<int>> levelOrder(Node* root) { if(root == NULL) return {}; vector<vector<int>> res; queue<pair<int,Node*>> q; q.push(pair(0, root)); map<int, vector<int>> levelMap; while(! q.empty()) { pair<int, Node*> curr = q.front(); q.pop(); int level = curr.first; Node* node = curr.second; levelMap[level].push_back(node->val); for(auto& child: node->children) { q.push(pair(level+1, child)); } } for(auto& [level, arr]: levelMap) res.push_back(arr); return res; } };
102. Binary Tree Level Order Traversal Given a binary tree, return the level order traversal of its nodes' values. (ie, from left to right, level by level). Given binary tree [3,9,20,null,null,15,7], 3 / \ 9 20 / \ 15 7 [ [3], [9,20], [15,7] ]
use map class Solution { public: vector<vector<int>> levelOrder(TreeNode* root) { if(root == NULL) return {}; map<int, vector<int>> levelMap; queue< pair<int, TreeNode*> > q; q.push(pair(0, root)); while(! q.empty()) { pair<int, TreeNode*> curr = q.front(); q.pop(); int level = curr.first; TreeNode* node = curr.second; if(node == NULL) continue; levelMap[level].push_back(node->val); q.push(pair(level+1, node->left)); q.push(pair(level+1, node->right)); } vector<vector<int>> res; for(auto& [level, arr]: levelMap) { res.push_back(arr); } return res; } };
404. Sum of Left Leaves Find the sum of all left leaves in a given binary tree. 3 / \ 9 20 / \ 15 7 There are two left leaves in the binary tree, with values 9 and 15 respectively. Return 24.
use recursive func. func also pass one bool: isLeft. class Solution { private: int ans = 0; public: int sumOfLeftLeaves(TreeNode* root) { solve(root, false); return ans; } void solve(TreeNode* root, bool isLeft) { if(root == NULL) return; if(root -> left == NULL && root->right == NULL) { if(isLeft) ans += root->val; return; } solve(root->left, true); solve(root->right, false); } };
110. Balanced Binary Tree For this problem, a height-balanced binary tree is defined as: a binary tree in which the depth of the two subtrees of every node never differ by more than 1. Given the following tree [3,9,20,null,null,15,7]: 3 / \ 9 20 / \ 15 7
we check whether child tree is valid and the depth diff between left and right child <= 1; class Solution { public: bool isBalanced(TreeNode* root) { pair<bool, int> res = getDepth(root); return res.first; } pair<bool, int> getDepth(TreeNode* root) { if(root == NULL) return { true, -1}; pair<bool, int> left = getDepth(root->left); pair<bool, int> right = getDepth(root->right); int val = max(left.second, right.second) + 1; if(!left.first | !right.first) return pair(false, val); if(abs(left.second - right.second) > 1) return pair(false, val); return pair(true, val); } };
116. Populating Next Right Pointers in Each Node You are given a perfect binary tree where all leaves are on the same level, and every parent has two children. The binary tree has the following definition:
we get levelMap and then on each level, arr[i]->next = arr[i+1]; class Solution { public: Node* connect(Node* root) { unordered_map<int, vector<Node*> > levelMap; queue< pair<int, Node*> > q; q.push(pair(0, root)); while(! q.empty()) { pair<int, Node*> curr = q.front(); q.pop(); int level = curr.first; Node* node = curr.second; if(node == NULL) continue; levelMap[level].push_back(node); q.push(pair(level+1, node->left)); q.push(pair(level+1, node->right)); } for(auto& [level, arr]: levelMap) { for(int i = 1; i < arr.size(); i ++) { arr[i-1]->next = arr[i]; } } return root; } };
236. Lowest Common Ancestor of a Binary Tree Given a binary tree, find the lowest common ancestor (LCA) of two given nodes in the tree. According to the definition of LCA on Wikipedia: "The lowest common ancestor is defined between two nodes p and q as the lowest node in T that has both p and q as descendants (where we allow a node to be a descendant of itself)." Input: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1 Output: 3
we have two ways: 1. get the path to the two nodes and find first common node. 2. recursively. use recursive func, return whether node is in current root tree. class Solution { private: unordered_map<int, int> parent; vector<TreeNode*> path1; vector<TreeNode*> path2; TreeNode* ans; public: TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) { solve(root, p->val, q->val); return ans; } bool solve(TreeNode* root, int v1, int v2) { if(root == NULL) return false; int l = solve(root ->left, v1, v2); int r = solve(root->right, v1, v2); int mid = (root->val == v1 || root->val == v2); if(l +r + mid >= 2) { ans = root; } return (l + r + mid > 0); } TreeNode* lowestCommonAncestor1(TreeNode* root, TreeNode* p, TreeNode* q) { //find the path then compare if(root == NULL) return NULL; vector<TreeNode*> tmp; findPath(root, p->val, q->val, tmp); set<TreeNode*> nodeSet(path1.begin(), path1.end()); for(int i = path2.size()-1; i >= 0; i --) { if(nodeSet.find(path2[i]) != nodeSet.end()) return path2[i]; } return NULL; } void findPath(TreeNode* root, int v1, int v2, vector<TreeNode*>& tmp) { if(root == NULL) return; int v = root->val; tmp.push_back(root); if(v == v1) { path1 = tmp; if(! path2.empty()) return; } else if(v == v2) { path2 = tmp; if(! path1.empty()) return; } findPath(root->left, v1, v2, tmp); findPath(root->right, v1, v2, tmp); tmp.pop_back(); } };
297. Serialize and Deserialize Binary Tree Design an algorithm to serialize and deserialize a binary tree. There is no restriction on how your serialization/deserialization algorithm should work. You just need to ensure that a binary tree can be serialized to a string and this string can be deserialized to the original tree structure. 1 / \ 2 3 / \ 4 5 as "[1,2,3,null,null,4,5]"
we have two ways: 1. use bfs and queue. 2. use dfs and recursive func class Codec { public: // Encodes a tree to a single string. string serialize(TreeNode* root) { if(root == NULL) return "#"; string s = bfs(root); s.pop_back(); return s; } string bfs(TreeNode* root) { if(root == NULL) return "# "; string l = bfs(root->left); string r = bfs(root->right); return to_string(root->val) + " " + l + r; } TreeNode* deserialize(string data) { if(data == "#") return NULL; vector<string> tokens = getTokens(data); int ind = 0; return solve(tokens, ind); } TreeNode* solve(vector<string>& tokens, int& ind) { if(tokens[ind] == "#") { ind++; return NULL; } TreeNode* root = new TreeNode(stoi(tokens[ind++])); root -> left = solve(tokens, ind); root -> right = solve(tokens, ind); return root; } string serialize1(TreeNode* root) { //bfs using queue string res; if(root == NULL) return "#"; queue<TreeNode*> q; q.push(root); while(! q.empty()) { TreeNode* curr = q.front(); q.pop(); if(curr == NULL) { res += "# "; continue; } res += to_string(curr->val) + " "; q.push(curr->left); q.push(curr->right); } res.pop_back(); return res; } // Decodes your encoded data to tree. TreeNode* deserialize1(string data) { if(data == "#") return NULL; vector<string> tokens = getTokens(data); int ind = 0; TreeNode* root = new TreeNode(stoi(tokens[ind])); ind ++; queue<TreeNode*> q; q.push(root); while(! q.empty()) { TreeNode* curr = q.front(); q.pop(); if(curr == NULL) continue; string l = tokens[ind++]; string r = tokens[ind++]; TreeNode* left = NULL; TreeNode* right = NULL; if(l != "#") { left = new TreeNode(stoi(l)); q.push(left); } if(r != "#") { right = new TreeNode(stoi(r)); q.push(right); } curr -> left = left; curr->right = right; } return root; } vector<string> getTokens(string s) { int pos = s.find(" "); vector<string> res; while(pos != -1) { res.push_back(s.substr(0, pos)); s.erase(0, pos+1); pos = s.find(" "); } res.push_back(s); return res; } };
257. Binary Tree Paths Given a binary tree, return all root-to-leaf paths. Note: A leaf is a node with no children. 1 / \ 2 3 \ 5 Output: ["1->2->5", "1->3"]
we recursively adding current node to the path. class Solution { private: vector<string> res; public: vector<string> binaryTreePaths(TreeNode* root) { if(root == NULL) return {}; solve(root, ""); return res; } void solve(TreeNode* root, string tmp) { if(root == NULL) return; if(root -> left == NULL && root->right == NULL) { tmp += to_string(root->val); res.push_back(tmp); } tmp += to_string(root->val) + "->"; solve(root->left, tmp); solve(root->right, tmp); } };
112. Path Sum Given a binary tree and a sum, determine if the tree has a root-to-leaf path such that adding up all the values along the path equals the given sum. Given the below binary tree and sum = 22, 5 / \ 4 8 / / \ 11 13 4 / \ \ 7 2 1
we recursively check whether current node is leaf, or left child/ right child is true. class Solution { public: bool hasPathSum(TreeNode* root, int sum) { return solve(root, sum); } bool solve(TreeNode* root, int sum) { if(root == NULL) { return false; } if(root ->left == NULL && root->right == NULL) { if(sum == root->val) return true; return false; } if(solve(root->left, sum - root->val)) return true; if(solve(root->right, sum - root->val)) return true; return false; } };
104. Maximum Depth of Binary Tree The maximum depth is the number of nodes along the longest path from the root node down to the farthest leaf node. 3 / \ 9 20 / \ 15 7 return its depth = 3.
we recursively get the left , right child depth. class Solution { private: unordered_map<TreeNode*, int> memo; public: int maxDepth(TreeNode* root) { if(root == NULL) return 0; if(root->left == 0 && root->right == 0) return 1; if(memo.find(root) != memo.end()) return memo[root]; int l = maxDepth(root->left); int r = maxDepth(root->right); memo[root] = 1 + l + r; return 1 + max(l, r); } };
366. Find Leaves of Binary Tree Given a binary tree, collect a tree's nodes as if you were doing this: Collect and remove all leaves, repeat until the tree is empty. Input: [1,2,3,4,5] 1 / \ 2 3 / \ 4 5 Output: [[4,5,3],[2],[1]]
we store parent and children count. get leaves and push to queue, then pop each node. during pop current node, update parent, if parent is next leave, push to queue. class Solution { private: unordered_map<TreeNode*, TreeNode*> parent; unordered_map<TreeNode*, int> children; vector<vector<int>> ans; public: vector<vector<int>> findLeaves(TreeNode* root) { if(root == NULL) return {}; vector<TreeNode*> leaves; solve(root, NULL, leaves); queue<TreeNode*> q; for(auto& node: leaves) q.push(node); while(! q.empty()) { vector<TreeNode*> cache; vector<int> tmp; int size = q.size(); for(int i = 0; i< size; i ++) { TreeNode* curr = q.front(); q.pop(); if(curr == NULL) continue; tmp.push_back(curr->val); TreeNode* pa = parent[curr]; children[pa] --; if(children[pa] == 0) cache.push_back(pa); } ans.push_back(tmp); for(auto& node: cache) q.push(node); } return ans; } void solve(TreeNode* root, TreeNode* p, vector<TreeNode*>& leaves) { if(root == NULL) return; parent[root] = p; children[root] = (root->left == NULL? 0: 1) + (root-> right == NULL? 0: 1); if(root -> left == NULL && root -> right == NULL) { leaves.push_back(root); return; } solve(root->left, root, leaves); solve(root->right, root, leaves); } };
333. Largest BST Subtree Given a binary tree, find the largest subtree which is a Binary Search Tree (BST), where largest means subtree with largest number of nodes in it. Input: [10,5,15,1,8,null,7] 10 / \ 5 15 / \ \ 1 8 7 Output: 3
we use a new struct resNode to record the func result. it contains min, max, valid, size. struct resNode { int minV; int maxV; int size; bool valid; resNode() {}; resNode(int minV, int maxV, int size, bool valid): minV(minV), maxV(maxV), size(size), valid(valid) {}; }; class Solution { private: int ans = 0; public: int largestBSTSubtree(TreeNode* root) { if(root == NULL) return ans; solve(root); return ans; } resNode solve(TreeNode* root) { if(root == NULL) return resNode(INT_MIN, INT_MIN, 0, true); if(root -> left == NULL && root -> right == NULL) { ans = max(ans, 1); return resNode(root->val, root->val, 1, true); } int v = root->val; resNode l = solve(root -> left); resNode r = solve(root->right); int minL = l.minV; int maxL = l.maxV; int minR = r.minV; int maxR = r.maxV; if(!l.valid || !r.valid) return resNode(-1,-1, 0, false); if(root->left == NULL) { if(minR <= v) return resNode(-1, -1, 0, false); ans = max(ans, 1+r.size); return resNode(v, maxR, 1+r.size, true); } else if(root->right == NULL) { if(maxL >= v) return resNode(-1, -1, 0, false); ans = max(ans, 1 + l.size); return resNode(minL, v, 1+l.size, true); } bool valid = (maxL < v) && (minR > v); if(valid) ans = max(ans, 1+l.size+r.size); return resNode((minL == INT_MIN? v: minL), (maxR == INT_MIN? v: maxR), 1+l.size+r.size, valid); } };
199. Binary Tree Right Side View Given a binary tree, imagine yourself standing on the right side of it, return the values of the nodes you can see ordered from top to bottom. Example: Input: [1,2,3,null,5,null,4] Output: [1, 3, 4] Explanation: 1 <--- / \ 2 3 <--- \ \ 5 4 <---
we use levelmap class Solution { public: vector<int> rightSideView(TreeNode* root) { if(root == NULL) return {}; map<int, vector<int>> levelMap; queue<pair<int,TreeNode*>> q; q.push(pair(0, root)); while(! q.empty()) { pair<int, TreeNode*> curr = q.front(); q.pop(); int level = curr.first; TreeNode* node = curr.second; if(node == NULL) continue; levelMap[level].push_back(node->val); q.push(pair(level+1, node->left)); q.push(pair(level+1, node->right)); } vector<int> res; for(auto& [level, arr]: levelMap) { res.push_back(arr.back()); } return res; } };
337. House Robber III The thief has found himself a new place for his thievery again. There is only one entrance to this area, called the "root." Besides the root, each house has one and only one parent house. After a tour, the smart thief realized that "all houses in this place forms a binary tree". It will automatically contact the police if two directly-linked houses were broken into on the same night. 3 / \ 2 3 \ \ 3 1 Output: 7
we use recursive func. func return pair value, first is not take the root, second is take the root. class Solution { private: int ans = 0; public: int rob(TreeNode* root) { solve(root); return ans; } pair<int,int> solve(TreeNode* root) { if(root == NULL) return pair(0, 0); if(root -> left == NULL && root->right == NULL) { ans = max(ans, root->val); return pair(0, root->val); } int v = root ->val; pair<int,int> l = solve(root->left); pair<int,int> r = solve(root -> right); int v1 = max(l.first, l.second) + max(r.first, r.second); int v2 = v + l.first +r.first; ans = max(ans, max(v1, v2)); return pair(v1, v2); } };
250. Count Univalue Subtrees Given a binary tree, count the number of uni-value subtrees. A Uni-value subtree means all nodes of the subtree have the same value. Input: root = [5,1,5,5,5,null,5] 5 / \ 1 5 / \ \ 5 5 5 Output: 4
we use recursive func. func return the min max pair. class Solution { private: int ans = 0; public: int countUnivalSubtrees(TreeNode* root) { solve(root); return ans; } pair<int,int> solve(TreeNode* root) { if(root == NULL) return pair(INT_MIN, INT_MIN); if(root->left == NULL && root->right == NULL) { ans ++; return pair(root->val, root->val); } pair<int,int> l = solve(root->left); pair<int,int> r = solve(root->right); if(root -> left == NULL) l = r; else if(root -> right == NULL) r = l; int minV = min(l.first, r.first); int maxV = max(l.second, r.second); if(minV == maxV && minV == root->val) ans ++; return pair(min(root->val, minV), max(root->val, maxV)); } };
298. Binary Tree Longest Consecutive Sequence Given a binary tree, find the length of the longest consecutive sequence path. The path refers to any sequence of nodes from some starting node to any node in the tree along the parent-child connections. The longest consecutive path need to be from parent to child (cannot be the reverse). 2 \ 3 / 2 / 1 Output: 2
we use recursive func. func will also pass down the parent node and parent max count. class Solution { private: int ans = 0; public: int longestConsecutive(TreeNode* root) { if(root == NULL) return ans; solve(root, NULL, 0); return ans; } void solve(TreeNode* root, TreeNode* parent, int prevCount) { if(root == NULL) return; int count = 1; if(parent != NULL && root->val == parent->val + 1) { count = prevCount + 1; } ans = max(ans, count); solve(root -> left, root, count); solve(root -> right, root, count); } };
285. Inorder Successor in BST Given a binary search tree and a node in it, find the in-order successor of that node in the BST. The successor of a node p is the node with the smallest key greater than p.val. Input: root = [2,1,3], p = 1 Output: 2
we use recursive func. the func pass down the successor node. if search left subtree, curr node is passed. if search right subtree, current successor is passed. class Solution { public: TreeNode* inorderSuccessor(TreeNode* root, TreeNode* p) { return solve(root, p->val, NULL); } TreeNode* solve(TreeNode* root, int target, TreeNode* successor) { if(root == NULL) return NULL; if(root ->val == target) { if(root -> right == NULL) return successor; else { TreeNode* node = root -> right; while(node -> left != NULL) node = node -> left; return node; } } if(target < root -> val) return solve(root -> left, target, root); return solve(root->right, target, successor); } };
124. Binary Tree Maximum Path Sum Given a non-empty binary tree, find the maximum path sum. For this problem, a path is defined as any sequence of nodes from some starting node to any node in the tree along the parent-child connections. The path must contain at least one node and does not need to go through the root. Input: [-10,9,20,null,null,15,7] -10 / \ 9 20 / \ 15 7 Output: 42
we use recursive function. func return the current max val of subtree. inside func, we calc subtree sum. class Solution { private: int res = INT_MIN; public: int maxPathSum(TreeNode* root) { solve(root); return res; } int solve(TreeNode* root) { if(root == NULL) return INT_MIN; if(root ->left == NULL && root -> right == NULL) { res = max(res, root->val); return root->val; } int l = solve(root->left); int r = solve(root->right); int maxV = max(l ,r); int sub = root -> val + (l < 0? 0: l) + (r < 0? 0: r); int global = root -> val + (maxV < 0? 0: maxV); res = max(res, max(sub, global)); return global; } };
114. Flatten Binary Tree to Linked List Given a binary tree, flatten it to a linked list in-place. For example, given the following tree: 1 / \ 2 5 / \ \ 3 4 6 1 \ 2 \ 3 \ 4 \ 5 \ 6
we use recursive function. function return the root and the last element. class Solution { public: void flatten(TreeNode* root) { if(root == NULL) return; solve(root); } pair<TreeNode*,TreeNode*> solve(TreeNode* root) { if(root == NULL) return {NULL, NULL}; if(root ->left == NULL && root->right == NULL) return {root, root}; pair<TreeNode*,TreeNode*> leftRes = solve(root->left); TreeNode* l = leftRes.first; TreeNode* endL = leftRes.second; TreeNode* right = root -> right; pair<TreeNode*,TreeNode*> rightRes = solve(root -> right); TreeNode* r = rightRes.first; TreeNode* endR = rightRes.second; if(l != NULL) { root -> right = l; endL -> right = r; } root -> left = NULL; return pair(root, endR == NULL? endL: endR); } };
431. Encode N-ary Tree to Binary Tree Design an algorithm to encode an N-ary tree into a binary tree and decode the binary tree to get the original N-ary tree. An N-ary tree is a rooted tree in which each node has no more than N children. Similarly, a binary tree is a rooted tree in which each node has no more than 2 children. There is no restriction on how your encode/decode algorithm should work. You just need to ensure that an N-ary tree can be encoded to a binary tree and this binary tree can be decoded to the original N-nary tree structure.
when convert n-ary to binary: we alternatively put children all to right or all to left. when covert binary ton-ary: we alternatively iterate right child or left child. class Codec { public: // Encodes an n-ary tree to a binary tree. TreeNode* encode(Node* root) { return convert(root, true); } TreeNode* convert(Node* root, bool toRight) { if(root == NULL) return NULL; TreeNode* treeRoot = new TreeNode(root->val); TreeNode* curr = treeRoot; for(auto& node: root->children) { TreeNode* tmp = convert(node, !toRight); if(toRight) curr->right = tmp; else curr->left = tmp; curr = tmp; } return treeRoot; } // Decodes your binary tree to an n-ary tree. Node* decode(TreeNode* root) { return revert(root, true); } Node* revert(TreeNode* treeRoot, bool toRight) { if(treeRoot == NULL) return NULL; Node* root = new Node(treeRoot->val, {}); TreeNode* curr = treeRoot; queue<TreeNode*> q; if(toRight) { while(curr->right != NULL) { q.push(curr->right); curr = curr->right; } } else { while(curr->left != NULL) { q.push(curr->left); curr = curr->left; } } while(! q.empty()) { TreeNode* treeNode = q.front(); q.pop(); Node* node = revert(treeNode, !toRight); root->children.push_back(node); } return root; } };