代码随想录算法训练营第17天 | LeetCode654.最大二叉树、LeetCode617.合并二叉树、LeetCode700.二叉搜索树中的搜索、LeetCode98.验证二叉搜索树

目录

LeetCode654.最大二叉树

1. 创建数组法

2. 索引下标法

LeetCode617.合并二叉树

1. 递归法

2. 迭代法

LeetCode700.二叉搜索树中的搜索

1. 普通二叉树

1.1 递归法

1.2 迭代法

2. 二叉搜索树

2.1 递归法

2.2 迭代法

LeetCode98.验证二叉搜索树

1. 转换法

2. 递归法

3. 迭代法


 

LeetCode654.最大二叉树

给定一个不重复的整数数组 nums 。 最大二叉树 可以用下面的算法从 nums 递归地构建:

  1. 创建一个根节点,其值为 nums 中的最大值。
  2. 递归地在最大值 左边 的 子数组前缀上 构建左子树。
  3. 递归地在最大值 右边 的 子数组后缀上 构建右子树。

返回 nums 构建的 最大二叉树

思路:这道题和之前做过的使用中序和后序遍历求二叉树的题目一样,首先是确定分割索引,然后将所给数组序列分割成左右两部分,再使用递归,生成左右子树。这里给出两种方法,一种是创建数组法,一种是使用索引下标法

整体来说还是比较简单的,因为之前的文章也详细分析过。

1. 创建数组法

TreeNode* constructMaximumBinaryTree(vector<int>& nums) {
        if(nums.size() == 0) return NULL;
        
        int delimiterIndex; //设置分割索引下标
        int max_value = INT_MIN;

        for(int i = 0; i< nums.size(); i ++){
            if(nums[i] > max_value){
                max_value = nums[i]; //记录最大值
                delimiterIndex = i; //记录最大值的下标索引
            }
        }

        TreeNode* root = new TreeNode(max_value);
        if(nums.size() == 0) return root; //返回叶子结点

        //开始划分左右区间
        vector<int> leftnums(nums.begin(), nums.begin() + delimiterIndex);
        vector<int> rightnums(nums.begin() + delimiterIndex + 1, nums.end());

        root -> left = constructMaximumBinaryTree(leftnums);
        root -> right = constructMaximumBinaryTree(rightnums);

        return root;
    }

2. 索引下标法

    TreeNode* traversal(vector<int>& nums, int begin, int end){
        if(end - begin == 0) return NULL;
        int delimiterIndex;
        int max_value = INT_MIN;

        for(int i = begin; i < end; i ++){//注意这里的起始结点为begin,不是0
            if(nums[i] > max_value){
                max_value = nums[i];
                delimiterIndex = i;
            }
        }

        TreeNode* root = new TreeNode(max_value);
        if(end - begin == 1) return root;

        //开始划分左右区间
        int leftBegin = begin;
        int leftEnd = delimiterIndex;
        int rightBegin = delimiterIndex + 1;
        int rightEnd = end;

        root -> left = traversal(nums, leftBegin, leftEnd);
        root -> right = traversal(nums, rightBegin, rightEnd);

        return root;
    }
    TreeNode* constructMaximumBinaryTree(vector<int>& nums) {
        if(nums.size() == NULL) return NULL;
        return traversal(nums, 0, nums.size());
    }

LeetCode617.合并二叉树

给你两棵二叉树: root1root2

想象一下,当你将其中一棵覆盖到另一棵之上时,两棵树上的一些节点将会重叠(而另一些不会)。你需要将这两棵树合并成一棵新二叉树。合并的规则是:如果两个节点重叠,那么将这两个节点的值相加作为合并后节点的新值;否则,不为 null 的节点将直接作为新二叉树的节点。

返回合并后的二叉树。

注意: 合并过程必须从两个树的根节点开始。

 

思路:这里合并二叉树给出了两个二叉树的根节点,操作两个结点的之前我们也遇到过,当时是判断一棵二叉树是否对称。这里我们给出递归法迭代法来进行处理。

1. 递归法

首先对所给两棵树是否为空进行判断,然后创建一个新的root结点,准备作为新的合并后的根节点返回,当两棵树对应位置的结点都存在时,将两值相加,得到新结点,当一方为空一方不为空时,使root就等于不为空的一方,然后对root的左右结点继续进行递归,最后返回root即可。

TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
       if(root1 == NULL && root2 == NULL) return NULL;
        TreeNode* root = new TreeNode(0);
        if(root1 != NULL && root2 != NULL){
            root -> val = root1 -> val + root2 -> val;
            //当左右结点不为空时,将root的值赋值为两结点值之和
        }else if(root1 != NULL && root2 == NULL){
            root = root1;
            //此时一方为空一方不为空,直接令root等于不为空的一方,
            //相当于指向了不为空的一方的所有后序结点,然后返回
            return root;
        }else if(root1 == NULL && root2 != NULL){
            root = root2;
            //同理,同样指向不为空的一方,然后返回
            return root;
        }
        root -> left = mergeTrees(root1 -> left, root2 -> left);
        root -> right = mergeTrees(root1 -> right, root2 -> right);

        return root; 
    }

 当然,还可以进一步精化,当一方为空时,直接返回另一方,然后当两方都不为空时,进行合并,最后递归求新的左右子树,最后返回。

TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
        if(root1 == NULL) return root2;
        if(root2 == NULL) return root1;//就算两者同时为NULL也没事,反正返回NULL
        TreeNode* root = new TreeNode(0);
        root -> val += root1 -> val + root2 -> val;//中
        root -> left = mergeTrees(root1 -> left, root2 -> left);//左
        root -> right = mergeTrees(root1 -> right, root2 -> right);//右

        return root; 
    }

2. 迭代法

使用迭代法时,采用的是辅助队列来对结点进行暂存,在循环中进行比较,这里将node1进行了一个复用,这样结点之间的关联也就保持住了。

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;//这里直接使用了node1
            //处理左子树
            if(node1 -> left != NULL && node2 -> left != NULL){
                que.push(node1 -> left);
                que.push(node2 -> left);
            }
            if(node1 ->left == NULL && node2 -> left != NULL){
                node1 -> left = node2 -> left;
            }
            //处理右子树
            if(node1 -> right != NULL && node2 -> right != NULL){
                que.push(node1 -> right);
                que.push(node2 -> right);
            }
            if(node1 -> right == NULL && node2 -> right != NULL){
                node1 -> right = node2 -> right;
            }
        }
        return root1;
    }

LeetCode700.二叉搜索树中的搜索

给定二叉搜索树(BST)的根节点 root 和一个整数值 val

你需要在 BST 中找到节点值等于 val 的节点。 返回以该节点为根的子树。 如果节点不存在,则返回 null 。

 

思路:这里需要注意一下,二叉搜索树是一种比较特殊的二叉树,可以是空树,并且左子树存在时,左子树所有结点值都小于中间结点的值;右子树存在时,右子树所有结点值都大于中间结点的值;并且左右子树都是二叉搜索树。

于是这里我们采用了普通二叉树找目标值的方法和二叉搜索树找目标结点的方法。

1. 普通二叉树

1.1 递归法

这里我们采用了前序遍历的递归方法来对目标结点进行寻找,并且设置了一个全局变量result对结果进行接收。

TreeNode* result = NULL;
    void traversal(TreeNode* cur, int val){
        if(cur == NULL) return;
        if(cur -> val == val){
            result = cur;//当找到了相应的值后,记录并返回
            return;
        } 
        traversal(cur -> left, val);//遍历左子树
        traversal(cur -> right, val);//遍历右子树
    }
    TreeNode* searchBST(TreeNode* root, int val) {
        if(root == NULL) return NULL;
        traversal(root, val);
        if(result) return result;
        return NULL;
    }

下面是对上面代码的精化。

TreeNode* searchBST(TreeNode* root, int val) {
        if(root == NULL) return NULL;
        if(root -> val == val) return root;
        TreeNode* leftresult = searchBST(root -> left, val);//左子树遍历
        TreeNode* rightresult = searchBST(root -> right, val);//右子树遍历
        if(leftresult) return leftresult;
        if(rightresult) return rightresult;
        return NULL;
    }

1.2 迭代法

这里的迭代法采用了前序遍历的迭代法来对二叉树进行遍历找目标结点,相对比较简单。

TreeNode* searchBST(TreeNode* root, int val) {
        stack<TreeNode*> st;
        if(root == NULL) return NULL;
        st.push(root);
        TreeNode* result = NULL;//记录最终结果
        while(!st.empty()){
            TreeNode* node = st.top();
            st.pop();
            if(node -> val == val){
                result = node;
                break;
            };
            if(node -> right) st.push(node -> right);//右
            if(node -> left) st.push(node -> left);//左
        }
        return result;
    }

2. 二叉搜索树

2.1 递归法

利用二叉搜索树的特性,可以进一步简化代码,如下所示。当所遍历结点值大于目标值,就在其左边查找;当所遍历结点值小于目标值,就在其右边查找。主要是因为二叉树里面的结点值是有序的。

