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

654.最大二叉树

1.分析及思路

路径总和 从中序与后序遍历序列构造二叉树非常类似,只是处理的时候有点区别,单层递归逻辑一样,只不过一个是用根节点划分,一个是用最大的数划分。凡是构造二叉树之类的题目,都是使用中序遍历进行的,只有有了根节点,才能向外延申,也符合逻辑。

递归三部曲:

1.函数的功能、函数的传入参数、函数的返回值

函数的功能:生成一个结点,并且给它赋值左右孩子

函数的传入参数:题目是根据数组划分的,所以要有一个数组,即int*型。还要有一个数组大小的int型用来标记数组边界。

函数的返回值:找左右孩子时,结点本身是找不到的,只能向下一层递归寻找,所以返回值是struct TreeNode*

2.终止条件

因为数组最小为1,所以当数组大小为1时,终止循环。也可以不加这句,因为数组为1时不会进入递归,到函数最后也是会退出循环。

3.单层递归逻辑

拿到一个数,就为它分配空间,并且寻找其左右孩子。

寻找左右孩子就是递归的调用函数

2.代码及分析

代码中的区间是左闭右开

//获取最大值的下标
int GetMaxIndex(int* nums,int numsSize){
    int max = nums[0];//初始化最大值为首元素
    int maxIndex = 0;//下标为0
    for(int i=0;i<numsSize;i++){
        if(nums[i] > max){//如果当前值大于最大值
            max = nums[i];//更新最大值
            maxIndex = i;//并且记录下标
        }
    }
    return maxIndex;//返回最大值的下标
}

struct TreeNode* constructMaximumBinaryTree(int* nums, int numsSize) {
    int maxIndex = GetMaxIndex(nums,numsSize);//获取数组中最大的值的下标
    struct TreeNode* Node = (struct TreeNode*)malloc(sizeof(struct TreeNode));
    Node->left = NULL;
    Node->right = NULL;
    Node->val = nums[maxIndex];//初始化结点,并写入最大值
    if(numsSize == 1) return Node;//若数组中只有一个元素,则不用再次划分,直接返回它即可。不加它也可以,就是多执行了几步
    if(maxIndex != 0)//若最大值的下标不是左边界,则递归的向左寻找其左孩子
        Node->left = constructMaximumBinaryTree(nums,maxIndex);
    if(maxIndex != numsSize-1)//若最大值的下标不是右边界,则向右寻找其右孩子
        Node->right = constructMaximumBinaryTree(&nums[maxIndex+1],numsSize-1-maxIndex);
    return Node;
}

卡哥思路

思路大致一样,只是递归出口不一样。由于递归时就要产生新结点,所以在一开始就申请空间,然进行递归出口的判断,最后在进行左右孩子寻找的递归。

1.函数的功能、函数的传入参数、函数的返回值

函数的功能:生成一个结点,并且给它赋值左右孩子

函数的传入参数:题目是根据数组划分的,所以要有一个数组,即int*型。还要有一个数组大小的int型用来标记数组边界。

函数的返回值:找左右孩子时,结点本身是找不到的,只能向下一层递归寻找,所以返回值是struct TreeNode*

2.终止条件

由于题目不会给一个空数组,所以在递归时,遇到只剩下一个元素时结束掉。就算它递归进去也不会有任何数值产生,因为它会成为叶子结点,不会传入左右数组。

3.单层递归逻辑

拿到一个数,就为它分配空间,并且寻找其左右孩子。

寻找左右孩子就是递归的调用函数

//获取最大值的下标
int GetMaxIndex(int* nums,int numsSize){
    int max = nums[0];//初始化最大值为首元素
    int maxIndex = 0;//下标为0
    for(int i=0;i<numsSize;i++){
        if(nums[i] > max){//如果当前值大于最大值
            max = nums[i];//更新最大值
            maxIndex = i;//并且记录下标
        }
    }
    return maxIndex;//返回最大值的下标
}

