数据结构C语言练习(二叉树)

本篇练习题(二叉树):

1.二叉树的前序遍历

2.二叉树中序遍历

3.二叉树的后序遍历

4.单值二叉树

5.对称二叉树

6.检查两颗树是否相同

7.另一颗树的子树

8.二叉树的构建及遍历

1.二叉树的前序遍历

一、二叉树前序遍历的定义

二叉树的前序遍历,遵循 “根 - 左 - 右” 的访问顺序。即先访问根节点,接着访问左子树,最后访问右子树 。例如,对于一棵简单二叉树,根节点值为 5,左子节点值为 3,右子节点值为 7,其前序遍历结果就是 5 -> 3 -> 7 。这种遍历方式在诸如二叉树初始化、计算节点属性等众多场景中广泛应用。

二、解题思路

(一)确定遍历顺序

前序遍历要求我们首先处理根节点,这是整个遍历过程的起始点。在处理完根节点后,按照 “左 - 右” 的顺序分别处理其左子树和右子树。这里采用递归的思想非常直观,因为二叉树的子树本身也是二叉树,对于每一棵子树都可以重复 “根 - 左 - 右” 的操作。

(二)存储遍历结果

为了记录遍历过程中访问到的节点值,我们需要一个数据结构来存储结果。通常可以使用数组来实现。在遍历过程中,将每个访问到的节点值依次存入数组。

(三)计算二叉树节点个数

在使用数组存储遍历结果前,我们需要知道数组的大小,也就是二叉树节点的个数。通过递归地计算左子树节点个数、右子树节点个数,并加上根节点(若根节点存在),就可以得到整个二叉树的节点个数。

三、代码实现及解析

以下是用 C 语言实现二叉树前序遍历的代码:

// 二叉树节点结构体定义
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
};
typedef struct TreeNode TreeNode;

// 计算二叉树节点个数
int TreeSize(TreeNode* root){
    if(root == NULL){
        return 0;
    }
    return 1 + TreeSize(root->left) + TreeSize(root->right);
}

// 前序遍历核心函数
void preOrder(TreeNode* root,int* arr,int* pi){
    if(root == NULL){
        return ;
    }
    arr[(*pi)++] = root->val;
    preOrder(root->left,arr,pi);
    preOrder(root->right,arr,pi);
}

// 前序遍历对外接口
int* preorderTraversal(struct TreeNode* root, int* returnSize) {
    *returnSize = TreeSize(root);
    int* arr = (int*)malloc(sizeof(int) * (*returnSize));

    int i = 0;
    preOrder(root,arr,&i);
    return arr;
}

(一)TreeNode 结构体定义

代码开头定义了二叉树节点的结构体 TreeNode,其中 val 用于存储节点的值,left 和 right 分别是指向左子节点和右子节点的指针。typedef 操作简化了后续代码中对该结构体的引用。

(二)TreeSize 函数

该函数用于计算二叉树的节点个数。当 root 为空时,代表空树,节点个数为 0,直接返回。若 root 不为空,节点个数等于根节点(计为 1)加上左子树节点个数(通过递归调用 TreeSize(root->left) 获得 )与右子树节点个数(通过递归调用 TreeSize(root->right) 获得 )之和。

(三)preOrder 函数

此函数是前序遍历的核心。首先判断当前节点 root 是否为空,若为空则直接返回,不再继续处理。若 root 不为空,先将其值存入数组 arr 中,通过 (*pi)++ 操作来更新数组存储位置。然后,按照前序遍历规则,先递归处理左子树(preOrder(root->left,arr,pi);),再递归处理右子树(preOrder(root->right,arr,pi);) 。

(四)preorderTraversal 函数

作为前序遍历的对外接口函数,它首先调用 TreeSize 函数获取二叉树节点个数,并将其赋值给 *returnSize,用于后续告知调用者存储结果的数组大小。接着,使用 malloc 函数动态分配一个大小合适的数组 arr 用于存储遍历结果。然后,初始化一个变量 i 作为数组下标起始值,调用 preOrder 函数进行前序遍历并填充数组 arr。最后,返回存储前序遍历结果的数组 arr

