算法训练营 Day 17

1、最大二叉树

前序递归:

class Solution {
public:
    TreeNode* constructMaximumBinaryTree(vector<int>& nums) { //nums长度最小为1,不用判空
        TreeNode* node = new TreeNode(0);
        if (nums.size() == 1) {
            node->val = nums[0];
            return node;
        }
        // 找到数组中最大的值和对应的下标
        int maxValue = 0; //为什么赋初值为0,因为nums都是正整数,0足够小了
        int maxValueIndex = 0; //最好赋一个初始值,不然是一个垃圾值
        for (int i = 0; i < nums.size(); ++i) {
            if (nums[i] > maxValue) {
                maxValue = nums[i];
                maxValueIndex = i;
            }
        }
        node->val = maxValue;
        // 最大值所在的下标左区间 构造左子树
        if (maxValueIndex > 0) { //要保证左子树至少要有一个结点才进入递归,否则就跳过
            vector<int> newVec(nums.begin(), nums.begin() + maxValueIndex);
            node->left = constructMaximumBinaryTree(newVec);
        }
        // 最大值所在的下标右区间 构造右子树
        if (maxValueIndex < (nums.size() - 1)) {
            vector<int> newVec(nums.begin() + maxValueIndex + 1, nums.end());
            node->right = constructMaximumBinaryTree(newVec);
        }
        return node;
    }
};

优化后的:

class Solution {
private:
    // 在左闭右开区间[left, right),构造二叉树
    TreeNode* traversal(vector<int>& nums, int left, int right) {
        if (left >= right) return nullptr; //这里进行了判空

        // 分割点下标:maxValueIndex
        int maxValueIndex = left;
        for (int i = left + 1; i < right; ++i) { //分割点不能在两端端点
            if (nums[i] > nums[maxValueIndex])
                maxValueIndex = i;
        }

        TreeNode* root = new TreeNode(nums[maxValueIndex]);

        // 左闭右开:[left, maxValueIndex)
        root->left = traversal(nums, left, maxValueIndex);

        // 左闭右开:[maxValueIndex + 1, right)
        root->right = traversal(nums, maxValueIndex + 1, right);

        return root;
    }

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

2、合并二叉树

前序递归:

class Solution {
public:
    TreeNode* mergeTrees(TreeNode* t1, TreeNode* t2) { // 直接在树t1上进行改动
        if (t1 == nullptr) return t2; // 如果t1为空,合并之后就应该是t2
        if (t2 == nullptr) return t1; // 如果t2为空,合并之后就应该是t1
        // 修改了t1的数值和结构
        t1->val += t2->val;                             // 中
        t1->left = mergeTrees(t1->left, t2->left);      // 左
        t1->right = mergeTrees(t1->right, t2->right);   // 右
        return t1;
    }
};

中序递归和后序递归只用改变中、左、右三条语句的顺序就可以了。

构造新的树:

class Solution {
public:
    TreeNode* mergeTrees(TreeNode* t1, TreeNode* t2) {
        if (t1 == nullptr) return t2;
        if (t2 == nullptr) return t1;
        // 重新定义新的节点,不修改原有两个树的结构
        TreeNode* root = new TreeNode(0);
        root->val = t1->val + t2->val;
        root->left = mergeTrees(t1->left, t2->left);
        root->right = mergeTrees(t1->right, t2->right);
        return root;
    }
};

层序迭代:

class Solution {
public:
    TreeNode* mergeTrees(TreeNode* t1, TreeNode* t2) {
        if (t1 == nullptr) return t2;
        if (t2 == nullptr) return t1;
        queue<TreeNode*> que;
        que.push(t1);
        que.push(t2);
        while(!que.empty()) {
            TreeNode* node1 = que.front(); 
            que.pop();
            TreeNode* node2 = que.front(); 
            que.pop();
            // 此时两个节点一定不为空,val相加
            node1->val += node2->val;

            // 如果两棵树左节点都不为空,加入队列
            if (node1->left != nullptr && node2->left != nullptr) {
                que.push(node1->left);
                que.push(node2->left);
            }
            // 如果两棵树右节点都不为空,加入队列
            if (node1->right != nullptr && node2->right != nullptr) {
                que.push(node1->right);
                que.push(node2->right);
            }
            
            // 当t1的左节点为空 t2左节点不为空,就赋值过去
            if (node1->left == nullptr && node2->left != nullptr) {
                node1->left = node2->left;
            }
            // 当t1的右节点为空 t2右节点不为空,就赋值过去
            if (node1->right == nullptr && node2->right != nullptr) {
                node1->right = node2->right;
            }
            //如果t1和t2的左右孩子都为空,直接跳过这轮循环
            //如果t1左节点不为空,t2左节点为空,也直接跳过这轮循环
            //如果t1右节点不为空,t2右节点为空,也直接跳过这轮循环
        }
        return t1;
    }
};

也可以使用指针去解决:

class Solution {
public:
    void process(TreeNode** t1, TreeNode** t2) { //使用二级指针,是对指针指向的直接修改,就不用再返回指针了
        // 四个终止条件
        if ((*t1) == nullptr && (*t2) == nullptr) return; //为什么要改变指针的指向呢,因为递归调用时要改变指针的指向
        if ((*t1) != nullptr && (*t2) != nullptr) {
            (*t1)->val += (*t2)->val; //当都不为空时,并不终止;至少其中一个为空时才终止
        }
        if ((*t1) == nullptr && (*t2) != nullptr) {
            *t1 = *t2;
            return;
        }
        if ((*t1) != nullptr && (*t2) == nullptr) {
            return;
        }
        
        process(&((*t1)->left), &((*t2)->left));
        process(&((*t1)->right), &((*t2)->right));
    }

    TreeNode* mergeTrees(TreeNode* t1, TreeNode* t2) {
        process(&t1, &t2); //传一级指针的地址
        return t1; //直接在树t1上进行修改
    }
};

实践证明,指针才是最快的,也是最不好理解的。

3、二叉搜索树中的搜索

注意二叉搜索树中不能存在数值相同的结点。

递归法:

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

迭代法:

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

4、验证二叉搜索树

int类型的表示范围为: − 2 31 ∼ 2 31 − 1 -2^{31}\sim2^{31}-1 2312311​,所以结合本题数值范围,最好用long long类型。

递归法:

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

优化之后:

class Solution {
public:
    long long maxVal = LONG_MIN; // 因为后台测试数据中有int最小值
    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;
    }
};

再优化,不使用LONG_MIN

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; //root为空前面判断过了
        pre = root; // 记录前一个节点

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

中序迭代:


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

                cur = cur->right;               // 右
            }
        }
        return true;
    }
};
  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值