代码随想录算法训练营第二十天| 654.最大二叉树 617.合并二叉树 700.二叉搜索树中的搜索 98.验证二叉搜索树

文章介绍了如何使用递归和迭代方法构造最大二叉树,合并二叉树以及在二叉搜索树中进行搜索。关键在于理解二叉树的性质,如最大值作为根节点、左子树小于根节点、右子树大于根节点等。对于验证二叉搜索树的有效性,可以通过中序遍历并比较节点值来实现。

654.最大二叉树

题目链接

思路:和前序构造二叉树一样,找到最大的值的下标,划分左右区间,然后进入迭代
要注意的就是要保证区间(循环不变量原则)

class Solution {
public:
    TreeNode* constructMaximumBinaryTree(vector<int>& nums) {
        //是 1 是因为题目要求 1 <= nums.length <= 1000
        TreeNode* node = new TreeNode(0);
        if (nums.size() == 1) { 
            node->val = nums[0];
            return node;
        }

        //找数组里面的最大值,然后将该点值成为根节点,然后分割重复
        int maxVal = 0; //0 <= nums[i] <= 1000
        int index = 0;
        for (int i = 0; i < nums.size(); i++) {
            if (nums[i] > maxVal) {
                 maxVal = nums[i];
                 index = i;
            }
        }
        // TreeNode* node = new TreeNode(0);
        node->val = maxVal;
        
        //分割左子树
        if (index > 0) {
            vector<int> newLeft(nums.begin(), nums.begin() + index);
            node->left = constructMaximumBinaryTree(newLeft);
        }
		
		//分割右子树 都是遵循左闭右开
        if (index < nums.size() - 1) {
            vector<int> newRight(nums.begin() + index + 1, nums.end());
            node->right = constructMaximumBinaryTree(newRight);
        }
        
        return node;
    }
};

简化代码如下:

class Solution {
public:
    // 在左闭右开区间[left, right),构造二叉树
    TreeNode* traversal(vector<int>& nums, int left, int right) {
        if (left >= right) return nullptr;
		
		// 分割点下标:maxIndex
        int maxIndex = left;
        for (int i = left + 1; i < right; i++) {
            if (nums[i] > nums[maxIndex]) maxIndex = i;
        }
        TreeNode* node = new TreeNode(nums[maxIndex]);
        
		// 左闭右开:[left, maxValueIndex)
        node->left = traversal(nums, left, maxIndex);
		// 左闭右开:[left, maxValueIndex)
        node->right = traversal(nums, maxIndex + 1, right);

        return node;
    }

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

注意类似用数组构造二叉树的题目,每次分隔尽量不要定义新的数组,而是通过下标索引直接在原数组上操作,这样可以节约时间和空间上的开销。

一些同学也会疑惑,什么时候递归函数前面加if,什么时候不加if,这个问题我在最后也给出了解释。

其实就是不同代码风格的实现,一般情况来说:如果让空节点(空指针)进入递归,就不加if,如果不让空节点进入递归,就加if限制一下, 终止条件也会相应的调整。

617.合并二叉树

题目链接

思路递归三件套
1、确定递归函数的参数和返回值:
首先要合入两个二叉树,那么参数至少是要传入两个二叉树的根节点,返回值就是合并之后二叉树的根节点。
2、确定终止条件:
因为是传入了两个树,那么就有两个树遍历的节点t1 和 t2,如果t1 == NULL 了,两个树合并就应该是 t2 了(如果t2也为NULL也无所谓,合并之后就是NULL)。
反过来如果t2 == NULL,那么两个数合并就是t1(如果t1也为NULL也无所谓,合并之后就是NULL)。
3、确定单层递归的逻辑:
单层递归的逻辑就比较好写了,这里我们重复利用一下t1这个树,t1就是合并之后树的根节点(就是修改了原来树的结构)。

接下来t1 的左子树是:合并 t1左子树 t2左子树之后的左子树。
t1 的右子树:是 合并 t1右子树 t2右子树之后的右子树。
最终t1就是合并之后的根节点。

当然也可以新建一颗树来实现操作两个二叉树

class Solution {
public:
    TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
        if (root1 == nullptr) return root2;
        if (root2 == nullptr) return root1;
        
        TreeNode* node = new TreeNode(0);   
        node->val = root1->val + root2->val;

        node->left = mergeTrees(root1->left, root2->left);

        node->right = mergeTrees(root1->right, root2->right);

        return node;
    }
};

总结
本题可以使用前序后序中序,但是前序是最好理解的,思路和我们构造二叉树的思维是一样的。

700.二叉搜索树中的搜索

题目链接

思路: 搞明白二叉搜索树的概念:

若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
它的左、右子树也分别为二叉搜索树

明白了概念以后,本题就很容易解决了

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

迭代法

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

总结:二叉搜索树(BST)的概念决定了搜索的方向,所以本题很简单,就是在树内找节点元素,迭代法递归法都很简单。

98.验证二叉搜索树

题目链接

给定一个二叉树,判断其是否是一个有效的二叉搜索树。

假设一个二叉搜索树具有如下特征:

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

思路: (直白思路)
首先遍历整个二叉树(按照中序遍历),得到一个数组,根据二叉搜索树的概念,那么得到的这个数组一定是个升序的数组,比较数组的元素和前一个元素,当有不满足的情况,返回false,其他情况返回true;

class Solution {
public:
    vector<int> ans;
    bool isValidBST(TreeNode* root) {
        if (root == nullptr) return true;
       
        isValidBST(root->left);
        ans.push_back(root->val);
        isValidBST(root->right);

        for (int i = 1; i < ans.size(); i++) {
            if (ans[i] <= ans[i - 1]) return false;
        }
        return true;
        
    }
};

思路2:因为构建数组会导致时间空间增加,所以可以通过比较节点是否有序的方法来判断

class Solution {
public:

    long long maxVal = LONG_MIN; // 因为后台测试数据中有int最小值  -2^31 <= Node.val <= 2^31 - 1
    bool isValidBST(TreeNode* root) {
        if (root == nullptr) 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;
    }
};
class Solution {
public:

    TreeNode* pre = nullptr; // 记录前一个节点
    bool isValidBST(TreeNode* root) {
        if (root == nullptr) return true;

        bool left = isValidBST(root->left);

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

        bool right = isValidBST(root->right);

        return left && right; // 返回的时候都为true才是true
    }
};

总结:这道题直接做也行,但是会消耗空间和时间,可以考虑新建一个前节点,然后二叉搜索树的概念,用中序遍历的过程中比较大小,比较后移动。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值