四、总结

二叉树的前序遍历是二叉树相关算法的基础内容。通过明确遍历顺序,合理存储结果并计算节点个数,我们可以利用递归的方式简洁高效地实现这一算法。理解前序遍历的解题思路和代码实现,不仅有助于解决与二叉树相关的基础问题,还能为学习更复杂的树结构算法打下坚实的基础。在实际应用中,我们可以根据具体需求对代码进行优化和扩展,以满足不同场景下的业务要求。

2.二叉树中序遍历

一、二叉树中序遍历的定义

二叉树的中序遍历,按照 “左 - 根 - 右” 的顺序访问节点。即先遍历左子树,再访问根节点,最后遍历右子树 。例如,对于一个简单二叉树,根节点值为 8,左子节点值为 4,右子节点值为 12,其中序遍历结果就是 4 -> 8 -> 12 。这种遍历方式在二叉搜索树中应用广泛,能使节点值按升序输出。

二、解题思路

(一)递归思路

中序遍历天然适合用递归实现。因为二叉树的子树也是二叉树,对于每一棵子树都可以重复 “左 - 根 - 右” 的操作。递归的终止条件是当前节点为空,此时不再继续深入。对于非空节点,先递归处理左子树,将左子树的节点值按顺序存入结果数组;然后处理根节点,把根节点值存入数组;最后递归处理右子树,将右子树节点值存入数组。

(二)确定存储结构

为了记录遍历过程中访问到的节点值,我们需要一个合适的数据结构。这里选择数组来存储遍历结果。在遍历开始前,需要确定数组的大小,也就是二叉树节点的个数。

(三)计算节点个数

计算二叉树节点个数可以通过递归方式。若根节点为空,节点个数为 0;若根节点不为空,节点个数等于根节点(计为 1)加上左子树节点个数与右子树节点个数之和。通过这种递归计算,就能准确得到二叉树的节点总数,从而为存储遍历结果的数组分配合适大小的空间。

三、代码实现及解析

以下是用 C 语言实现二叉树中序遍历的代码:

// 二叉树节点结构体定义
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
};
typedef struct TreeNode TreeNode;

// 计算二叉树节点个数
int TreeSize(TreeNode* root){
    if(root == NULL){
        return 0;
    }
    return 1 + TreeSize(root->left) + TreeSize(root->right);
}

// 中序遍历核心函数(函数名有误,应体现中序遍历,实际功能是中序遍历)
void preOrder(TreeNode* root,int* arr,int* pi){
    if(root == NULL){
        return ;
    }
    preOrder(root->left,arr,pi);
    arr[(*pi)++] = root->val;
    preOrder(root->right,arr,pi);
}

// 中序遍历对外接口
int* inorderTraversal(struct TreeNode* root, int* returnSize) {
    *returnSize = TreeSize(root);
    int* arr = (int*)malloc(sizeof(int) * (*returnSize));

    int i = 0;
    preOrder(root,arr,&i);
    return arr;
}

(一)TreeNode 结构体定义

代码开头定义了二叉树节点的结构体 TreeNodeval 用于存储节点值,left 和 right 分别是指向左、右子节点的指针。typedef 简化了后续对该结构体的引用。

(二)TreeSize 函数

该函数通过递归计算二叉树节点个数。当 root 为空时,返回 0;若 root 不为空,节点个数等于 1(根节点)加上左子树节点个数(递归调用 TreeSize(root->left) )与右子树节点个数(递归调用 TreeSize(root->right) )之和。

(三)preOrder 函数(实际为中序遍历功能)

此函数是中序遍历的核心。先判断 root 是否为空,为空则返回。若 root 不为空,先递归处理左子树(preOrder(root->left,arr,pi);),按照 “左 - 根 - 右” 顺序,先完成左子树节点值的存储;然后将当前根节点值存入数组 arrarr[(*pi)++] = root->val;);最后递归处理右子树(preOrder(root->right,arr,pi);),从而实现中序遍历并将节点值存入数组。

