代码随想录算法训练营第十七天| 110.平衡二叉树 (优先掌握递归)、 257. 二叉树的所有路径 (优先掌握递归) 、 404.左叶子之和 (优先掌握递归)

110.平衡二叉树 (优先掌握递归)

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

 

1.分析及思路

判断一个树是不是平衡二叉树,就要判断,其任意结点的左右孩子高度差的绝对值不能超过1。

用后续遍历判断其是不是平衡二叉树,因为左右孩子判断完毕才能判断父节点是不是符合平衡二叉树。

递归三部曲:

1.确定返回值以及传入的参数

因为是根据高度来判断是不是平衡二叉树,所以返回值为int高度值,传入参数就是结点

2.终止条件

传入NULL时结束,返回值为0,因为空结点没有高度。

3.确定单层递归逻辑

对任意结点判断的是,左子树是不是平衡二叉树,右子树是不是平衡二叉树,最后再判断该结点的左右子树高度差是否符合平衡二叉树。若任意一个不是平衡二叉树,就返回-1作为高度。

2.代码及注释 

int GetHigh(struct TreeNode* root) {
    if(root == NULL)//终止条件
        return 0;
    int leftHigh = GetHigh(root->left);//获取左子树高度
    if(leftHigh == -1)//若左子树不是平衡二叉树,则返回-1
        return -1;
    int rightHigh = GetHigh(root->right);//获取右子树高度
    if(rightHigh == -1)//右子树不是平衡二叉树,则返回-1
        return -1;
    //判断结点是否符合二叉树,不符合返回-1
    if( !((leftHigh-rightHigh>=-1)&&(leftHigh-rightHigh<=1)) )
        return -1;
    //若符合二叉树,则返回该结点的高度
    return (leftHigh>rightHigh)?(1+leftHigh):(1+rightHigh);
}
bool isBalanced(struct TreeNode* root) {
    if(root == NULL)//若结点为空结点,则为平衡二叉树
        return true;
    //判断传入结点是否是平衡二叉树
    return (GetHigh(root)==-1)?false:true;
}

257. 二叉树的所有路径 (优先掌握递归)

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

 

1.分析及思路

路径是从根节点开始,到叶子结点结束。所以我们需要用前序遍历进行二叉树的访问。把访问的结点加入栈中用来保存,当遇到叶子结点时,就把所有的元素存储下来。然后进行下一次的路径寻找。

递归三部曲:

1.传入的参数及返回的类型

返回值为空,因为不需要返回什么。

传入的参数:1.结点

                     2.最终需要一个二维数组来存储结果,所以传入一个二维数组。需要对二维数组进行赋值,所以把int* returnSize传入进来

                     3.每次遍历都需要存储路径,所以需要一个栈来实现。

2.递归终止条件

遇到叶子结点时,我们终止,再次之前我们需要把路径存储下来。

3.确定单层递归逻辑

 对每一个结点,都是寻找路径,然后回溯。

2.代码及注释

