算法学习记录~2023.5.8~二叉树Day6~617.合并二叉树 & 700.二叉搜索树中的搜索 & 98.验证二叉搜索树 &530.二叉搜索树的最小绝对差


617.合并二叉树

题目链接

力扣题目链接

思路1:递归

使用DFS
可以将第一棵树当做主树,在递归时全都返回这棵树,将第二棵树的信息加到第一棵上。
比较关键的是终止条件,那就是root1为空时返回root2,反之亦然,即使同时为NULL也不影响结果

代码

class Solution {
public:
    TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
        if (root1 == NULL)  // 如果root1为空,合并之后就应该是root2(root2为NULL也不影响,都是NULL)
            return root2;
        if (root2 == NULL)
            return root1;

        root1 -> val += root2 -> val;   //使用root1的树当主树

        root1 -> left = mergeTrees(root1 -> left, root2 -> left);   //使用root1的树当主树
        root1 -> right = mergeTrees(root1 -> right, root2 -> right);

        return root1;
    }
};

思路2:迭代法(层序遍历)

和101. 对称二叉树的迭代法很像,把需要同时处理的两个点同时放入队列就可以一起处理。
同样是拿root1当主树,因此除了两树的左/右节点都不为空时需要入队列外,当root1的左/右节点为空而root2的左/右节点不为空时,需要把root2赋给root1。

代码

class Solution {
public:
    TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
        //根为空的话则直接返回另一棵树即可
        if ( root1 == NULL )
            return root2;
        if ( root2 == NULL )
            return root1;

        queue<TreeNode*> que;
        que.push(root1);
        que.push(root2);

        while ( !que.empty() ){
            TreeNode* node1 = que.front();
            que.pop();
            TreeNode* node2 = que.front();
            que.pop();

            node1 -> val += node2 -> val;   //合并,此时肯定都不为NULL

            if( node1 -> left && node2 -> left ){       //如果两棵树左节点都不为空,加入队列
                que.push(node1 -> left);
                que.push(node2 -> left);
            }
            if ( node1 -> right && node2 -> right ){     //如果两棵树右节点都不为空,加入队列
                que.push(node1 -> right);
                que.push(node2 -> right);
            }

            //如果只有node2的左/右子树为空则不需要特殊处理,因为都是返回的root1的树,只有node1的左/右为空而node2不空才需要下列处理
            if(!node1 -> left && node2 -> left){    //当root1的左节点为空,root2左节点不为空,就赋值过去
                node1 -> left = node2 -> left;
            }
            if(!node1 -> right && node2 -> right){  //当root1的右节点为空,root2右节点不为空,就赋值过去
                node1 -> right = node2 -> right;
            }
        }
        return root1;
    }
};

总结


700.二叉搜索树中的搜索

题目链接

力扣题目链接

思路1:递归

利用二叉搜索树的特性可以在比较每一个节点时只将递归引入其中一边的子树

代码

class Solution {
public:
    TreeNode* searchBST(TreeNode* root, int val) {
        if (root == NULL)           //根节点为空直接返回空(用root也一样)
            return NULL;
        if (root -> val == val)     //根结点正好为目标值则返回该树
            return root;

        if (root -> val > val)
            return searchBST(root -> left, val);
        if (root -> val < val)
            return searchBST(root -> right, val);

        return NULL;
    }
};

思路2:迭代法(利用二叉搜索树特性)

一提到二叉树遍历的迭代法,可能立刻想起使用栈来模拟深度遍历,使用队列来模拟广度遍历。
但对于二叉搜索树,因为它的特殊性,也就是节点的有序性,可以不使用辅助栈或者队列就可以写出迭代法。
对于一般二叉树,递归过程中还有回溯的过程,例如走一个左方向的分支走到头了,那么要调头,再走右分支。但对于二叉搜索树,不需要回溯的过程,因为节点的有序性就帮我们确定了搜索的方向

代码

class Solution {
public:
    TreeNode* searchBST(TreeNode* root, int val) {
        while(root != NULL){
            if(root -> val > val)
                root = root -> left;
            else if(root -> val < val)
                root = root -> right;
            else
                return root;
        }
        return NULL;
    }
};

总结


98.验证二叉搜索树

题目链接

力扣题目链接

陷阱

  1. 不能单纯的比较左节点小于中间节点,右节点大于中间节点就完事了,因为需要左子树所有节点小于中间节点,右子树所有节点大于中间节点,因此类似下面的代码就是容易犯的错误
if (root->val > root->left->val && root->val < root->right->val) 
    return true;
else 
    return false;
  1. 样例中最小节点可能是int的最小值,如果这样使用最小的int来比较也是不行的。此时可以初始化比较元素为longlong的最小值。
    但如果样例中根节点的val 可能是longlong的最小值,就通过记录前一个节点在比较时用

思路1:转化为判断数组是否递增

中序遍历下,输出的二叉搜索树节点的数值是有序序列,因此验证二叉搜索树就变成了判断一个序列是否为递增的。

代码

class Solution {
public:
    vector<int> array;  //保存中序遍历后的数组
    void traversal(TreeNode* root){
        if(root == NULL)
            return;
        traversal(root -> left);
        array.push_back(root -> val);
        traversal(root -> right);
    }
    bool isValidBST(TreeNode* root) {
        traversal(root);
        for(int i = 0; i < array.size() - 1; i++){
            if(array[i] >= array[i + 1])
                return false;
        }
        return true;
    }
};

思路2:递归法