(四)inorderTraversal 函数

作为中序遍历的对外接口,它首先调用 TreeSize 函数获取二叉树节点个数,并将其赋值给 *returnSize,用于确定存储遍历结果数组的大小。接着使用 malloc 动态分配数组 arr。然后初始化变量 i 为数组下标起始值,调用 preOrder 函数进行中序遍历并填充数组 arr,最后返回存储中序遍历结果的数组 arr

四、总结

二叉树的中序遍历是二叉树操作的重要基础。通过递归的思路、合适的存储结构以及准确的节点个数计算,我们能够实现这一算法。理解中序遍历的解题思路和代码实现,不仅有助于解决二叉树相关的基础问题,还能为深入学习更复杂的数据结构和算法提供有力支撑。在实际应用中,可根据具体需求对代码进行优化和拓展,以适应不同场景的要求。

3.二叉树的后序遍历

在算法领域,二叉树遍历是基础且重要的内容,其中后序遍历按照 “左 - 右 - 根” 的顺序访问节点。接下来,我们深入剖析二叉树后序遍历的解题思路,并展示对应的代码实现。

一、解题思路

(一)递归思想

后序遍历非常适合用递归实现。因为二叉树的子树同样是二叉树,对于每一棵子树都可以重复 “左 - 右 - 根” 的操作。递归的终止条件是当前节点为空,此时不再继续深入。对于非空节点,先递归处理左子树,将左子树的节点值按顺序存入结果数组(如果在递归过程中进行存储操作的话);接着递归处理右子树;最后处理根节点,把根节点值存入数组。

(二)确定存储结构

为了记录遍历过程中访问到的节点值,我们需要一个合适的数据结构。这里选择数组来存储遍历结果。在遍历开始前,需要确定数组的大小,也就是二叉树节点的个数。

(三)计算节点个数

计算二叉树节点个数可采用递归方式。若根节点为空,节点个数为 0;若根节点不为空,节点个数等于根节点(计为 1)加上左子树节点个数与右子树节点个数之和。通过这种递归计算,就能准确得到二叉树的节点总数,从而为存储遍历结果的数组分配合适大小的空间。

二、代码实现及解析

以下是用 C 语言实现二叉树后序遍历的代码:

// 二叉树节点结构体定义
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
};
typedef struct TreeNode TreeNode;

// 计算二叉树节点个数
int TreeSize(TreeNode* root){
    if(root == NULL){
        return 0;
    }
    return 1 + TreeSize(root->left) + TreeSize(root->right);
}

// 后序遍历核心函数(函数名preOrder有误,实际实现后序遍历逻辑)
void preOrder(TreeNode* root,int* arr,int* pi){
    if(root == NULL){
        return ;
    }
    preOrder(root->left,arr,pi);
    preOrder(root->right,arr,pi);
    arr[(*pi)++] = root->val;
}

// 后序遍历对外接口
int* postorderTraversal(struct TreeNode* root, int* returnSize) {
    *returnSize = TreeSize(root);
    int* arr = (int*)malloc(sizeof(int) * (*returnSize));

    int i = 0;
    preOrder(root,arr,&i);
    return arr;
}

(一)TreeNode 结构体定义

代码起始部分定义了二叉树节点的结构体 TreeNodeval 用于存储节点值,left 和 right 分别是指向左、右子节点的指针。typedef 简化了后续对该结构体的引用。

(二)TreeSize 函数

该函数通过递归计算二叉树节点个数。当 root 为空时,返回 0;若 root 不为空,节点个数等于 1(根节点)加上左子树节点个数(递归调用 TreeSize(root->left) )与右子树节点个数(递归调用 TreeSize(root->right) )之和。

(三)preOrder 函数(实际为后序遍历功能)

此函数是后序遍历的核心。先判断 root 是否为空,为空则返回。若 root 不为空,先递归处理左子树(preOrder(root->left,arr,pi);),按照 “左 - 右 - 根” 顺序,先完成左子树节点的遍历;接着递归处理右子树(preOrder(root->right,arr,pi);) ;最后将当前根节点值存入数组 arrarr[(*pi)++] = root->val;),从而实现后序遍历并将节点值存入数组。