#define MaxSize 20            //定义栈中元素的最大个数
typedef int ElemType;
typedef struct{
    ElemType data[MaxSize];   //存放栈中的元素
    int top;                  //栈顶指针
}SqStack;
/***************************************************************
** 功  能:初始化顺序栈
** 参  数:Stack 顺序栈的地址
** 返回值:无
****************************************************************/
void InitStack(SqStack *Stack){
    Stack->top = -1;//初始化栈顶指针
}
/***************************************************************
** 功  能:顺序栈的入栈
** 参  数:Stack顺序栈的地址,data传入的数据
** 返回值:true入栈成功  false入栈失败
****************************************************************/
bool Push(SqStack *Stack,ElemType data){
    if(Stack->top == MaxSize-1)      //判断栈是否满,若满则无法入栈
        return false;
    Stack->data[++Stack->top] = data;//栈顶指针先上移1,然后再入栈
    return true;
}
/***************************************************************
** 功  能:顺序栈的出栈
** 参  数:Stack顺序栈的地址,data保存数据的地址
** 返回值:true出栈成功  false出栈失败
****************************************************************/
bool Pop(SqStack *Stack,ElemType *data){
    if(Stack->top == -1)              //判断栈是否为空,为空无法出栈
        return false;
    if(data != NULL)
        *data = Stack->data[Stack->top];//不为空先赋值,然后指针再向下移动
    Stack->top--;
    return true;
}
/***************************************************************
** 功  能:顺序栈的获取栈顶元素
** 参  数:Stack顺序栈,data保存数据的地址
** 返回值:true获取成功  false获取失败
****************************************************************/
bool GetTop(SqStack Stack,ElemType *data){
    if(Stack.top == -1)           //判断栈是否为空,为空没有栈顶元素
        return false;
    *data = Stack.data[Stack.top];//读取栈顶元素
    return true;
}
void construct_paths(struct TreeNode* root, char** paths, int* returnSize, SqStack* sta) {
    Push(sta,root->val); // 将当前节点的值压入栈中

    if (root->left == NULL && root->right == NULL) { // 如果当前节点是叶子节点
        char* path = (char*)malloc(100 * sizeof(char)); // 分配存储路径的内存空间
        int path_len = 0; // 路径长度初始化为0
        for (int i = 0; i < sta->top; i++) { // 遍历栈中的元素
            path_len += sprintf(path + path_len, "%d->", sta->data[i]); // 将节点值按格式写入路径中
        }
        path_len += sprintf(path + path_len, "%d",sta->data[sta->top]); // 将最后一个节点值写入路径中
        paths[(*returnSize)++] = path; // 将路径存入结果数组中并更新返回大小
        return; // 返回
    }
        
    if(root->left){ // 如果左子树不为空
        construct_paths(root->left, paths, returnSize, sta); // 递归处理左子树
        Pop(sta,NULL); //回溯
    }
    if(root->right){ // 如果右子树不为空
        construct_paths(root->right, paths, returnSize, sta); // 递归处理右子树
        Pop(sta,NULL); //回溯
    } 
}

char** binaryTreePaths(struct TreeNode* root, int* returnSize) {
    char** paths = (char**)malloc(sizeof(char*) * 1001); // 分配存储路径的内存空间
    *returnSize = 0; // 返回大小初始化为0
    if (root == NULL) { // 如果根节点为空
        return paths; // 直接返回空路径数组
    }
    SqStack sta; // 定义一个栈
    InitStack(&sta); // 初始化栈
    construct_paths(root, paths, returnSize, &sta); // 构建二叉树路径
    return paths; // 返回路径数组
}

3.部分代码讲解

path_len += sprintf(path + path_len, "%d->", sta->data[i]);

 path + path_len代表起始地址。sprintf的返回值:如果成功,则返回写入的字符总数,不包括字符串追加在字符串末尾的空字符。如果失败,则返回一个负数。

为什么中的代码在最前面那,因为最后一个结点也要加入到栈中。才能保证路径的完整。

模拟运行:

404.左叶子之和 (优先掌握递归)

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

1.分析及思路

把左右子树的左叶子之和返回给根节点,所以用后序遍历。

递归三部曲:

1.传入的参数和返回值

传入的参数:结点,返回值:左右子树的左叶子之和。

2.终止条件

毫无疑问NULL返回。当传入叶子结点时,它没有什么作用左右子树的左叶子之和为0,所以在终止条件中加上叶子结点。

3.确定单层递归的逻辑

对于所有的结点都是,先求出左子树的左叶子之和,再求出右子树左叶子之和。最终加一起。怎么判断左叶子那,就是

    if(root->left!=NULL && root->left->left==NULL && root->left->right==NULL)
        leftSum = root->left->val;

2.代码及注释

// 定义一个函数,计算左叶子节点的和
int sumOfLeftLeaves(struct TreeNode* root){
    // 如果根节点为空,返回0
    if(root == NULL)
        return 0;
    // 如果根节点的左右子节点都为空,返回0
    if(root->left == NULL && root->right == NULL)
        return 0;
    // 递归计算左子树的左叶子节点的和
    int leftSum = sumOfLeftLeaves(root->left);
    // 如果左子树不为空且左子树的左右子节点都为空,将左子树的值赋给leftSum
    if(root->left!=NULL && root->left->left==NULL && root->left->right==NULL)
        leftSum = root->left->val;
    // 递归计算右子树的左叶子节点的和
    int rightSum = sumOfLeftLeaves(root->right);
    // 返回左子树和右子树左叶子节点和的总和
    return rightSum+leftSum;
}

  • 13
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值