struct TreeNode* constructMaximumBinaryTree(int* nums, int numsSize) {
    //每次递归都说明有一个新结点的产生
    struct TreeNode* Node = (struct TreeNode*)malloc(sizeof(struct TreeNode));
    //初始化该结点
    Node->left = NULL;
    Node->right = NULL;
    if(numsSize == 1){//若数组中只剩下一个元素,则说明遍历结束
        Node->val = nums[0];//把元素赋值给结点
        return Node;
    }
    int maxIndex = GetMaxIndex(nums,numsSize);//获取数组中最大的值的下标
    Node->val = nums[maxIndex];//不是最后一个元素,则保存数组中最大的值,作为结点的值
    if(maxIndex != 0){//不是左边界,则向左寻找左孩子
        Node->left = constructMaximumBinaryTree(nums,maxIndex);
    }
    if(maxIndex != (numsSize-1))//若最大值的下标不是右边界,则向右寻找其右孩子
        Node->right = constructMaximumBinaryTree(nums+maxIndex+1,numsSize-1-maxIndex);
    return Node;
}

617.合并二叉树

题目链接/文章讲解: 代码随想录

 1.分析及思路

同步遍历两个二叉树,把对应的元素相加即可。前序遍历符合一般的思路,先多中结点进行合并,然后再进行左右结点的合并。因为要遍历树所以可以采用递归,这里使用前序遍历的递归。

递归三部曲:

1.函数的功能、函数的传入参数、函数的返回值

函数的功能:合并两个树,返回其根节点

函数的传入参数:合并两个树,很明显,传入两个根节点

函数的返回值:最终会返回结点

2.终止条件

假设遇到非空结点,处理顺序是,合并其值,然后修改其左右孩子的值。所以它不能作为终止条件。当遇到非空时,没有办法进行合并值。所以作为终止条件。有三种情况

root1 == NULL和root2 == NULL

root1 != NULL 和root2 == NULL

root1 == NULL和root2 != NULL

三种情况

3.单层递归的逻辑

拿到一个非空的结点,要做的是就是,中结点合并,然后寻找其左孩子的值,右孩子的值。

即左孩子的值,右孩子的值就是调用该函数。

2.代码及注释

struct TreeNode* mergeTrees(struct TreeNode* root1, struct TreeNode* root2) {
    if(root1 == NULL && root2 != NULL) return root2;//当root1为空时,返回root2
    if(root1 != NULL && root2 == NULL) return root1;//当root2为空时,返回root1
    if(root1 == NULL && root2 == NULL) return NULL;//都为空时,返回空
    struct TreeNode* Node = (struct TreeNode*)malloc(sizeof(struct TreeNode));
    Node->val = root1->val + root2->val;//中。中结点合并
    Node->left = mergeTrees(root1->left,root2->left);//左。寻找左孩子的值
    Node->right = mergeTrees(root1->right,root2->right);//右。寻找右孩子的值
    return Node;//返回该结点
}

卡哥思路

大体思路一样只是,递归的出口简化了。用的root1作为返回的结果。

struct TreeNode* mergeTrees(struct TreeNode* root1, struct TreeNode* root2) {
    if(root1 == NULL) return root2;//当root1为空时,如果root为空,则还是返回空,空也是root2
    if(root2 == NULL) return root1;//所以它两个谁为空返回另一个即可
    root1->val = root1->val + root2->val;//中。中结点合并
    root1->left = mergeTrees(root1->left,root2->left);//左。寻找左孩子的值
    root1->right = mergeTrees(root1->right,root2->right);//右。寻找右孩子的值
    return root1;//返回该结点
}

700.二叉搜索树中的搜索

题目链接/文章讲解:  代码随想录

1.当成普通的树 

1.1分析及思路

当成普通的二叉树时,只需在遍历整个树时,加上判断值即可。

递归三部曲:

1.函数的功能、函数的传入参数、函数的返回值

函数的功能:查找树中是否有该值

传入参数:一个结点、一个要查找的数值

返回值:要返回一个结点,因为下边的结点要上返回找没找到

2.终止条件

当传入空结点时,无法查找所以结束。

3.单层递归逻辑

拿到一个结点,先判断和目标值相等吗,相等就返回,不相等就向左右子树寻找。

1.2代码及注释

struct TreeNode* searchBST(struct TreeNode* root, int val) {
    if(root == NULL) return NULL;//空结点结束
    if(root->val == val)//判断是否等于目标值
        return root;
    struct TreeNode* Node = searchBST(root->left,val);//向左子树寻找
    if(Node != NULL)//左子树没找到
        return Node;
    return searchBST(root->right,val);//向
}