(四)postorderTraversal 函数

作为后序遍历的对外接口,它首先调用 TreeSize 函数获取二叉树节点个数,并将其赋值给 *returnSize,用于确定存储遍历结果数组的大小。接着使用 malloc 动态分配数组 arr。然后初始化变量 i 为数组下标起始值,调用 preOrder 函数进行后序遍历并填充数组 arr,最后返回存储后序遍历结果的数组 arr

三、总结

二叉树的后序遍历是二叉树操作的重要基础。通过递归的思路、合适的存储结构以及准确的节点个数计算,我们能够实现这一算法。理解后序遍历的解题思路和代码实现,不仅有助于解决二叉树相关的基础问题,还能为深入学习更复杂的数据结构和算法提供有力支撑。在实际应用中,可根据具体需求对代码进行优化和拓展,以适应不同场景的要求。

4.单值二叉树

在二叉树相关的算法问题中,判断一棵二叉树是否为单值二叉树是一个常见且有趣的题目。今天,我们就来深入探讨这个问题,解析其解题思路并详细解读代码实现。

一、问题描述

单值二叉树的定义是二叉树的每个节点都具有相同的值。我们的任务是编写一个函数,输入一棵二叉树的根节点,判断这棵树是否为单值二叉树。如果是,返回 true;如果不是,返回 false

二、解题思路

解决这个问题的关键在于从根节点出发,逐步检查每个节点及其子节点的值是否一致。我们采用递归的策略,因为二叉树的子树本身也是二叉树,这为递归提供了天然的条件。具体步骤如下:

  1. 首先处理特殊情况,即根节点为空的情况,此时可将空树视为单值二叉树,直接返回 true
  2. 接着检查根节点与它的左子节点(若存在)的值是否相同。若不相同,说明这棵树不是单值二叉树,直接返回 false
  3. 然后检查根节点与它的右子节点(若存在)的值是否相同。若不相同,同样返回 false
  4. 如果根节点与它的左右子节点(存在的情况下)值都相同,那么递归地检查左子树和右子树是否为单值二叉树。只有当左子树和右子树都满足单值二叉树的条件时,整棵树才是单值二叉树。

三、代码实现与解析

以下是用 C 语言实现判断单值二叉树的代码:

// 二叉树节点结构体定义
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
};

bool isUnivalTree(struct TreeNode* root) {
    
    if(root == NULL){
        return true;
    }
    if(root->left && root->val != root->left->val){
        return false;
    }
    if(root->right && root->val != root->right->val ){
        return false;
    }
    return isUnivalTree(root->left) && isUnivalTree(root->right);
}

1. 二叉树节点结构体定义

代码开头定义了二叉树节点的结构体 TreeNode,其中 val 用于存储节点的值,left 和 right 分别是指向左子节点和右子节点的指针。这是二叉树数据结构的基础定义,后续对二叉树的操作都基于此结构体。

2. isUnivalTree 函数

  • 空树处理:函数一开始通过 if(root == NULL) 判断根节点是否为空。如果为空,按照我们前面设定的规则,直接返回 true,将空树视为单值二叉树。
  • 根与左子节点比较if(root->left && root->val != root->left->val) 这一条件判断根节点有左子节点且根节点值与左子节点值不相等的情况。一旦满足此条件,说明树不是单值二叉树,立即返回 false
  • 根与右子节点比较if(root->right && root->val != root->right->val ) 用于判断根节点有右子节点且根节点值与右子节点值不相等的情况。若满足,同样返回 false
  • 递归检查子树:最后,通过 return isUnivalTree(root->left) && isUnivalTree(root->right); 递归地检查左子树和右子树。只有当左子树和右子树都满足单值二叉树条件时,整个表达式才为 true,表示整棵树是单值二叉树;否则返回 false

四、总结

