算法训练day20||654.最大二叉树

654.最大二叉树

思路:

构造二叉树一般都是使用前序遍历,先构造出二叉树的根节点然后递归构造我们的左右子树。

递归三部曲:

1.确定返回值和参数:因为构造二叉树所以要返回根节点,参数是数组。

TreeNode* constructMaximumBinaryTree(vector<int>& nums)

2.确定递归终止条件:如果nums数组的大小为1,说明遍历到了叶子节点,那么return 根据数组元素构造出来的节点

TreeNode* node = new TreeNode(0);
if (nums.size() == 1) {
    node->val = nums[0];
    return node;
}

3.单层循环逻辑:遍历数组寻找最大值和最大值所在的位置,找到最大值对当前根节点进行赋值,找到最大值的位置用于切割数组的左右两个数组。找到切割点后进行切割数组,然后向左右进行递归构造,向左右进行递归构造的条件是,左右数组的大小至少大于1.

int maxValue = 0;
int maxValueIndex = 0;
for (int i = 0; i < nums.size(); i++) {
    if (nums[i] > maxValue) {
        maxValue = nums[i];
        maxValueIndex = i;
    }
}
TreeNode* node = new TreeNode(0);
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);
}

整体代码:

class Solution {
public:
    TreeNode* constructMaximumBinaryTree(vector<int>& nums) {
        TreeNode* node = new TreeNode(0);
        if (nums.size() == 1) {
            node->val = nums[0];
            return node;
        }
        // 找到数组中最大的值和对应的下标
        int maxValue = 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;
    }
};

这样写出的代码还比较冗余,每次递归时都需要切割原数组创建新的vector,所以为了优化代码,我们可以不创建新数组而是通过操作索引小标来分割数组。

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

可以发现上面的代码看上去简洁一些,主要是因为第二版其实是允许空节点进入递归,所以不用在递归的时候加判断节点是否为空

617.合并二叉树

思路:

同时操作两颗二叉树的题目,涉及到同时遍历二叉树相同位置的节点。

递归三部曲:

1.确定返回值类型和参数:返回值类型返回合并后的二叉树的根节点,所以返回值类型是指向二叉树根节点的指针,参数是操作的两颗二叉树根节点

TreeNode* mergeTrees(TreeNode* t1, TreeNode* t2) {

2.确定递归终止条件:如果遍历的二叉树t1的节点为null,return t2遍历到的节点,如果遍历到t2的节点为null则返回t1,可能这时会出现一个疑惑t1和t2同时为null怎么办?其实如果t1和t2同时为null返回t1和t2等同于返回null.

if (t1 == NULL) return t2; // 如果t1为空,合并之后就应该是t2
if (t2 == NULL) return t1; // 如果t2为空,合并之后就应该是t1

3.确定单层递归:

t1->val += t2->val;

 代码展示:

class Solution {
public:
    TreeNode* mergeTrees(TreeNode* t1, TreeNode* t2) {
        if (t1 == NULL) return t2; // 如果t1为空,合并之后就应该是t2
        if (t2 == NULL) 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;
    }
};

注意!!!!!有一个点大家可能会疑惑,比如二叉树1只有一个结点,二叉树2有很多层结点,递归循环只进行了一层,表面看二叉树1只接上了左右两个子节点,但是左右两个子节点会将之前的左右子树都带上,而且不用担心的是,原先的二叉树1,对应结点的地方都为空,所以直接继承了二多的节点,而不会出错。

700.二叉搜索树中的搜索

思路:

这与普通二叉树不同,这是一颗二叉搜索树。

二叉搜索树的概念:根节点的左子树里的节点的值都小于根节点的值,根节点的右子树里的所有节点的值都大于根节点,左右子树也满足这一特性。

利用二叉搜索树的特性,我们可以知道,遍历这颗二叉树找元素不需要使用前中后序遍历,我们可以有方向的遍历一颗二叉树。

递归三部曲:

1.确定返回值类型和参数:因为要返回找到的根节点所以需要返回指向二叉树结点的指针,参数是二叉树根节点和目标值。

2.确定终止条件:

if (root == NULL || root->val == val) return root;

3.确定单层逻辑:如果val>root->val,向右递归,如果val<root->val,向左递归。

TreeNode* result = NULL;
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 == 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;
    }
};

这里不用在左右递归后面写如果result!=null,就return之类的话,因为这是二叉搜索树,是有方向的递归搜索,如果找到了,返回回去,也是按照原路直上。

98.验证二叉搜索树

思路:

验证一颗二叉树是不是二叉搜索树,实质上就是验证他的结点顺序是不是正确的,根据二叉搜索树的特性,如果我们使用中序遍历二叉树搜索树,那么得到的结果是有序的。

方法1:我们遍历中序遍历一颗二叉搜索树,将遍历到的节点压入数组中,然后再验证数组是否是有序数组,如果有序,那么就是二叉搜索树

方法2:不创建数组,直接在二叉树中验证是否是二叉搜索树,定义一个全局变量maxvalue,初始值赋值为LONG_INT,用中序遍历,如果如果遍历到的数值比maxvalue大,就将maxvalue赋值为该结点的值,如果不比他大,那么就不是有序的,不是二叉搜索树。return false;

方法2的弊端:二叉树左下角的数值也为LONG_MIN那么无法使用这个方法。

方法3:定义一个pre全局指针用来记录遍历到上一个节点的指针,如果每次遍历到的节点的值都大于上一次遍历到节点的值,证明是二叉搜索树。

方法3代码细节:

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值