【数据结构入门指南】二叉树链式结构的实现(保姆级代码思路解读,非常经典)

简介: 【数据结构入门指南】二叉树链式结构的实现(保姆级代码思路解读,非常经典)

一、前置说明

其他数据结构不同,二叉树的增删查改接口实现的意义不大(后续搜索树的增删查改才有意义)。普通初阶二叉树更重要的是学习控制结构,为后续的AVL树、红黑树等高级数据结构打下基础。同时大部分OJ题也出在此处。


二、二叉树的遍历

所谓二叉树遍历(Traversal)是按照某种特定的规则,依次对二叉树中的节点进行相应的操作,并且每个节点只操作一次。访问结点所做的操作依赖于具体的应用问题。

 

按照规则,二叉树的遍历有:前序/中序/后序的递归结构遍历:

  • 前序遍历(Preorder Traversal 亦称先序遍历)——访问顺序:根节点—>左子树—>右子树
  • 中序遍历(Inorder Traversal)——访问顺序:左子树—>根节点—>右子树
  • 后序遍历(Postorder Traversal)——访问顺序:左子树—>右子树—>根节点

2.1前序遍历

【代码思路】:

  1. 若二叉树为空,则直接返回。
  2. 访问根节点。
  3. 递归遍历左子树,即调用前序遍历函数,传入左子树的根节点。
  4. 递归遍历右子树,即调用前序遍历函数,传入右子树的根节点

代码:

void PrevOrder(BTNode* root)
{
  if (root == NULL)
  {
    printf("NULL ");
    return;
  }
  printf("%d ", root->data);
  PrevOrder(root->left);
  PrevOrder(root->right);
}

2.2中序遍历

【代码思路】:

  1. 首先判断二叉树是否为空,若为空则直接返回。
  2. 对当前节点的左子树进行中序遍历,即递归调用中序遍历函数。
  3. 访问当前节点的值。
  4. 对当前节点的右子树进行中序遍历,即递归调用中序遍历函数。

代码:

void InOrder(BTNode* root)
{
  if (root == NULL)
  {
    printf("NULL ");
    return;
  }
  InOrder(root->left);
  printf("%d ", root->data);
  InOrder(root->right);
}

2.3 后序遍历

【代码思路】:

  1. 判断当前节点是否为空,若为空则返回。
  2. 递归遍历当前节点的左子树。
  3. 递归遍历当前节点的右子树。
  4. 访问当前节点。

代码:

void PostOrder(BTNode* root)
{
  if (root == NULL)
  {
    printf("N ");
    return;
  }
  PostOrder(root->left);
  PostOrder(root->right);
  printf("%d ", root->data);
}

三、以前序遍历为例,递归图解

前序遍历递归图解:



上述三种遍历结果:

  1. 前序遍历结果:1 2 3 4 5 6
  2. 中序遍历结果:3 2 1 5 4 6
  3. 后序遍历结果:3 2 5 6 4 1

四、层序遍历

层序遍历:除了先序遍历、中序遍历、后序遍历外,还可以对二叉树进行层序遍历。设二叉树的根节点所在层数为1,层序遍历就是从所在二叉树的根节点出发,首先访问第一层的树根节点,然后从左到右访问第2层上的节点,接着是第三层的节点,以此类推,自上而下,自左至右逐层访问树的结点的过程就是层序遍历。

【代码思路】:(核心思想:上一层出时带下一层进队列,即根节点出栈时,根节点的孩子节点入栈)

  1. 首先将根节点入栈。
  2. 循环进行以下操作,直到栈为空。
    。弹出栈顶元素,并将其值输出;
    。如果该节点有右子节点,则将右子节点入栈。
    。 如果该节点有左子节点,则将左子节点入栈

栈相关代码自行查看:栈和队列代码实现

代码:

void LevelOrder(BTNode* root)
{
  Queue q;
  QueueInit(&q);
  if (root)
    QueuePush(&q, root);
  while (!QueueEmpty(&q))
  {
    BTNode* front = QueueFront(&q);
    QueuePop(&q);
    printf("%d ", front->data);
    if(front->left)
      QueuePush(&q, front->left);
    if (front->right)
      QueuePush(&q, front->right);
  }
  printf("\n");
  QueueDestroy(&q);
}

五、节点个数以及高度等

5.1 二叉树节点个数

【代码思路】:

  1. 如果二叉树为空,则节点个数为0。
  2. 否则,节点个数等于根节点的个数加上左子树的节点个数和右子树的节点个数。
  3. 递归计算左子树的节点个数。
  4. 递归计算右子树的节点个数。
  5. 返回根节点个数加上左子树和右子树的节点个数。