判断单值二叉树这个问题,通过递归的方式能够简洁高效地解决。理解递归的思路以及对二叉树节点的处理逻辑,对于解决类似的二叉树相关算法问题有很大的帮助。在实际应用中,这种判断可以用于数据的校验、特定二叉树结构的筛选等场景。希望通过这篇博客,大家能对单值二叉树的判断算法有更深入的理解。

5.对称二叉树

在二叉树的算法问题中,判断一棵二叉树是否轴对称是一个经典且有趣的题目。今天,我们就来深入剖析这个问题,探讨其解题思路并详细解读代码实现。

一、问题描述

给定一棵二叉树的根节点,我们需要判断这棵二叉树是否关于某条竖直线轴对称。也就是说,树的左子树和右子树在结构和节点值上呈现镜像对称的关系。

二、解题思路

解决这个问题的核心在于比较二叉树的左子树和右子树是否镜像对称。我们采用递归的方法,从根节点的左右子树开始检查。具体步骤如下:

  1. 首先处理特殊情况,即两个子树节点都为空的情况,此时认为它们是对称的,返回 true
  2. 接着检查是否存在一个子树节点为空,另一个不为空的情况,如果是,则说明不对称,返回 false
  3. 然后在两个子树节点都不为空的情况下,比较它们的值是否相等。若不相等,返回 false
  4. 如果前面条件都满足,那么递归地检查左子树的左节点和右子树的右节点是否对称,以及左子树的右节点和右子树的左节点是否对称。只有当这两个递归检查都为 true 时,整棵树才是轴对称的。

三、代码实现与解析

以下是用 C 语言实现判断二叉树是否轴对称的代码:

// 二叉树节点结构体定义
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
};

bool isSameTree(struct TreeNode* p, struct TreeNode* q) {
    //都为空
    if(p == NULL && q == NULL){
        return true;
    }
    // 其中一个非空
    if(p == NULL || q == NULL){
        return false;
    }
    // 都不为空,比较值
    if(p->val != q->val){
        return false;
    }
    //结构相同 + 值相同
    return isSameTree(p->left,q->right) && isSameTree(p->right,q->left);
}

bool isSymmetric(struct TreeNode* root) {
    return isSameTree(root->left,root->right);
    
}

1. 二叉树节点结构体定义

代码开头定义了二叉树节点的结构体 TreeNode,其中 val 用于存储节点的值,left 和 right 分别是指向左子节点和右子节点的指针。这是二叉树数据结构的基础定义,后续对二叉树的操作都基于此结构体。

2. isSameTree 函数

  • 双空情况处理:函数一开始通过 if(p == NULL && q == NULL) 判断 p 和 q 是否都为空。如果都为空,按照我们前面设定的规则,认为这两个子树在当前层次上是对称的,直接返回 true
  • 单空情况处理if(p == NULL || q == NULL) 用于检查是否存在一个子树节点为空,另一个不为空的情况。若满足此条件,说明不对称,立即返回 false
  • 节点值比较if(p->val != q->val) 用于在 p 和 q 都不为空的情况下,比较它们的值。若值不相等,返回 false
  • 递归检查子树:最后,通过 return isSameTree(p->left,q->right) && isSameTree(p->right,q->left); 递归地检查左子树的左节点和右子树的右节点,以及左子树的右节点和右子树的左节点是否对称。只有当这两个递归调用都为 true 时,才返回 true,表示这两个子树在整体上是对称的;否则返回 false

3. isSymmetric 函数

该函数作为判断二叉树是否轴对称的对外接口,直接调用 isSameTree 函数,传入二叉树的左子树和右子树的根节点作为参数。因为判断整棵二叉树是否轴对称,就是判断其左子树和右子树是否镜像对称,所以 isSameTree 的返回值就是最终结果,直接返回即可。

四、总结

判断二叉树是否轴对称通过递归的方式能够清晰且有效地实现。理解递归过程中对节点的各种情况判断以及子树的比较逻辑,对于解决类似的二叉树相关算法问题至关重要。在实际应用中,这种判断可以用于数据结构的校验、特定二叉树结构的筛选等场景。希望通过这篇博客,大家能对二叉树对称性判断算法有更深入的理解。