这次的递归主要针对陷阱2,第一份代码为将最大值先初始化为longlong最小值来进行比较的开始,第二份代码则优化为取前一个节点的数值来比较

//代码1:将最大值先初始化为longlong最小值
class Solution {
public:
    long long maxVal = LONG_MIN; // 因为后台测试数据中有int最小值
    bool isValidBST(TreeNode* root) {
        if (root == NULL) return true;

        bool left = isValidBST(root->left);
        // 中序遍历,验证遍历的元素是不是从小到大
        if (maxVal < root->val) maxVal = root->val;
        else return false;
        bool right = isValidBST(root->right);

        return left && right;
    }
};
//代码2:取前一个节点的数值来比较
class Solution {
public:
    TreeNode* pre = NULL; // 用来记录前一个节点
    bool isValidBST(TreeNode* root) {
        if (root == NULL) return true;
        bool left = isValidBST(root->left);

        if (pre != NULL && pre->val >= root->val) return false;
        pre = root; // 记录前一个节点

        bool right = isValidBST(root->right);
        return left && right;
    }
};

思路3:迭代法

通过迭代法来模拟二叉树的中序遍历,来完成类似思路2一样的实现

代码

class Solution {
public:
    bool isValidBST(TreeNode* root) {
        stack<TreeNode*> st;
        TreeNode* cur = root;
        TreeNode* pre = NULL;   //记录前一个节点
        while(cur != NULL || !st.empty()){
            if(cur != NULL){
                st.push(cur);
                cur = cur -> left;  //左
            }
            else{
                cur = st.top();     //中
                st.pop();
                if ( pre != NULL && cur -> val <= pre -> val)
                    return false;
                pre = cur;  //保存前一个访问的节点
                cur = cur -> right; //右
            }
        }
        return true;
    }
};

总结

  1. 二叉搜索树的中序遍历是有序数组需要意识到并且善加利用
  2. 陷阱1非常容易被忽略,也就是只处理每个节点和它左右节点的大小关系,而忘了整个子树的所有节点和根结点的大小关系也需要确定

530.二叉搜索树的最小绝对差

题目链接

力扣题目链接

思路1:递归

看到二叉搜索树第一反应就是中序遍历转换成有序数组,然后再对数组进行数据处理,很简单因此代码不写了。
但其实在中序遍历过程中就可以直接计算了,和98.验证二叉搜索树一样也是需要一个pre节点来记录cur节点的前一个节点。
由于是二叉搜索树,因此按中序遍历,cur的值一定大于pre的值,因此不需要abs()函数获取绝对值

代码

class Solution {
public:
    int result = INT_MAX;      //结果要求最小值,那就先初始化为最大值
    TreeNode* pre = NULL;      //用于记录当前节点的前一个节点
    void dfs(TreeNode* cur){
        if (cur == NULL)
            return;
        dfs(cur -> left);   //左
        if (pre != NULL)    //中
            result = min(result, abs(cur -> val - pre -> val));
        pre = cur;          //记录前一个节点
        dfs(cur -> right);  //右
    }

    int getMinimumDifference(TreeNode* root) {
        dfs(root);
        return result;
    }
};

思路2:迭代

同样是利用迭代实现中序遍历,还是需要一个pre节点,在遍历过程中取得计算结果。
由于是二叉搜索树,因此按中序遍历,cur的值一定大于pre的值,因此不需要abs()函数获取绝对值

代码

class Solution {
public:
    int getMinimumDifference(TreeNode* root) {
        stack<TreeNode*> st;
        TreeNode* cur = root;
        TreeNode* pre = NULL;
        int result = INT_MAX;
        while(cur != NULL || !st.empty()){
            if(cur != NULL){        //指针来访问节点到最底层
                st.push(cur);       //将访问的节点入栈
                cur = cur -> left;  //左
            }
            else{
                cur = st.top();
                st.pop();
                if ( pre != NULL )  //中
                    result = min(result, cur ->val - pre -> val);
                pre = cur;
                cur = cur -> right; //右
            }
        }
        return result;
    }
};

总结


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
1. 二叉树的递归遍历: 二叉树的递归遍历是指通过递归方法遍历二叉树的各个节点,按照某种次序访问每个节点。常见的二叉树遍历方式有前序遍历、序遍历和后序遍历。 2. 二叉树的非递归遍历: 二叉树的非递归遍历是指通过循环等非递归方法遍历二叉树的各个节点,按照某种次序访问每个节点。非递归遍历需要借助栈来实现,常见的二叉树遍历方式有前序遍历、序遍历和后序遍历。 3. 二叉树的层次遍历: 二叉树的层次遍历是指按照从上到下、从左到右的顺序遍历每一层节点。常用的方法是使用队列来实现,首先将根节点入队列,然后依次出队列,并将其左右子节点入队列,直到队列为空。 4. 输出二叉树上所有叶节点: 二叉树上的叶节点是指没有子节点的节点。可以通过递归方式,对每个节点进行判断,如果该节点没有左右子节点,则将该节点输出。 5. 求二叉树的高度: 二叉树的高度是指从根节点到叶节点最长路径上经过的边数。可以通过递归方式求解,从左右子树选取较大的一个加上根节点即可。 6. 二叉树层序生成算法二叉树层序生成算法是指按照从上到下、从左到右的顺序依次生成每个节点。可以使用队列来实现,首先将根节点入队列,然后依次出队列,并根据当前节点生成其左右子节点,将其入队列,直到生成完所有节点。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

山药泥拌饭

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值