代码:

int BinaryTreeSize(BTNode* root)
{
  if (root == NULL)
  {
    return 0;
  }
  return  BinaryTreeSize(root->left) + BinaryTreeSize(root->right) + 1;
}

5.2二叉树叶子节点个数

【代码思路】:

  1. 判断根节点是否为空,若为空,则返回0。
  2. 判断根节点的左右子树是否为空,若都为空,则表示根节点是叶子节点,返回1。
  3. 通过递归分别计算左子树和右子树的叶子节点个数,并返回左子树和右子树的叶子节点个数之和。

代码:

int BinaryTreeLeafSize(BTNode* root)
{
  if (root == NULL)
  {
    return 0;
  }
  if (root->left == NULL && root->right == NULL)
  {
    return 1;
  }
  return BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right);
}

5.3 二叉树第k层节点个数

【代码思路】:

  1. 判断二叉树是否为空,如果是则返回0。
  2. 判断k是否等于1,如果是则返回1,表示当前层为第k层,只有一个节点。
  3. 如果以上条件都不满足,则递归计算二叉树的左子树和右子树的第k-1层节点个数,然后将左右子树的第k-1层节点个数相加,即为二叉树第k层节点个数。

代码:

int BinaryTreeLevelKSize(BTNode* root, int k)
{
  assert(k > 0);
  if (root == NULL)
  {
    return 0;
  }
  if (k == 1)
  {
    return 1;
  }
  return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1);
}

5.4 二叉树查找值为x的节点

【代码思路】:

  1. 如果当前节点为空,则返回空。
  2. 如果当前节点的值等于x,则返回当前节点。
  3. 否则,递归查找左子树,如果找到则返回左子树中的节点。
  4. 否则,递归查找右子树,如果找到则返回右子树中的节点。

代码:

BTNode* BTreeFind(BTNode* root, BTDataType x)
{
  if (root == NULL)
    return NULL;
  if (root->data == x)
    return root;
  BTNode* ret1 = BTreeFind(root->left, x);
  if (ret1)
    return ret1;
  BTNode* ret2 = BTreeFind(root->right, x);
  if (ret2)
    return ret2;
  return NULL;
}

5.5 二叉树的高度

【代码思路】:

  1. 如果树为空树,则高度为0。
  2. 如果树不为空树,则高度为左子树的高度和右子树的高度中的较大值加1。
  3. 递归地计算左子树和右子树的高度,并取较大值。
  4. 返回左子树和右子树高度的较大值加1。

代码:

int BinaryTreeHeight(BTNode* root)
{
  if (root == NULL)
  {
    return 0;
  }
  int LeftHeight = BinaryTreeHeight(root->left);
  int RightHeight = BinaryTreeHeight(root->right);
  return LeftHeight > RightHeight ? LeftHeight + 1 : RightHeight + 1;
}

六、二叉树的创建和销毁

6.1 构建二叉树