6.检查两颗树是否相同

在二叉树相关的算法问题中,判断两棵二叉树是否相同是一个基础且重要的题目。今天,我们就来深入探讨这个问题,解析其解题思路并详细解读代码实现。

一、问题描述

给定两棵二叉树的根节点,我们需要编写一个函数来判断这两棵树是否相同。这里的 “相同” 要求两棵树在结构上完全一致,并且对应节点的值也都相等。

二、解题思路

解决这个问题的关键在于从根节点开始,逐步对比两棵树的结构和节点值。我们采用递归的策略,因为二叉树的子树本身也是二叉树,这为递归提供了天然的条件。具体步骤如下:

  1. 首先处理特殊情况,即两棵树的当前节点都为空的情况,此时认为它们是相同的,返回 true
  2. 接着检查是否存在一个节点为空,另一个不为空的情况,如果是,则说明两棵树结构不同,返回 false
  3. 然后在两个节点都不为空的情况下,比较它们的值是否相等。若不相等,返回 false
  4. 如果前面条件都满足,那么递归地检查它们的左子树和右子树是否相同。只有当左子树和右子树都相同,整棵树才是相同的。

三、代码实现与解析

以下是用 C 语言实现判断两棵二叉树是否相同的代码:

// 二叉树节点结构体定义
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
};

bool isSameTree(struct TreeNode* p, struct TreeNode* q) {
    //都为空
    if(p == NULL && q == NULL){
        return true;
    }
    // 其中一个非空
    if(p == NULL || q == NULL){
        return false;
    }
    // 都不为空,比较值
    if(p->val != q->val){
        return false;
    }
    //结构相同 + 值相同
    return isSameTree(p->left,q->left) && isSameTree(p->right,q->right);
}

1. 二叉树节点结构体定义

代码开头定义了二叉树节点的结构体 TreeNode,其中 val 用于存储节点的值,left 和 right 分别是指向左子节点和右子节点的指针。这是二叉树数据结构的基础定义,后续对二叉树的操作都基于此结构体。

2. isSameTree 函数

  • 双空情况处理:函数一开始通过 if(p == NULL && q == NULL) 判断 p 和 q 是否都为空。如果都为空,按照我们前面设定的规则,认为这两个节点在当前层次上是相同的,直接返回 true
  • 单空情况处理if(p == NULL || q == NULL) 用于检查是否存在一个节点为空,另一个不为空的情况。若满足此条件,说明两棵树结构不同,立即返回 false
  • 节点值比较if(p->val != q->val) 用于在 p 和 q 都不为空的情况下,比较它们的值。若值不相等,返回 false
  • 递归检查子树:最后,通过 return isSameTree(p->left,q->left) && isSameTree(p->right,q->right); 递归地检查两棵树的左子树和右子树是否相同。只有当这两个递归调用都为 true 时,才返回 true,表示这两棵二叉树在整体上是相同的;否则返回 false

四、总结

判断两棵二叉树是否相同通过递归的方式能够简洁高效地解决。理解递归过程中对节点的各种情况判断以及子树的比较逻辑,对于解决类似的二叉树相关算法问题有很大的帮助。在实际应用中,这种判断可以用于数据结构的校验、特定二叉树结构的筛选等场景。希望通过这篇博客,大家能对二叉树相同性判断算法有更深入的理解。

7.另一颗树的子树

在二叉树相关的算法问题中,判断一棵二叉树是否为另一棵二叉树的子树是一个具有代表性的题目。今天,我们就来深入剖析这个问题,探讨其解题思路并详细解读代码实现。

一、问题描述

给定两棵二叉树 root 和 subRoot,我们需要编写一个函数来判断 root 树中是否包含 subRoot 树作为子树。这里的子树要求在结构和节点值上都与 subRoot 完全相同。

二、解题思路

