力扣刷题笔记:二叉树(16)

100. 相同的树(深搜)

递归判断两个树是否相等

class Solution {
public:
    bool isSameTree(TreeNode* p, TreeNode* q) {
        if (p == nullptr && q == nullptr) 
            return true;
        else if (p == nullptr || q == nullptr) 
            return false;
        else if (p->val != q->val) 
            return false;
        else 
            return isSameTree(p->left, q->left) && isSameTree(p->right, q->right);
    }
};

101. 对称二叉树(深搜)

递归判断左右子树是否对称,用else覆盖所有情况

    bool isSymmetric(TreeNode* root) {
    if (root==NULL) return true;
    return dfs(root->left,root->right);
    }
    //必须新写一个递归,递归判断左左和右右,左右和右左
    bool dfs(TreeNode* left,TreeNode* right) {
        if(left==NULL&&right==NULL) return true;
        else if (left==NULL||right==NULL) return false;
        else if(left->val!=right->val) return false;
        else return dfs(left->left,right->right)&&dfs(left->right,right->left);
    }

104. 二叉树的最大深度(深搜、广搜)

在外面记录一个当前最大深度,找到叶子节点时判断是否大于最大深度,若大于则更新,最后返回最大深度

class Solution {
public:
    int ans;
    int maxDepth(TreeNode* root) {
        ans=0;
        dfs(root,0);
        return ans;
    }
    void dfs(TreeNode* node,int depth)
    {
        if(node==nullptr){
            if(depth>ans)
                ans=depth;
            return;
        }
        dfs(node->left,depth+1);
        dfs(node->right,depth+1);
    }
};

简介版答案

class Solution {
public:
    int maxDepth(TreeNode* root) {
        if (root == nullptr) return 0;
        return max(maxDepth(root->left), maxDepth(root->right)) + 1;
    }
};

105. 从前序与中序遍历序列构造二叉树(深搜)

1、根据前序遍历查找根节点,根据中序遍历查找该节点,得到其左右范围,构造该节点并递归遍历
2、递归函数设置当前查找范围,前序中序数组和当前根节点序号设置为全局变量保存

class Solution {
public:
    vector<int> pre,ino;
    int temp=0;
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
    	pre=preorder;ino=inorder;
   		//不能传入preorder.size()-1,不然少输出一个数
    	return dfs(0,preorder.size());
    }
    //递归没有必要传入vector,设置成为成员变量
    TreeNode* dfs(int a,int b)
    {
        //前序遍历指针没有必要传入函数,设置一个成员变量进行记录,如果到头则返回
        if(temp==pre.size()||a==b) return NULL;
        TreeNode* res=NULL;
        for(int i=a;i<b;i++)
        if(ino[i]==pre[temp]){
            res=new TreeNode(pre[temp++]);
            res->left=dfs(a,i);
            res->right=dfs(i+1,b);
            break;
        }
        return res;
    }
};

106. 从中序与后序遍历序列构造二叉树

class Solution {
    int post_idx;
    unordered_map<int, int> idx_map;
public:
    TreeNode* helper(int in_left, int in_right, vector<int>& inorder, vector<int>& postorder){
        // 如果这里没有节点构造二叉树了,就结束
        if (in_left > in_right) 
            return nullptr;
        // 选择 post_idx 位置的元素作为当前子树根节点
        int root_val = postorder[post_idx];
        TreeNode* root = new TreeNode(root_val);
        // 根据 root 所在位置分成左右两棵子树
        int index = idx_map[root_val];      
        // 下标减一
        post_idx--;
        // 构造右子树
        root->right = helper(index + 1, in_right, inorder, postorder);
        // 构造左子树
        root->left = helper(in_left, index - 1, inorder, postorder);
        return root;
    }
    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
        // 从后序遍历的最后一个元素开始
        post_idx = (int)postorder.size() - 1;
        // 建立(元素,下标)键值对的哈希表
        int idx = 0;
        for (auto& val : inorder) 
            idx_map[val] = idx++;
        return helper(0, (int)inorder.size() - 1, inorder, postorder);
    }
};

112. 路径总和(深搜)

参考113的解析,不用存路径序列,递归代码简单

