leetcode(三) 树专题

98.验证二叉搜索树

思路:采用自顶向下的搜索方法,如果每个元素都合理处在自己的范围里,那么是BST
关键在于每次搜索后更新当前节点(root[min,max])儿子节点的范围
左儿子更新成[min,val-1],右儿子更新成[val+1,max]

在这里插入图片描述

/**
 * 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:
    bool isValidBST(TreeNode* root) {
        return dfs(root,INT_MIN,INT_MAX);                //根节点范围是int上下界
    }

    bool dfs(TreeNode* root,long long min,long long max){ 
        if(!root)return true;                               //根节点(或者子树根)为空,返回true
  
        if(root->val>max||root->val<min)return false;      //不在范围内,返回false

        return dfs(root->left,min,root->val-1ll)&&dfs(root->right,root->val+1ll,max);      //搜索当前节点的左右子树
    }
};

94.二叉树的中序遍历 **

这题不给用递归所以得自己开一个栈迭代
思路:
1.首先把要压入树最左边的一条链压入栈
2.每次取出栈顶元素,如果有,将其右子树压入栈
3.重复操作1

在这里插入图片描述

/**
 * 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:
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int>res;

        stack<TreeNode*> stk;

        auto p=root;

        while(p||stk.size()){    //只要p有指向或者栈中有元素,循环执行
            while(p){           
                stk.push(p);     //步骤一将要加入栈的树的最左链入栈

                p=p->left;
            }
            
            p=stk.top();             

            stk.pop();

            res.push_back(p->val);     //步骤二取栈顶元素,将其右子树入栈(如果没有,下次循环会跳过)

            p=p->right;
        }

        return res;
    }
};

附上递归的写法

/**
 * 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:
   
    vector<int> inorderTraversal(TreeNode* root) {
          
          vector<int>res;
          
          return dfs(root,res);
    }

    vector<int> dfs(TreeNode* root,vector<int> &res){     //注意这里别忘了写引用&符号
        if(!root)return  res;

        dfs(root->left,res);

        res.push_back(root->val);

        dfs(root->right,res);

        return  res;
    }


};

附上通用解法,之前看交大数据结构也是那么教的,一直不会实现hh,前中后改个顺序即可
可以用栈来模拟这个过程。栈中每个元素存储两个值:TreeNode节点和一个整型的标记。

  • 标记 = 0,表示还没遍历该节点的左子树;
  • 标记 = 1,表示已经遍历完左子树,但还没遍历右子树;
  • 标记 = 2,表示已经遍历完右子树;

然后我们可以根据标记的值,来分别处理各种情况:

  • 标记 = 0,则将该节点的标记改成1,然后将其左儿子压入栈中;
  • 标记 = 1,则说明左子树已经遍历完,将根节点的值插入答案序列中,然后将该节点的标记改成2,并将右儿子压入栈中;
  • 标记 = 2,则说明以该节点为根的子树已经遍历完,直接从栈中弹出即可;

时间复杂度分析:树中每个节点仅会遍历一遍,且进栈出栈一次,所以时间复杂度是 O(n)O(n)。

/**
 * 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:
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> res;
        stack<pair<TreeNode*, int>>sta;
        sta.push(make_pair(root, 0));
        while (!sta.empty())
        {
            if (sta.top().first == NULL)
            {
                sta.pop();
                continue;
            }
            int t = sta.top().second;
            if (t == 0)
            {
                sta.top().second = 1;
                sta.push(make_pair(sta.top().first->left, 0));
            }
            else if (t == 1)
            {
                res.push_back(sta.top().first->val);
                sta.top().second = 2;
                sta.push(make_pair(sta.top().first->right, 0));
            }
            else sta.pop();
        }
        return res;
    }
};

101.对称二叉树 **

思路:
递归:
以根节点为轴对称分为左右两棵树
这两棵树首先根节点值相等,其次他们的左右子树分别对称相等
递归定义即可

在这里插入图片描述
在这里插入图片描述

//递归写法
class Solution {
public:
    bool isSymmetric(TreeNode* root) {
        if(!root)return true;                   //空节点为对称二叉树       
        return dfs(root->left,root->right); 
    }

    bool  dfs(TreeNode* p,TreeNode* q){
        if(!p||!q)return !p&&!q;                      //只有左右两根节点都不存在时才对称,返回true,一个存在一个不存在,返回false

        return p->val==q->val&&dfs(p->left,q->right)&&dfs(p->right,q->left);
    }   //根节点值要相等,左根的左树要对称右根的右树,左根的右树要对称右根的左树
};

思路
迭代
与上题相似的方法遍历左右子树,只不过左子树用左中右方法遍历,右子树用右中左方法遍历,每次从栈中取出一对称两点比较,比较完毕就是对称二叉树

//迭代写法
class Solution {
public:
    bool isSymmetric(TreeNode* root) { 
        if(!root)return true;

        stack<TreeNode*>left,right;    //分别保存左右子树遍历的值

        auto p=root->left;             //pq指向左右根节点

        auto q=root->right;

        while(p||q||left.size()||right.size()){   
            while(p&&q){                             //pq左右链入栈
                left.push(p),right.push(q);

                p=p->left,q=q->right;
            }
            if(p||q)return false;                    //精髓,如果pq有一个不存在,则不对称,上面循环保证不可能两个都存在

            p=left.top(),q=right.top();

            left.pop(),right.pop();

            if(p->val!=q->val)return false;         //对称节点值不相等不存在

            p=p->right,q=q->left;                   //左子树遍历右节点,右子树遍历左节点(对称遍历)      
        }

        return true;
    }
};

从前序和中序遍历构造二叉树

数据结构基础题的算法实现。。
思路:
前序遍历第一个节点就是根节点,在中序遍历中找到他的位置,以他为界分出左右两棵子树,每次建立一个节点,递归建立其左右子树
关键在于找前序遍历的根节点在中序的位置,如果对每一次建立节点都循环遍历一次inorder,时间复杂度会是O(n 2)
为此建立一个哈希表,存放inorder中对应数据的位置,将遍历的时间复杂度降为O(1)

在这里插入图片描述

class Solution {
public:
    unordered_map<int,int> pos;

    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        int n=preorder.size();

        if(!n)return NULL;

        for(int i=0;i<n;i++)pos[inorder[i]]=i;    //初始化哈希表

        return dfs(preorder,inorder,0,n-1,0,n-1);
    }
    
    TreeNode* dfs(vector<int> &preorder,vector<int> &inorder,int lp,int rp,int li,int ri){
        if(lp>rp)return NULL;

        int val=preorder[lp];                //当前建立的根节点的值

        int k=pos[val];                      //找到该节点在inorder中对应的位置

        int len=k-li;                       //确定当前根节点左子树的元素个数

        auto root=new  TreeNode(val);

        root->left=dfs(preorder,inorder,lp+1,lp+len,li,k-1);     //递归建立左右节点

        root->right=dfs(preorder,inorder,lp+len+1,rp,k+1,ri);

        return root;
    }
};

102.二叉树的层次遍历

思路:
常规宽搜思路,只不过每搜一层要返回一层元素,拓展之前先记录一下长度

在这里插入图片描述

class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        vector<vector<int>>res;      //记录答案
        
        if(!root)return res;

        queue<TreeNode*>q;           //宽搜队列

        q.push(root);

        while(q.size()){
            int len =q.size();       //记录当前层的长度

            vector<int>level;        //记录当前层的元素

            for(int i=0;i<len;i++){         //拓展当前层的每一个元素
                auto p=q.front();             

                q.pop();

                if(p->left)q.push(p->left);

                if(p->right)q.push(p->right);

                level.push_back(p->val);       
            }

            res.push_back(level);            //返回当前层的元素
        }

        return res;
    }
};

236.二叉树的最近公共祖先 **

思路:
拿到这题,先分析因该是个递归判断

  • 首先分析当前根节点是不是祖先节点,如果他不存在返回root(leetcode特色)
    由于pq不重合且肯定存在,所以如果根节点等于p或者q,也直接返回root
  • 其次判断左右子节点是不是祖先节点,调用当前方法返回左右节点对应的值
  • 如果左右都返回了一个node,说明左右子树分别存在着pq,此时返回根节点
  • 如果只有左子树返回一个node,而右子树返回NULL,说明pq都存在左子树当中,此时返回左子树node
  • 如果只有右子树返回一个node,而左子树返回NULL,说明pq都存在右子树当中,此时返回右子树node

在这里插入图片描述


class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(!root||root==p||root==q)return root;     //当前节点包含p或者q,或者为空都返回这个node

        auto left=lowestCommonAncestor(root->left,p,q);  //分别看一下左右节点

        auto right=lowestCommonAncestor(root->right,p,q);

        if(!left)return right;      //如果left为空返回right,right空返回left,都为空就返回NULL
  
        if(!right)return left;

        return  root;     //如果左右子节点都不是空就返回根节点

    }
};

543.二叉树的直径 **

(自底向上的递归想法)
这题一开始完全想错了,因为有可能最长路径不从根节点走。。

  • 所以枚举每一条路径的顶点(其实就是所有的点),递归求出其左右路径长度,加起来算出总长度,然后更新答案即可
  • 注意这题有点像自底向上递归的感觉,,从叶子节点开始考虑,每次更新完最大值之后需要返回该节点左右最长的长度加一(左右最长代表以该节点为顶点的树的深度,加一是因为返回到上一个节点有一条边

在这里插入图片描述
在这里插入图片描述

class Solution {
public:

    int ans=0;      //最后答案

    int diameterOfBinaryTree(TreeNode* root) {
        if(!root)return 0;

        dfs(root);

        return ans;

    }

    int dfs(TreeNode* root){
        if(!root)return 0;

        int left=dfs(root->left);       //每次先查看该节点左右路径长度

        int right=dfs(root->right);

        ans=max(ans,right+left);      //left+right是以该节点为根(顶点)的最大路径,更新最大长度

        return max(right+1,left+1);   //返回到上一个根节点,用的是该树最大深度加一
    }
};

124 .二叉树中的最大路径和

这道题做法和上一题一样
只不过在返回时多考虑几种情况
因为其不是边权和而是节点权和且存在负数节点
所以返回上一级时,最小应当返回0(返回0即表示最大路径不从这里走,只有负权路径)

在这里插入图片描述
在这里插入图片描述

class Solution {
public:

    int res=INT_MIN;    //初值应定为最小值

    int maxPathSum(TreeNode* root) {

         dfs(root);

         return res;

    }

    int dfs(TreeNode* root){
        if(!root)return 0;

        int left=dfs(root->left);     //搜索左右路径最大值,最小是0

        int right=dfs(root->right);

        res=max(res,root->val+right+left);    

        return max(0,root->val+(0,max(right,left)));  //返回从当前节点经过路径的最大值即  从0,root->val,root->val+left,root->val+right选择一个最大值
    }
};

173.二叉搜索树迭代器

这道题要求实现一个中序遍历的迭代器
我们把前面中序遍历迭代写法那题(## 94.二叉树的中序遍历)拆开写
分部分实现二叉树的函数

在这里插入图片描述

class BSTIterator {
public:
    stack<TreeNode*>stk;
    BSTIterator(TreeNode* root) {       //初始化时将最左链入栈
       while(root){
           stk.push(root);

           root=root->left;
       } 

    }
    
    /** @return the next smallest number */
    int next() {                     //栈顶元素即当前最小值,每次返回后要将栈    顶元素的右子树入栈
        auto p=stk.top();

        stk.pop();

        int val=p->val;

        p=p->right;

        while(p){
            stk.push(p);

            p=p->left;
        }
        return val;
    }
    
    /** @return whether we have a next smallest number */
    bool hasNext() {
        return !stk.empty();      //栈空则遍历结束,没有最小值
    }
};