解决这个问题的核心在于遍历 root 树的各个节点,尝试以每个节点为根与 subRoot 树进行对比。我们采用递归的方式来实现,先定义一个函数判断两棵树是否完全相同,再利用这个函数在 root 树中递归查找 subRoot 子树。具体步骤如下:

  1. 定义一个函数 isSameTree 用于判断两棵二叉树是否相同。在这个函数中,先处理特殊情况(节点都为空、一个节点为空另一个不为空),然后比较节点值,最后递归比较子树。
  2. 定义主函数 isSubtree,首先处理 root 为空的情况,若为空直接返回 false。然后判断以 root 为根的树和 subRoot 树是否相同,若相同返回 true。若以上情况都不满足,则递归地在 root 的左子树和右子树中查找 subRoot 子树。

三、代码实现与解析

以下是用 C 语言实现判断二叉树子树的代码:

// 二叉树节点结构体定义
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
};

bool isSameTree(struct TreeNode* p, struct TreeNode* q) {
    //都为空
    if(p == NULL && q == NULL){
        return true;
    }
    // 其中一个非空
    if(p == NULL || q == NULL){
        return false;
    }
    // 都不为空,比较值
    if(p->val != q->val){
        return false;
    }
    //结构相同 + 值相同
    return isSameTree(p->left,q->left) && isSameTree(p->right,q->right);
}

bool isSubtree(struct TreeNode* root, struct TreeNode* subRoot) {
    if(root == NULL){
        return false;
    }
    if(isSameTree(root,subRoot)){
        return true;
    }
    return isSubtree(root->left,subRoot) || isSubtree(root->right,subRoot);
}

1. 二叉树节点结构体定义

代码开头定义了二叉树节点的结构体 TreeNode,其中 val 用于存储节点的值,left 和 right 分别是指向左子节点和右子节点的指针。这是二叉树数据结构的基础定义,后续对二叉树的操作都基于此结构体。

2. isSameTree 函数

  • 双空情况处理:函数一开始通过 if(p == NULL && q == NULL) 判断 p 和 q 是否都为空。如果都为空,按照我们前面设定的规则,认为这两个节点在当前层次上是相同的,直接返回 true
  • 单空情况处理if(p == NULL || q == NULL) 用于检查是否存在一个节点为空,另一个不为空的情况。若满足此条件,说明两棵树结构不同,立即返回 false
  • 节点值比较if(p->val != q->val) 用于在 p 和 q 都不为空的情况下,比较它们的值。若值不相等,返回 false
  • 递归检查子树:最后,通过 return isSameTree(p->left,q->left) && isSameTree(p->right,q->right); 递归地检查两棵树的左子树和右子树是否相同。只有当这两个递归调用都为 true 时,才返回 true,表示这两棵二叉树在整体上是相同的;否则返回 false

3. isSubtree 函数

  • 根节点为空处理:函数首先通过 if(root == NULL) 判断 root 是否为空。若为空,直接返回 false,因为空树不可能包含其他子树。
  • 当前树与子树相同判断:接着调用 isSameTree 函数判断以 root 为根的树和 subRoot 树是否相同。若相同,返回 true,表示找到了子树。
  • 递归查找子树:若前面情况都不满足,则通过 return isSubtree(root->left,subRoot) || isSubtree(root->right,subRoot); 递归地在 root 的左子树和右子树中查找 subRoot 子树。只要左子树或右子树中能找到符合条件的子树,就返回 true;否则返回 false

四、总结

判断二叉树子树问题通过合理运用递归和对二叉树节点的比较能够有效解决。理解 isSameTree 函数对两棵树相同性的判断逻辑,以及 isSubtree 函数在整棵树中递归查找子树的过程,对于掌握此类算法问题至关重要。在实际应用中,这种判断可用于数据结构的完整性校验、特定二叉树结构的匹配等场景。希望通过这篇博客,大家能对二叉树子树判断算法有更深入的理解。

8.二叉树的构建及遍历

在数据结构的学习中,二叉树是一个重要的内容,而根据特定遍历序列构建二叉树并进行遍历操作更是常见的算法问题。今天,我们就来深入探讨如何根据先序遍历字符串构建二叉树,并对其进行中序遍历,详细解析相关代码实现。

一、问题描述

