记录刷题中遇到的知识点方便复习(十六)(二叉树3)

四十、LeetCode第四十题(230)

二叉搜索树中第k小的元素

给定一个二叉搜索树的根节点 root ,和一个整数 k ,请你设计一个算法查找其中第 k 个最小元素(从 1 开始计数)。

很简单,中序遍历,遍历到第k个元素就好了

class Solution {
public:
    int kthSmallest(TreeNode* root, int k) {
        stack<TreeNode*> stk;
        while(root || !stk.empty()){
            while(root){
                stk.push(root);
                root = root->left;
            }
            --k;
            if (k == 0) {
                break;
            }
            root = root->right;
        }
        return root->val;
    }
};

四十一、LeetCode第四十一题(199)

二叉树的右视图

给定一个二叉树的 根节点 root,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。

这道题我脑袋短路了,一开始直接想的是遍历右子树就好了,右子树没有了再遍历左子树,结果发现左子树更深的话就漏掉了。看了提示才想起来层序遍历,也就是广度优先搜索,遍历每一层,取出来每一层的最后元素就好了,代码如下:就是在层序遍历的基础上把每一层的最后一个元素存下来,就得到结果了。队列的基础操作都用上了。(我觉得官方题解给复杂了)

class Solution {
public:
    vector<int> rightSideView(TreeNode* root) {
        vector<int> res;
        if(!root) return res;
        //层序遍历
        queue<TreeNode*> q;
        q.push(root);
        while(!q.empty()){
            int n = q.size();
            res.push_back(q.back()->val); 
            for(int i = 1; i<=n; i++){
                TreeNode* p = q.front();
                q.pop();
                if(p->left) q.push(p->left);
                if(p->right) q.push(p->right);
            }                       
        }
        return res;
    }
};

注意一般开始都要判断以下root是否为空,不然总是报错:runtime error: member access within null pointer of type 'TreeNode' (solution.cpp)

四十二、LeetCode第四十二题(114)

二叉树展开为链表

给你二叉树的根结点 root ,请你将它展开为一个单链表:

  • 展开后的单链表应该同样使用 TreeNode ,其中 right 子指针指向链表中下一个结点,而左子指针始终为 null 。
  • 展开后的单链表应该与二叉树 先序遍历 顺序相同。

这道题首先分析肯定与前序遍历有关,常规思路是,前序遍历二叉树存下节点的值,再创建一个链表按顺序存进去,代码如下:

class Solution {
public:
    void flatten(TreeNode* root) {
        //TreeNode* head = root;
        if(root == nullptr) return;
        TreeNode* pre = root;     
         
        vector<int> s;
        stack<TreeNode*> stk;

        while(root || !stk.empty()){
            while(root){
                stk.push(root);  
                s.push_back(root->val); 
                root = root->left;              
            }
            root = stk.top();
            stk.pop();
            root = root->right;
        }
        for(int i = 1; i<s.size(); i++){
            TreeNode *p = new TreeNode(s[i]);
            pre->right = p;
            pre->left = nullptr;
            pre = p;
        }
    }
};

或者是在前序遍历的过程中,边遍历边展开为链表,这个要修改前序遍历的方式,只能用栈的方法,不能递归。. - 力扣(LeetCode)

另外就是递归的方法:反前序遍历,即首先得到的是前序遍历的最后一个节点,存为pre,然后把倒数第二个节点的右子树存在pre,回溯的思想。

class Solution {
public:
    TreeNode *pre;
    void flatten(TreeNode* root) {
        //递归
        if(!root) return;
        flatten(root->right);
        flatten(root->left);
        root->right = pre;
        root->left = nullptr;
        pre = root;
    }
};

另外评论区有一个很棒的解法:直接把左子树的最右节点连接到根的右节点,再把根的右子树连接改成左子树,根移动到右节点,依次循环到null也就是右子树的底部

class Solution {
    public void flatten(TreeNode root) {
        while (root != null) {
            TreeNode move = root.left;
            while (move != null && move.right != null) {
                move = move.right;
            }
            if (move != null) {
                move.right = root.right;
                root.right = root.left;
                root.left = null;
            }
            root = root.right;
        }
    }
}

四十三、LeetCode第四十三题(105)

从前序与中序遍历序列构造二叉树

给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的先序遍历, inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。

这道题最容易想到的方式,就是递归吧,首先先序遍历的第一个节点一定是根节点,然后在中序遍历中找到根节点,左边就对应着左子树,右边就对应右子树,而先序遍历可以划分为根+左子树+右子树,这样划分数组,就可以对左右子树进行递归,代码如下:

class Solution {
public:
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {        
        if(!preorder.size()) return nullptr;
        
        TreeNode *root = new TreeNode(preorder[0]);
        int iter = 0;
        for(int i = 0; i<inorder.size(); i++){
            if(inorder[i] == preorder[0]){
                iter = i;
                break;
            }
        }
        
        vector<int> in1;
        for(int i = 0; i<iter; i++){
            in1.push_back(inorder[i]);
        }
        vector<int> pre1;
        for(int i = 1; i<=in1.size(); i++){
            pre1.push_back(preorder[i]);
        }
        vector<int> in2;
        for(int i = iter+1; i<inorder.size(); i++){
            in2.push_back(inorder[i]);
        }
        vector<int> pre2;
        for(int i = in1.size()+1; i<preorder.size(); i++){
            pre2.push_back(preorder[i]);
        }
        root->left = buildTree(pre1, in1);
        root->right = buildTree(pre2, in2);
        return root;
       
    }
};

有一说一这种递归真的超级慢,官方给出了用哈希表的方式辅助查找然后递归,另外还有非递归的方法 . - 力扣(LeetCode)

四十四、LeetCode第四十四题(236)

二叉树的最近公共祖先

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

题目真是做的怀疑人生。

看解答了

递归:首先判断根是否为空,p或q是否为根节点,任一成立都是直接返回root

然后递归。在左右子树里面找p,q,找到了就返回当前节点,没找到就返回root

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(root == nullptr || root == p || root == q) return root;
        TreeNode *left = lowestCommonAncestor(root->left, p, q);
        TreeNode *right = lowestCommonAncestor(root->right, p ,q);
        if(left == nullptr) return right;
        if(right == nullptr) return left;
        return root;
    }
};

这么简洁的代码,我怎么就想不出来啊

另一种方法:存储父节点,指路. - 力扣(LeetCode)

四十五、LeetCode第四十五题(124) 

二叉树中的最大路径和

二叉树中的 路径 被定义为一条节点序列,序列中每对相邻节点之间都存在一条边。同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个 节点,且不一定经过根节点。

路径和 是路径中各节点值的总和。

给你一个二叉树的根节点 root ,返回其 最大路径和 。

没做出来看的题解

这个作者:Ikaruga 讲的很清晰

. - 力扣(LeetCode)

class Solution {
public:
    int maxGain(TreeNode *root, int& maxNum){
        if(root == nullptr) return 0;
        int l = maxGain(root->left, maxNum);
        int r = maxGain(root->right, maxNum);
        int cur = root->val + max(0, l) + max(0, r);
        int sec = root->val + max(0, max(l, r));
        maxNum = max(maxNum, max(cur, sec));
        return sec;
         
    }
    int maxPathSum(TreeNode* root) {
        int maxNum = INT_MIN;
        maxGain(root, maxNum);
        return maxNum;
    }
};

难度hard,等我进阶归来再二刷。

  • 13
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值