class Solution {
public:
    bool hasPathSum(TreeNode *root, int sum) {
        if (root == nullptr) {
            return false;
        }
        if (root->left == nullptr && root->right == nullptr) {
            return sum == root->val;
        }
        return hasPathSum(root->left, sum - root->val) || hasPathSum(root->right, sum - root->val);
    }
};

113. 路径总和 II(深搜)

1、题目要求的是从根节点到叶子节点的路径序列,所以每个节点必须塞入临时数组,不存在不塞的情况
2、如果和正好相等,并且左右子节点都为空,才是符合要求的序列
3、临时数组作为参数传入,不用考虑弹出操作

class Solution {
public:
vector<vector<int>> ans;
    int m_sum;
    vector<vector<int>> pathSum(TreeNode* root, int sum) {
   		vector<int> temp;
    	int count=0,m_sum=sum;
    	dfs(root,temp,count);
    	return ans;
    }
    void dfs(TreeNode* root,vector<int> temp,int count)
    {
        if(root==NULL) return;
        count+=root->val;
        temp.push_back(root->val);
        if(count==m_sum&&root->left==NULL&&root->right==NULL){
            ans.push_back(temp);
            return;
        }
        //有负数样例,不能用大小取判断
        else{
            dfs(root->left,temp,count);
            dfs(root->right,temp,count);
            return;
        }
    }
};

129. 求根节点到叶节点数字之和(深搜)

一边加一边深搜,父节点乘以10加上子节点

class Solution {
public:
    int dfs(TreeNode* root, int prevSum) {
        if (root == nullptr) 
            return 0;
        int sum = prevSum * 10 + root->val;
        if (root->left == nullptr && root->right == nullptr) 
            return sum;
        else 
            return dfs(root->left, sum) + dfs(root->right, sum);
    }
    int sumNumbers(TreeNode* root) {
        return dfs(root, 0);
    }
};

124. 二叉树中的最大路径和(深搜)

1、当前节点最大值=该节点+左右子节点之和,负数不加,因为路径可以两条。
2、返回值为当前节点+左右子节点较大者,负数不加,因为路径只能一条。

class Solution {
private:
    int maxSum = INT_MIN;

public:
    int maxGain(TreeNode* node) {
        if (node == nullptr) {
            return 0;
        // 递归计算左右子节点的最大贡献值,只有在最大贡献值大于 0 时,才会选取对应子节点
        int leftGain = max(maxGain(node->left), 0);
        int rightGain = max(maxGain(node->right), 0);
        // 节点的最大路径和取决于该节点的值与该节点的左右子节点的最大贡献值之和
        int priceNewpath = node->val + leftGain + rightGain;
        // 更新答案
        maxSum = max(maxSum, priceNewpath);
        // 返回节点的最大贡献值
        return node->val + max(leftGain, rightGain);
    }
    int maxPathSum(TreeNode* root) {
        maxGain(root);
        return maxSum;
    }
};

199. 二叉树的右视图、左视图(深搜)

深度优先搜索,用depth记录当前深度,depth不能设置为全局,应该设置为参数,ans设置为全局记录右视图
按照中->右->左的方式递归,如果当前节点为该层第一个则记录为右视图

class Solution {
public:
    vector<int> ans;
    vector<int> rightSideView(TreeNode* root) {
        dfs(root,0);
        return ans;
    }
    void dfs(TreeNode* node,int depth){
        if(node==nullptr) return;
        if(depth==ans.size())
            ans.push_back(node->val); 
        depth++;
        dfs(node->right,depth);
        dfs(node->left,depth);
    }
};

226. 翻转二叉树(深搜)