本题要求编写程序,根据用户输入的先序遍历字符串构建二叉树(其中 # 表示空节点),然后对构建好的二叉树进行中序遍历并输出结果。例如,输入先序遍历字符串 abc##de#g##f###,构建二叉树后进行中序遍历,应输出 c b e g d f a

二、解题思路

解决这个问题主要分为几个步骤:

  1. 创建二叉树节点:编写函数用于创建二叉树节点,为节点分配内存并初始化节点数据和子节点指针。
  2. 构建二叉树:根据先序遍历字符串,通过递归方式构建二叉树。在遍历字符串时,遇到 # 表示空节点,否则创建新节点并递归构建其左右子树。
  3. 中序遍历:编写函数对构建好的二叉树进行中序遍历,按照 “左 - 根 - 右” 的顺序访问节点并输出节点值。
  4. 主函数流程:在主函数中实现输入先序遍历字符串、调用构建二叉树函数和中序遍历函数的整体流程。

三、代码实现与解析

以下是用 C 语言实现上述功能的代码:

#include 
#include
typedef struct TreeNode{
    char data;
    struct TreeNode* left;
    struct TreeNode* right;
}TreeNode;

TreeNode* buyNode(char ch){
    TreeNode* node = (TreeNode*)malloc(sizeof(TreeNode));
    if(node == NULL){
        perror("Fail malloc");
        exit(1);
    }
    node->data = ch;
    node->left = node->right = NULL;
    return node;
}

TreeNode* createTree(char* arr,int* pi){
    if(arr[*pi] == '#'){
        (*pi)++;
        return NULL;
    }
    TreeNode* root = buyNode(arr[*pi]);
    (*pi)++;
    root->left = createTree(arr,pi);
    root->right = createTree(arr,pi);

    return root;
}

void inOrder(TreeNode* root){
    if(root == NULL){
        return ;
    }
    inOrder(root->left);
    printf("%c ",root->data);
    inOrder(root->right);
}

int main(){
    char arr[100];
    scanf("%s",arr);
    int i = 0;
    TreeNode* root = createTree(arr,&i);
    inOrder(root);
    return 0;
}

1. 二叉树节点结构体定义

代码开头定义了二叉树节点的结构体 TreeNode,包含一个字符类型的 data 成员用于存储节点数据,以及两个指向 TreeNode 结构体的指针 left 和 right,分别指向节点的左子节点和右子节点。

2. buyNode 函数

  • 节点创建与初始化:该函数负责创建二叉树节点。使用 malloc 函数分配内存空间,若分配失败则打印错误信息并终止程序。然后将传入的字符赋值给节点的 data 成员,并将左右子节点指针初始化为 NULL,最后返回创建好的节点指针。

3. createTree 函数

  • 二叉树构建逻辑:此函数根据先序遍历字符串构建二叉树。首先检查当前字符是否为 #,若是则表示空节点,移动指针并返回 NULL。若不是 #,则创建新节点,移动指针后递归构建其左右子树,最终返回构建好的子树的根节点指针。

4. inOrder 函数

  • 中序遍历实现:该函数实现二叉树的中序遍历。先判断节点是否为空,为空则返回。否则先递归遍历左子树,然后输出当前节点数据,最后递归遍历右子树,从而按照 “左 - 根 - 右” 的顺序完成遍历并输出节点值。

5. main 函数

  • 整体流程控制:作为程序入口,定义字符数组接收用户输入的先序遍历字符串,初始化索引变量后调用 createTree 函数构建二叉树,再调用 inOrder 函数进行中序遍历并输出结果,最后返回 0 表示程序正常结束。

四、总结

通过上述代码,我们实现了根据先序遍历字符串构建二叉树并进行中序遍历的功能。理解每个函数的作用和实现逻辑,对于掌握二叉树相关算法至关重要。在实际应用中,类似的操作可用于数据的层次化存储与处理、表达式树的构建与求值等场景。希望通过这篇博客,大家能对二叉树的构建与遍历有更深入的认识。

你可能感兴趣的:(数据结构,c语言)