【代码随想录】Day 20 二叉树进阶Ⅳ(构造最大二叉树、二树合并、BST搜索、BST验证)

第一题:

力扣

 已AC,用的sort,这是最容易想到的方法,但是操作数比较大,如何优化还没想好:

class Solution {
public:
    TreeNode* getNode(vector<int>& nums) {
        if (nums.size() == 0) return nullptr;
        if (nums.size() == 1) return new TreeNode(nums[0]);
        vector<int> copynums(nums.begin(), nums.end());
        sort(copynums.begin(), copynums.end());
        TreeNode* root = new TreeNode(copynums.back());
        int index = 0;
        for (; index < nums.size(); index++) {
            if (nums[index] == root->val) break;
        }
        vector<int> leftTree(nums.begin(), nums.begin() + index);
        vector<int> rightTree(nums.begin() + index + 1, nums.end());
        root->left = getNode(leftTree);
        root->right = getNode(rightTree);
        return root;
    }

    TreeNode* constructMaximumBinaryTree(vector<int>& nums) {
        return getNode(nums);
    }
};

学习记录:

看了代码随想录才知道的确是find max以后从上到下构造,不过他在原数组的基础上修改的区间替代了新建数组,可以加快代码运行减少内存:

class Solution {
public:
    TreeNode* getNode(vector<int>& nums, int left, int right) {
        if (left >= right) return nullptr;
        
        int index = left, MaxNum = 0;
        for (int i = left + 1; i < right; i++) {
            if (nums[i] > nums[index]) {
                index = i; 
            }
        }

        TreeNode* root = new TreeNode(nums[index]);
        root->left = getNode(nums, left, index);
        root->right = getNode(nums, index + 1, right);
        return root;
    }

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

TIPS:

每次分割不用定义新的数组,而是通过下标索引直接在原数组上操作会更快!

第二题:

力扣

已AC:

class Solution {
public:

    TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
        if (root1 == nullptr && root2 == nullptr) return nullptr;
        else if (root1 == nullptr) return root2;
        else if (root2 == nullptr) return root1;
        else {
            TreeNode* root = new TreeNode(root1->val + root2->val);
            root->left = mergeTrees(root1->left, root2->left);
            root->right = mergeTrees(root1->right, root2->right);
            return root;
        }
    }
};

和代码随想录的差不多,不过我是创建了新节点来存两个节点的和,他是直接用的root1这棵树:

class Solution {
public:

    TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
        if (root1 == nullptr && root2 == nullptr) return nullptr;
        else if (root1 == nullptr) return root2;
        else if (root2 == nullptr) return root1;
        else {
            root1->val += root2->val;
            root1->left = mergeTrees(root1->left, root2->left);
            root1->right = mergeTrees(root1->right, root2->right);
            return root1;
        }
    }
};

第三题:

力扣

 已AC:

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

学习记录:

二叉搜索树:根节点的值比左子树所有节点的值都大,比右子树所有节点的值都小;

因此如果val大于根节点的值,那么应该比较root->right->val和val,即右子树递归;如果val小于根节点的值,应该比较root->left->val,即左子树递归:

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

第四题:

力扣

对二叉搜索树的概念不熟悉,踩太多坑,没能AC。

学习记录:

二叉搜索树:节点的左子树的所有节点值都小于当前节点值,右子树所有节点值都大于当前节点值。

因此按照中序遍历左中右,会得到一个有序递增的数组。验证二叉搜索树,就相当于变成了判断一个序列是不是递增的。

根据上述分析,可以将中序遍历的节点值存入一个vector,然后判断vector是否为递增序列:

class Solution {
private:
    vector<int> vec;
    void traversal(TreeNode* root) {
        if (root == NULL) return;
        traversal(root->left);
        vec.push_back(root->val); // 将二叉搜索树转换为有序数组
        traversal(root->right);
    }
public:
    bool isValidBST(TreeNode* root) {
        vec.clear(); // 不加这句在leetcode上也可以过,但最好加上
        traversal(root);
        for (int i = 1; i < vec.size(); i++) {
            // 注意要小于等于,搜索树里不能有相同元素
            if (vec[i] <= vec[i - 1]) return false;
        }
        return true;
    }
};

该题容易踩的坑:

  • 陷阱1:不能单纯的比较左节点小于中间节点,右节点大于中间节点就完事了。我们要比较的是 左子树所有节点小于中间节点,右子树所有节点大于中间节点。