    TreeNode* invertTree(TreeNode* root) {
    //镜像等于交换左右子树
    if(root==NULL) return NULL;
    swap(root->left,root->right);
    invertTree(root->left);
    invertTree(root->right);
    return root;
    }

230. 二叉搜索树中第K小的元素(深搜)

解法1: 中序遍历存储到数组,取nodes[k - 1]
解法2: 直接count中序遍历过的元素,到k个的时候退出
解法3: 二分搜索,根据BST的性质,如果左子树的nodes数目多于k个,直接去左子树递归查找,如果左子树恰好为k-1个,返回根的val,如果左子树节点数L小与k-1,去右子树中查找第k - L - 1小的元素。

class Solution {
public:
    //递归逆中序遍历,并输出第k-1个
    vector<int> ans;
    int kthLargest(TreeNode* root, int k) {
        middle(root);
        return ans[k-1];
    }
    void middle(TreeNode* root)
    {
        if(root==NULL) return;
        else{
            middle(root->right);
            ans.push_back(root->val);
            middle(root->left);
        }
    }
};

236. 二叉树的最近公共祖先(深搜)

1、如果左右子树都true,或自己为true,左右子树只有一个true,则该节点是唯一最近祖先
2、只要自己或左右子树有一个是true,就返回true

class Solution {
public:
    int p1,q1;
    TreeNode* ans;
    bool dfs(TreeNode* root)
    {
        if(root==NULL) return false;
        int left=dfs(root->left);
        int right=dfs(root->right);
        if ((left&&right)||((left||right)&&(root->val==p1||root->val==q1))) ans=root;
        return (root->val==p1||root->val==q1||left||right);
    }
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        p1=p->val;
        q1=q->val;
        dfs(root);
        return ans;
    }
};

450. 删除二叉搜索树中的节点(深搜)

根据二叉搜索树的性质
1、如果目标节点大于当前节点值,则去右子树中删除;
2、如果目标节点小于当前节点值,则去左子树中删除;
3、如果目标节点就是当前节点,分为以下三种情况:
a、其无左子:其右子顶替其位置,删除了该节点;
b、其无右子:其左子顶替其位置,删除了该节点;
c、其左右子节点都有:其左子树转移到其右子树的最左节点的左子树上,然后右子树顶替其位置,由此删除了该节点。

    TreeNode* deleteNode(TreeNode* root, int key) 
    {
        if (root == nullptr)    return nullptr;
        if (key > root->val)    root->right = deleteNode(root->right, key);     // 去右子树删除
        else if (key < root->val)    root->left = deleteNode(root->left, key);  // 去左子树删除
        else    // 当前节点就是要删除的节点 {
            if (! root->left)   return root->right; // 情况1,欲删除节点无左子
            if (! root->right)  return root->left;  // 情况2,欲删除节点无右子
            TreeNode* node = root->right;           // 情况3,欲删除节点左右子都有 
            while (node->left)          // 寻找欲删除节点右子树的最左节点
                node = node->left;
            node->left = root->left;    // 将欲删除节点的左子树成为其右子树的最左节点的左子树
            root = root->right;         // 欲删除节点的右子顶替其位置,节点被删除
        }
        return root;    
    }

543. 二叉树的直径(深搜)

二叉树的直径等于每个节点长度的最大值,这个长度是左右子树加自己,用一个成员变量记录直径

class Solution {
    int ans;
    int depth(TreeNode* rt){
        if (rt == NULL) 
            return 0; // 访问到空节点了,返回0
        int L = depth(rt->left); // 左儿子为根的子树的深度
        int R = depth(rt->right); // 右儿子为根的子树的深度
        ans = max(ans, L + R + 1); // 计算d_node即L+R+1 并更新ans
        return max(L, R) + 1; // 返回该节点为根的子树的深度
    }
public:
    int diameterOfBinaryTree(TreeNode* root) {
        ans = 1;
        depth(root);
        return ans - 1;
    }
};

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/diameter-of-binary-tree/solution/er-cha-shu-de-zhi-jing-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

662. 二叉树最大宽度(广搜)

1、使用广搜,方便查找每层的最大宽度,由于中间可能有空节点,最大宽度不等于每层个数,而等于首尾序号差
2、防止序号溢出,下一层序号要减去当前层的起始值

int widthOfBinaryTree(TreeNode* root) {
        if (root == nullptr)
            return 0;
        int res = 0;
        queue<TreeNode*> q;
        root->val = 0;
        q.push(root);
        while (!q.empty()) {
            res = max(res, q.back()->val - q.front()->val + 1);
            int offset = q.front()->val;
            int n = q.size();
            for (int i = 0; i < n; ++i) {
                TreeNode* curr = q.front();
                q.pop();
                curr->val -= offset;
                if (curr->left){
                    curr->left->val = curr->val*2;
                    q.push(curr->left);
                }
                if (curr->right){
                    curr->right->val = curr->val*2+1;
                    q.push(curr->right);
                }
            }
        }
        return res;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值