Tips: 构建二叉树的方式有很多, 这里我们通过前序遍组"ABD##E#H##CF##G##"构建二叉树。(‘#’表示空树

【代码思路】:

  1. 如果节点为“#”表示空,返回空。
  2. 遍历创建左子树,并和根节点链接。
  3. 遍历创建右子树,并和根节点链接。
  4. 返回根节点

代码:

typedef int BTDataType;
typedef struct BinaryTreeNode//结构体类型
{
    BTDataType data;
    struct BinaryTreeNode* left;
    struct BinaryTreeNode* right;
}BTNode;
//创建节点
BTNode* BuyNode(BTDataType x)
{
    BTNode* Node = (BTNode*)malloc(sizeof(BTNode));
    if (Node == NULL)
    {
        perror("malloc fail");
        exit(-1);
    }
    Node->data = x;
    Node->left = NULL;
    Node->right = NULL;
    return Node;
}
//先序创建二叉树
BTNode* createrroot(char* a, int* pi)
{
    if (a[*pi] == '#')
    {
        (*pi)++;
        return NULL;
    }
    BTNode* root = BuyNode(a[*pi]);
    (*pi)++;
    root->left = createrroot(a, pi);
    root->right = createrroot(a, pi);
    return root;
}

6.2 二叉树的销毁

【代码思路】:

  1. 从根节点开始,递归地销毁左子树。
  2. 从根节点开始,递归地销毁右子树。
  3. 将根节点从内存中删除。

代码:

void BinaryTreeDestroy(BTNode* root)
{
  if (root == NULL)
    return;
  BinaryTreeDestory(root->left);
  BinaryTreeDestory(root->right);
  free(root);
}

6.3 判断二叉树是否为完全二叉树

(博主数据结构初阶是用C语言来实现的,所以此处直接栈代码从博主代码仓库中拷贝过来的,读者可以用其他语言来实现,大同小异)

【代码思路】:

  1. 遍历二叉树,使用层次遍历的方式,从根节点开始,逐层遍历每个节点。
  2. 当遇到第一颗空树时停止遍历。
  3. 从第一颗空树开始,后续节点如果有非空就不是完全二叉树,否则为完全二叉树。

栈相关代码自行查看:栈和队列代码实现

代码:

int BinaryTreeComplete(BTNode* root)
{
    Que q;
    QueueInit(&q);
    if (root)
        QueuePush(&q, root);
    while (!QueueEmpty(&q))
    {
        BTNode* front = QueueFront(&q);
        QueuePop(&q);
        //遇空就跳过
        if (front==NULL)
        {
            break;
        }
        QueuePush(&q, root->left);
        QueuePush(&q, root->right);
    }
    //检查后续节点是否有非空
    //有非空就不是完全二叉树
    while (!QueueEmpty(&q))
    {
        BTNode* front = QueueFront(&q);
        QueuePop(&q);
        if (front)
        {
            QueueDestroy(&q);
            return false;
        }
    }
    QueueDestroy(&q);
    return true;
}


相关文章
|
5天前
|
Java C++
【C++数据结构——树】二叉树的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现二叉树的基本运算。​ 相关知识 创建二叉树 销毁二叉树 查找结点 求二叉树的高度 输出二叉树 //二叉树节点结构体定义 structTreeNode{ intval; TreeNode*left; TreeNode*right; TreeNode(intx):val(x),left(NULL),right(NULL){} }; 创建二叉树 //创建二叉树函数(简单示例,手动构建) TreeNode*create
31 12
|
5天前
|
C++
【C++数据结构——树】二叉树的性质(头歌实践教学平台习题)【合集】
本文档介绍了如何根据二叉树的括号表示串创建二叉树,并计算其结点个数、叶子结点个数、某结点的层次和二叉树的宽度。主要内容包括: 1. **定义二叉树节点结构体**:定义了包含节点值、左子节点指针和右子节点指针的结构体。 2. **实现构建二叉树的函数**:通过解析括号表示串,递归地构建二叉树的各个节点及其子树。 3. **使用示例**:展示了如何调用 `buildTree` 函数构建二叉树并进行简单验证。 4. **计算二叉树属性**: - 计算二叉树节点个数。 - 计算二叉树叶子节点个数。 - 计算某节点的层次。 - 计算二叉树的宽度。 最后,提供了测试说明及通关代
31 10
|
5天前
|
存储 算法 测试技术
【C++数据结构——树】二叉树的遍历算法(头歌教学实验平台习题) 【合集】
本任务旨在实现二叉树的遍历,包括先序、中序、后序和层次遍历。首先介绍了二叉树的基本概念与结构定义,并通过C++代码示例展示了如何定义二叉树节点及构建二叉树。接着详细讲解了四种遍历方法的递归实现逻辑,以及层次遍历中队列的应用。最后提供了测试用例和预期输出,确保代码正确性。通过这些内容,帮助读者理解并掌握二叉树遍历的核心思想与实现技巧。
24 2
|
19天前
|
数据库
数据结构中二叉树,哈希表,顺序表,链表的比较补充
二叉搜索树,哈希表,顺序表,链表的特点的比较
数据结构中二叉树,哈希表,顺序表,链表的比较补充
|
2月前
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
76 1
|
2月前
|
机器学习/深度学习 存储 算法
数据结构实验之二叉树实验基础
本实验旨在掌握二叉树的基本特性和遍历算法,包括先序、中序、后序的递归与非递归遍历方法。通过编程实践,加深对二叉树结构的理解,学习如何计算二叉树的深度、叶子节点数等属性。实验内容涉及创建二叉树、实现各种遍历算法及求解特定节点数量。
108 4
|
2月前
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
265 9
|
2月前
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
42 1
|
5天前
|
存储 C语言 C++
【C++数据结构——栈与队列】顺序栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现顺序栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 1.初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储
116 75
|
5天前
|
存储 C++ 索引
【C++数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】
【数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】初始化队列、销毁队列、判断队列是否为空、进队列、出队列等。本关任务:编写一个程序实现环形队列的基本运算。(6)出队列序列:yzopq2*(5)依次进队列元素:opq2*(6)出队列序列:bcdef。(2)依次进队列元素:abc。(5)依次进队列元素:def。(2)依次进队列元素:xyz。开始你的任务吧,祝你成功!(4)出队一个元素a。(4)出队一个元素x。
27 13
【C++数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】