例如下图:

节点10大于左节点5,小于右节点15,但右子树里出现了一个6,因此不是二叉搜索树。 

  • 陷阱2:如果采用一个int值记录下前一个节点的值,由于样例中最小节点 可能是int的最小值,如果这样使用最小的int来比较也是不行的。

此时可以初始化比较元素为longlong的最小值(LONG_MIN)

本题相当于在寻找一个不符合条件的节点,如果没有找到这个节点就遍历了整个树,如果找到不符合的节点了,立刻返回。

递归三部曲:

  1. 递归函数类型以及参数:bool类型,参数为TreeNode*,还有一个全局变量longlong MaxValue,用来比较遍历的节点是否有序;
  2. 终止条件:节点为空的时候,也是二叉搜索树,因此 if (root == null) return true; 
  3. 单层递归逻辑:中序遍历,每个节点的值都要与前一个节点的值maxVal进行比较,如果小于前一个节点值则返回false,maxVal要一直更新。

因此可以写出以下代码(我比较喜欢在参数列表里回传要修改的值):

class Solution {
public:

    bool isValid(TreeNode* root, long long& MaxVal) {
        if (root == nullptr) return true;
        //中序遍历
        bool left = isValid(root->left, MaxVal);
        if (root->val > MaxVal) MaxVal = root->val;
        else return false;
        bool right = isValid(root->right, MaxVal);
        return left && right;
    }

    bool isValidBST(TreeNode* root) {
        long long max = LONG_MIN;
        return isValid(root, max);
    }
};

避免 初始化最小值,如下方法取到最左面节点的数值来比较:

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

这个方法比较好,可以通过pre是否为空排除掉根节点的情况,如果是用val存数值则不好排除!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,我可以为您实现搜索叉树BST)以及相关的操作。 首先,让我们定义一个BST节点的结构体: ``` typedef struct node { int key; struct node *left; struct node *right; } node_t; ``` 这个结构体包括了节点的值(key)以及左右子节点的指针。现在我们可以定义一个BST的结构体,这个结构体包含了BST的根节点指针: ``` typedef struct bst { node_t *root; } bst_t; ``` 现在,我们可以实现以下操作: 1. 创建一个新的节点 ``` node_t *new_node(int key) { node_t *node = (node_t *) malloc(sizeof(node_t)); node->key = key; node->left = NULL; node->right = NULL; return node; } ``` 2. 向BST中插入一个节点 ``` void insert(bst_t *bst, int key) { node_t *parent = NULL; node_t *current = bst->root; while (current != NULL) { parent = current; if (key < current->key) { current = current->left; } else { current = current->right; } } node_t *node = new_node(key); if (parent == NULL) { bst->root = node; } else if (key < parent->key) { parent->left = node; } else { parent->right = node; } } ``` 3. 获取BST的最小值 ``` node_t *min_value(node_t *node) { while (node->left != NULL) { node = node->left; } return node; } int get_min(bst_t *bst) { return min_value(bst->root)->key; } ``` 4. 获取BST的最大值 ``` node_t *max_value(node_t *node) { while (node->right != NULL) { node = node->right; } return node; } int get_max(bst_t *bst) { return max_value(bst->root)->key; } ``` 5. 在BST搜索一个节点 ``` node_t *search(node_t *node, int key) { if (node == NULL || node->key == key) { return node; } if (key < node->key) { return search(node->left, key); } return search(node->right, key); } node_t *get_node(bst_t *bst, int key) { return search(bst->root, key); } ``` 6. 获取一个节点的前驱节点 ``` node_t *predecessor(node_t *node) { if (node == NULL) { return NULL; } if (node->left != NULL) { return max_value(node->left); } node_t *parent = node->parent; while (parent != NULL && node == parent->left) { node = parent; parent = parent->parent; } return parent; } node_t *get_predecessor(bst_t *bst, int key) { node_t *node = get_node(bst, key); if (node == NULL) { return NULL; } return predecessor(node); } ``` 7. 前序遍历BST ``` void preorder(node_t *node) { if (node != NULL) { printf("%d ", node->key); preorder(node->left); preorder(node->right); } } void traverse_preorder(bst_t *bst) { preorder(bst->root); } ``` 现在,我们已经成功地实现了搜索叉树以及相关操作。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值