297.二叉树的序列化和反序列化 ** ??

题意:树转成字符串,字符串再转回去(都用递归方式)
用前序遍历的方法返回遍历顺序,没有的节点用#代替,保证转换回树时有唯一性

  • 转字符串
  • 按照根左右的顺序遍历,遇到数转字符加入字符串,遇到空节点则加入"#“作为标记,每个字符之间都要用”,"隔开
  • 转二叉树
  • 用下标标记遍历的位置
  • 按照根左右的方式递归建立树,每次遇到",“则停止,遇到”#"则返回NULL,遇到字符就转化成数字并初始化一个节点

在这里插入图片描述
在这里插入图片描述

```cpp
class Codec {
public:

    // Encodes a tree to a single string.
    string serialize(TreeNode* root) {
        string  res;

        dfs1(root,res);

        return res;
    }

    void dfs1(TreeNode* root,string& res){
        if(!root){
            res+="#,";       //注意别写成res+='#'+',';这样'#'+','会重新计算ASCLL码值并且被当成一个字符

            return;
        }

        res+=to_string(root->val)+',';    //val转化成字符并且+','
        
        dfs1(root->left,res);             //递归遍历左子树和右子树

        dfs1(root->right,res);
    }

    // Decodes your encoded data to tree.
    TreeNode* deserialize(string data) {
        int u=0;                          //遍历下标标记

        return dfs2(data,u);
    }

    TreeNode* dfs2(string &data,int &u){
        if(data[u]=='#'){
            u+=2;

            return NULL;         //遇到'#'返回NULL并且下标+2
        }

        bool  flag=false;        //标记负数
        int t=0;
        while(data[u]!=','){
            if(data[u]=='-')flag=true;
            
            else t=t*10+data[u]-'0';   //转化成数字  字符转int:'1'-'0'=1;int转字符:1+'0'='1';

            u++;
        }

        u++;

        if(flag)t=-t;               //如果为负数,转化成负数

        auto root=new TreeNode(t);

        root->left=dfs2(data,u);    //递归建立左右子树
  
        root->right=dfs2(data,u);

        return root;
    }
};

// Your Codec object will be instantiated and called as such:
// Codec codec;
// codec.deserialize(codec.serialize(root));


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值