LeetCode - 二叉树和递归
概述
名词 | 性质 |
---|
111. Minimum Depth of Binary Tree*
226. Invert Binary Tree**
100. Same Tree
101. Symmetric Tree***
222. Count Complete Tree Nodes***
110. Invert Binary Tree**
112. Path Sum**(递归返回条件)
404. Sum of Left Leaves**(递归返回条件)
257. Binary Tree Paths**(返回路径,回溯中全局变量引发的问题)
113. Path Sum II**(返回路径,回溯)
129. Sum Root to Leaf Numbers***(10^k + val, k为深度,回溯)
437. Path Sum III**(任意路径总和)
235. Lowest Common Ancestor of a Binary Search Tree**(递归实现遍历)
98. Validate Binary Search Tree**(区间传递)
450. Delete Node in a BST***(ALG4的代码是节点替换,这里是赋值替换代码更好理解)
108. Convert Sorted Array to Binary Search Tree*(二分法DFS)
230. Kth Smallest Element in a BST**(中序遍历求第k值)
236. Lowest Common Ancestor of a Binary Tree***(要注意p\q其中一个为root的情况,有点乱)
111. Minimum Depth of Binary Tree
- 递归解法
class Solution {
public:
int minDepth(TreeNode* root) {
if(root == NULL)
return 0;
if(root->left == NULL && root->right == NULL){
return 1;
}
if(root->left == NULL)
return minDepth(root->right) + 1;
else if(root->right == NULL)
return minDepth(root->left) + 1;
else
return min(minDepth(root->right), minDepth(root->left)) + 1;
}
};
- BFS
- DFS
100. Same Tree
递归:
class Solution {
public:
bool isSameTree(TreeNode* p, TreeNode* q) {
if(p == NULL && q == NULL)
return true;
if(p == NULL || q == NULL)
return false;
return p->val == q->val && isSameTree(p->left, q->left) && isSameTree(p->right, q->right);
}
};
迭代:
101. Symmetric Tree
还是容易让人一下想不出来的懵逼,四月份的时候还做过,完全想不起来了。
- 递归结构中,需要比较的是两棵树。
- helper比较的是两个子树是否对称。
- 与226的描述相对应,返回的是两颗子树是否对称(需要满足子树1的右孩子与子树2的左孩子,子树1的左孩子与子树2的右孩子相等),子树对称的话,再判断当前根节点下是否对称。
class Solution {
public:
bool isSymmetric(TreeNode* root) {
if(root == NULL || (root->left == NULL && root->right == NULL))
return true;
//helper比较的是两个子树是否对称.
return helper(root->left, root->right);
}
private:
bool helper(TreeNode* left, TreeNode* right){
if(left == NULL && right == NULL)
return true;
if(left == NULL || right == NULL)
return false;
//两颗子树,需要满足子树1的右孩子与子树2的左孩子,子树1的左孩子与子树2的右孩子相等
return left->val == right->val && helper(left->right, right->left) && helper(left->left, right->right);
}
};
222. Count Complete Tree Nodes
- 需要考虑到完全二叉树的性质,可以对最后一排节点数量使用二分法来确定。
递归穷举:
class Solution {
public:
int countNodes(TreeNode* root) {
if(root == NULL)
return 0;
nodes += 1;
countNodes(root->left);
countNodes(root->right);
return nodes;
}
private:
int nodes = 0;
};
尽量简洁些,更能看出递归结构:
class Solution {
public:
int countNodes(TreeNode* root) {
if(root == NULL)
return 0;
return countNodes(root->left) + countNodes(root->right) + 1;
}
};
二分思路:
class Solution {
public:
int countNodes(TreeNode* root) {
if(root == NULL)
return 0;
//getLeftDepth求出子树高度
int lDepth = getLeftDepth(root->left);
int rDepth = getLeftDepth(root->right);
//左子树为满二叉树,右子树继续递归
if(lDepth == rDepth){
return pow(2, lDepth) + countNodes(root->right);
}
//左子树继续递归,右子树为满二叉树
else{
return pow(2, rDepth) + countNodes(root->left);
}
}
private:
int getLeftDepth(TreeNode* root){
int Depth = 0;
while(root){
Depth += 1;
root = root->left;
}
return Depth;
}
};
226. Invert Binary Tree
记得以前还做过这道题,现在看着还是很懵,看着答案也觉得有点不可思议,过于简洁有点看不出来反转二叉树过程具体是如何变化的。
- 递归结构中,需要对左右子树进行反转操作,反转后返回左右两个子树的根节点,然后对当前的根节点进行左右子树反转操作。
后续遍历:
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
if(root == NULL){
return NULL;
}
TreeNode* left = invertTree(root->left);
TreeNode* right = invertTree(root->right);
root->right = left;
root->left = right;
return root;
}
};
前序遍历:
中序遍历:
112. Path Sum
- 要注意递归退出的条件(如何判断当前是叶子节点,而不是直接判断是否为NULL, 为NULL的时候,不一定是叶子节点下来的)
- 属于从底层返回结果的递归流程
class Solution {
public:
bool hasPathSum(TreeNode* root, int sum) {
if(root == NULL){
return false;
}
//当前root为叶子节点,并且刚好能够把sum减完,就返回true;
if(root->left == NULL && root->right == NULL && root->val == sum){
return true;
}
if(hasPathSum(root->left, sum - root->val)){
return true;
}
if(hasPathSum(root->right, sum - root->val)){
return true;
}
return false;
}
};
404. Sum of Left Leaves
- 当前节点上面才能判断出儿子节点是否为左叶子节点,当前节点无法判断当前节点是否为左叶子节点。
- 左叶子节点的判断方法:
root->left != NULL && root->left->left == NULL && root->left->right == NULL
然后遍历整个二叉树将所有满足左叶子节点的数值加起来。
class Solution {
public:
int sumOfLeftLeaves(TreeNode* root) {
if(root == NULL){
return 0;
}
return sumOfLeftLeaves(root->left) + sumOfLeftLeaves(root->right) +
(isLeftLeave(root) ? root->left->val : 0);
}
private:
bool isLeftLeave(TreeNode* root){
if(root->left == NULL)
return false;
if(root->left->left == NULL && root->left->right == NULL){
return true;
}
return false;
}
};
257. Binary Tree Paths
递归:
class Solution {
public:
vector<string> binaryTreePaths(TreeNode* root) {
vector<string> ret;
string s;
helper(root, ret, s);
return ret;
}
private:
void helper(TreeNode* root, vector<string>& ret, string s){
if(root == NULL)
return;
if(root->left == NULL && root->right == NULL){
ret.push_back(s + to_string(root->val));
return;
}
helper(root->left, ret, s + to_string(root->val) + "->");
helper(root->right, ret, s + to_string(root->val) + "->");
}
};
回溯法,回溯中全局变量string adds 导致回溯对象“2->”把顶层的回溯对象“1->”给覆盖了。,当需要回溯“1->”的时候,回溯到了“2->”,导致crash.
adds = to_string(root->val) + “->”;
class Solution {
public:
vector<string> binaryTreePaths(TreeNode* root) {
vector<string> ret;
string s;
helper(root, ret, s);
return ret;
}
private:
void helper(TreeNode* root, vector<string>& ret, string& s){
if(root == NULL)
return;
if(root->left == NULL && root->right == NULL){
s += to_string(root->val);
ret.push_back(s);
s.erase(s.rfind(to_string(root->val)), to_string(root->val).length());
return;
}
adds = to_string(root->val) + "->";
s += adds;
helper(root->left, ret, s);
helper(root->right, ret, s);
s.erase(s.rfind(adds), adds.length());
return;
}
string adds;
};
回溯法,效率并没有提升,只是写一下看看:
class Solution {
public:
vector<string> binaryTreePaths(TreeNode* root) {
vector<string> ret;
string s;
helper(root, ret, s);
return ret;
}
private:
void helper(TreeNode* root, vector<string>& ret, string& s) {
if (root == NULL) {
return;
}
if (root->left == NULL && root->right == NULL) {
s += to_string(root->val);
ret.push_back(s);
s.erase(s.rfind(to_string(root->val)), to_string(root->val).length());
return;
}
string adds = to_string(root->val) + "->";
s += adds;
helper(root->left, ret, s);
helper(root->right, ret, s);
s.erase(s.rfind(adds), adds.length());
return;
}
};
113. Path Sum II
直接贴回溯了
class Solution {
public:
vector<vector<int>> pathSum(TreeNode* root, int sum) {
vector<vector<int>> ret;
if(root == NULL)
return ret;
vector<int> solution;
helper(root, ret, solution, sum);
return ret;
}
void helper(TreeNode* root, vector<vector<int>>& ret, vector<int>& solution, int sum){
if(root == NULL)
return;
solution.push_back(root->val);
if(root->left == NULL && root->right == NULL && root->val == sum){
ret.push_back(solution);
solution.pop_back();
return;
}
helper(root->left, ret, solution, sum - root->val);
helper(root->right, ret, solution, sum - root->val);
solution.pop_back();
return;
}
};
129. Sum Root to Leaf Numbers
- 递归过程中每条路径的开始对每层的数值乘以10^k, k是节点的高度,相当于每次进入递归,都要把入参乘以10,然后加上当前节点的值, sum += 10*sum + root-val;
class Solution {
public:
//1.如何从root节点开始对每层的数值乘以10^k,k是节点的高度
//相当于每次进入递归,都要把入参乘以10,然后加上当前节点的值。
//sum += 10*sum + root-val;
int sumNumbers(TreeNode* root) {
int ret = 0;
int sum = 0;
helper(root, sum, ret);
return ret;
}
private:
void helper(TreeNode* root, int& sum, int& ret){
if(root == NULL)
return;
sum = 10*sum + root->val;
if(root->left == NULL && root->right == NULL){
ret += sum;
}
helper(root->left, sum, ret);
helper(root->right, sum, ret);
sum = (sum - root->val) / 10;
return;
}
};
437. Path Sum III
路径可以任意不一定需要从root到leaf, 返回路径的数量
- 双重递归,外层递归只是为了遍历所有可以作为根节点的node,内层递归解决当前节点作为root节点的所有满足条件的路径的数量。
- root->val == sum的时候不能直接返回,因为路径中有负数,可能在更深层的递归中重新满足条件。
class Solution {
public:
int pathSum(TreeNode* root, int sum) {
int res = 0;
if(root == NULL){
return 0;
}
res = helper(root, sum);
res += pathSum(root->left, sum);
res += pathSum(root->right, sum);
return res;
}
private:
int helper(TreeNode* root, int sum){
int ret = 0;
if(root == NULL){
return 0;
}
if(root->val == sum){
ret += 1;
//不要在这里返回,路径中有负数。
}
ret += helper(root->left, sum - root->val);
ret += helper(root->right, sum - root->val);
return ret;
}
};
前缀和解法?
235. Lowest Common Ancestor of a Binary Search Tree
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if(root == NULL){
return NULL;
}
if(root->val > p->val && root->val > q->val){
return lowestCommonAncestor(root->left, p, q);
}
else if(root->val < p->val && root->val < q->val){
return lowestCommonAncestor(root->right, p, q);
}
else{
return root;
}
}
};
98. Validate Binary Search Tree
class Solution {
public:
bool isValidBST(TreeNode* root) {
return helper(root, LONG_MIN, LONG_MAX);
}
private:
bool helper(TreeNode* root, long min, long max){
if(root == NULL){
return true;
}
if(root->val > min && root->val < max){
return helper(root->left, min, root->val) && helper(root->right, root->val, max);
}
return false;
}
};
450. Delete Node in a BST
- del node 如果没有双子节点,那么直接返回其中一个节点
- del node 如果有两个双子节点,将del node赋值为它的后继节点的值,然后删掉它的后继节点 root->right = deleteMin(root->right).
class Solution {
public:
//递归deleteNode找到key,找到它的后继节点,key赋值为后继节点,删除后继节点。
TreeNode* deleteNode(TreeNode* root, int key) {
if(!root) return NULL;
if(root->val < key)
root->right = deleteNode(root->right, key);
else if(root->val > key)
root->left = deleteNode(root->left, key);
//找到keyNode.
else{
if(root->left == NULL)
return root->right;
if(root->right == NULL)
return root->left;
//删除keyroot的后继节点
root->right = deleteMin(root->right);
//将key赋值为后继节点的值
root->val = tail;
}
return root;
}
private:
//删除查找树的最小节点,返回删除节点的右节点
TreeNode* deleteMin(TreeNode* root){
if(root == NULL)
return NULL;
if(root->left == NULL){
tail = root->val;
return root->right;
}
root->left = deleteMin(root->left);
return root;
}
int tail = 0;
};
108. Convert Sorted Array to Binary Search Tree
- DFS二分法:
class Solution {
public:
TreeNode* sortedArrayToBST(vector<int>& nums) {
return helper(nums, 0, nums.size() - 1);
}
private:
TreeNode* helper(vector<int>& nums, int left, int right){
if(left > right)
return NULL;
int mid = (left + right) / 2;
TreeNode* root = new TreeNode(nums[mid]);
root->left = helper(nums, left, mid - 1);
root->right = helper(nums, mid + 1, right);
return root;
}
};
- BFS
230. Kth Smallest Element in a BST
- 中序BST
class Solution {
public:
//栈模拟中序遍历,遇到第k个数的时候可以返回,不用遍历完整个搜索树
int kthSmallest(TreeNode* root, int k) {
stack<TreeNode*> st;
int n = 0;
while(true){
while(root){
st.push(root);
root = root->left;
}
//最左节点出栈访问,记录出栈访问次数n
TreeNode* node = st.top();
st.pop();
if(++n == k)
return node->val;
//最左节点的右节点入栈
root = node->right;
}
}
};
- 栈模拟中序迭代
class Solution {
public:
//栈模拟中序遍历,遇到第k个数的时候可以返回,不用遍历完整个搜索树
int kthSmallest(TreeNode* root, int k) {
stack<TreeNode*> st;
int n = 0;
while(true){
while(root){
st.push(root);
root = root->left;
}
//最左节点出栈访问,记录出栈访问次数n
TreeNode* node = st.top();
st.pop();
if(++n == k)
return node->val;
//最左节点的右节点入栈
root = node->right;
}
}
};
236. Lowest Common Ancestor of a Binary Tree
- 与235类似但是不是BST.
- 递归形式越简单,越不容易理解,尤其是在left、right不全为NULL的递归逻辑中。。。
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if(root == NULL || root->val == p->val || root->val == q->val)
return root;
TreeNode* left = lowestCommonAncestor(root->left, p, q);
TreeNode* right = lowestCommonAncestor(root->right, p, q);
if(left && right)
return root;
else
return left ? left : right;
}
};