TreeNode* searchBST(TreeNode* root, int val) {
        if(root == NULL || root ->val == val) return root;
        TreeNode* result = NULL;
        if(root -> val > val) result = searchBST(root -> left, val);//中间结点的值大于目标值,遍历左子树
        if(root -> val < val) result = searchBST(root -> right, val);//中间结点的值小于目标值,遍历右子树
        return result;
    }

2.2 迭代法

使用二叉树的特性,进行迭代,代码更加简单。

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 root;
    }

LeetCode98.验证二叉搜索树

给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。

有效 二叉搜索树定义如下:

  • 节点的左

    子树

    只包含 小于 当前节点的数。
  • 节点的右子树只包含 大于 当前节点的数。
  • 所有左子树和右子树自身必须也是二叉搜索树。

思路:这里有两个注意事项

首先,不是简单的左边结点小于中间结点,右边结点大于中间结点就可以了,是应该所有的左结点只要存在,那么都小于中间结点,同理,对于右边结点也是一样;

其次,我们在比较数值的过程中,测试集中可能会存在和int最小值相当的数,设置可能会出现更小的数,这时使用INT_MIN设置最小值就会出现问题。

这里采用三种方法,一种是转换法,一种是递归法,还有是迭代法

1. 转换法

我们知道,中序遍历的序列是左中右的顺序,当我们遍历一棵二叉搜索树时,得到的结果就是一个升序排列的数组(注意是无重复值的)。于是,我们只需要将待验证的二叉搜索树用一个vector数组将其个结点值统计出来看是不是一个升序的序列即可,若是,则为true,反之则为false。

void traversal(TreeNode* cur, vector<int>& vec){
        if(cur == NULL) return;
        traversal(cur -> left, vec);//左
        vec.push_back(cur -> val);//中
        traversal(cur -> right, vec);//右
    }
    bool isValidBST(TreeNode* root) {
        if(root == NULL) return true;
        vector<int> result;
        traversal(root, result);
        for(int i = 0; i < result.size() - 1; i ++){
            if(result[i] >= result[i + 1]) return false;
            //如果出现非升序排列现象,返回false
        }
        return true;
    }

2. 递归法

这里为了避免测试集中的int最小值,于是我们设置了一个LONG_MIN,这样能够避免在比较的时候出问题,思路同样是验证在使用中序遍历的时候是否会出现后面的值小于等于前面的值出现,只要出现立马返回false。

long long max_val = LONG_MIN;//测试数据中有小于INT_MIN的数,所以采用LONG_MIN
    bool isValidBST(TreeNode* root) {
        if(root == NULL) return true;

        int left = isValidBST(root -> left);//左

        if(root -> val > max_val) max_val = root -> val;//记录遍历的结点是否是升序的,中
        else return false;

        int right = isValidBST(root -> right);//右

        return left && right;
    }

当然还有一种方法,这种方法更好理解,能够解决前面的问题,并且还能够解决测试集中出现比LONG_MIN还小的数的情况,就是设置一个pre结点,用来记录前一个遍历结点,每次遍历的结点都与前一个结点的值进行比较,不满足升序直接返回false。

TreeNode* pre = NULL;
    bool isValidBST(TreeNode* root) {
        if(root == NULL) return true;

        int left = isValidBST(root -> left);//左

        if(pre != NULL && pre -> val >= root -> val) return false;
        pre = root;//记录前一个结点,能够更好的比较,中

        int right = isValidBST(root -> right);//右

        return left && right;
    }

3. 迭代法

这里采用了中序遍历的迭代方法,代码和普通的中序遍历是差不多的,只是增加了中间结点的条件判断,这里依然是使用了记录前一个结点的方法,与现在所遍历结点进行比较。

 bool isValidBST(TreeNode* root) {
        stack<TreeNode*> st;
        if(root == NULL) return true;
        TreeNode* pre = NULL; //记录前一个结点
        TreeNode* node = root;
        while(node != NULL || !st.empty()){
            if(node != NULL){
                st.push(node);
                node = node -> left;//左
            }else{
                node = st.top(); //中
                st.pop();
                if(pre != NULL && pre -> val >= node -> val) return false;
                pre = node; //记录前一个结点
                node = node -> right;//右
            }
        }
        return true;
    }

感谢你的阅读,希望我的文章能够给你帮助,如果有帮助,麻烦点赞加收藏,或者点点关注,非常感谢。

如果有什么问题欢迎评论区讨论!

  • 29
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值