2.二叉搜索树的特性

2.1分析及思路

二叉搜索树中,所有的结点都是左孩子小于中结点小于右孩子,根据这个特性就可以直到目标值的路线,该路线没有找到就说明其不存在。

递归三部曲:

1.函数的功能、返回值、参数

函数的功能:在搜索二叉树中寻找目标值

参数:目标值和二叉树

返回值:目标值的结点

2.终止条件

当结点为空时,说明查找结束。因为不为空时,就会继续进行目标值的判断

3.单层循环逻辑

若等于目标值,则直接返回结果。若小于目标值,则转向右孩子,大于目标值,则转向左孩子。

2.2代码及注释

struct TreeNode* searchBST(struct TreeNode* root, int val) {
    //root为空时,是中断条件。等于目标值是单层递归逻辑
    if( (root == NULL) || (root->val == val) ) return root;
    struct TreeNode* Node = NULL;
    if(root->val > val) Node = searchBST(root->left,val);//暂存左子树的寻找结果
    if(root->val < val) Node = searchBST(root->right,val);//暂存右子树的寻找结果
    return Node;
}

3.迭代法二叉搜索树

3.1分析及思路

对树进行遍历时,很明显是当结点为空时,退出循环。在寻找过程中若找的则退出循环。

按照搜索二叉树的特性进行搜索。

3.2代码及注释

struct TreeNode* searchBST(struct TreeNode* root, int val) {
    while(root != NULL){//非空就说明,还没找完
        if(root->val == val) break;//碰到目标值直接结束循环
        else if(root->val < val) root = root->right;//小于目标值转向左子树
        else root = root->left;//大于目标值转向右子树
    }
    return root;
}

98.验证二叉搜索树 

题目链接/文章讲解: 代码随想录

1.分析及思路

二叉搜索树,根节点大于左子树,小于右子树。每一个结点都符合这个逻辑。所以使用中序遍历时,会得到一个升序的数组。只需在进行中序遍历时,保证前一个结点小于后一个结点即可。

递归三部曲:

1.功能、参数、返回值

功能:判断该树是不是二叉搜索树,即判断你左子树是否全小于根节点,根节点是否全小于右子树。

参数:由于leetcode中全局变量,会影响不同的测试案例,所以不使用全局变量,用struct TreeNode** pre来保存每次遍历该结点的前一个结点。root用来传入二叉排序树的根节点

返回值:由于结点需要快速返回其判断的结果给上一层,且有一个不符合即整个树不符合。所以需要快速返回结果。用bool

2.终止条件

假设遇到一个非空结点,我们要做的就是判断其左子树符合条件吗,判断其左右孩子及根节点符合条件吗,判断其右子树符合条件吗,所以当结点为非空无法进行判断,所以当结点为空时,结束递归。

3.单层递归逻辑

当遇到一个结点时,判断其左子树符合条件吗,即递归调用函数,根节点时判断,上一次的值是否小于它(因为中序遍历一定是一个递增的),判断右子树符合条件吗。

2.代码及注释

//pre用来保存前驱
bool traversal(struct TreeNode** pre,struct TreeNode* root){
    if(root == NULL) return true;//递归出口
    if(!traversal(pre,root->left)) return false;//左子树不符合条件是直接返回空
    if(*pre != NULL && (*pre)->val >= root->val) return false;//根节点与前一个结点进行比较
    *pre = root;//更新结点
    if(!traversal(pre,root->right)) return false;//判断右子树是否符合条件
    return true;
}
bool isValidBST(struct TreeNode* root) {
    struct TreeNode* pre = NULL;//运行开始时,没有前驱
    return traversal(&pre,root);
}

3.其它讲解

注意的是,每一个结点都符合,左子树<根节点<右子树。所以不能进行

(root->val > root->left->val) && (root->val < root->right->val)的判断

则1<5<6符合,
    4<6<7符合,但是整体来看右子树应该都比5大,所以不符合。

因为leetcode每个测试用例的全局变量相互影响,所以用局部变量进行重置,所以传入了一个新的参数。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值