【初阶数据结构】二叉树链式结构的实现和遍历

简介: 在学习二叉树的基本操作前,需先要创建一棵二叉树,然后才能学习其相关的基本操作。由于现在大家对二叉树结构掌握还不够深入,为了降低大家学习成本,此处手动快速创建一棵简单的二叉树,快速进入二叉树操作学习,等二叉树结构了解的差不多时,我们反过头再来研究二叉树真正的创建方式。

二叉树链式结构的实现

我们可以将一个二叉树分成左右两个子树,两个子树又一次分成两个子树,这样一个二叉树就可以被我们拆解左右拆解开来,如果没有左/右孩子记作空。

代码实现

typedef struct BinaryTreeNode 
{
  struct BinaryTreeNode * left;
  struct BinaryTreeNode* right;
  int  val;
}BTNode;
BTNode* BuyBTNode(int x)
{
  BTNode* tmp = (BTNode*)malloc(sizeof(BTNode));
  if (tmp == NULL)
  {
    perror("malloc failed");
    exit(-1);
  }
  tmp->val = x;
  tmp->left = NULL;
  tmp->right = NULL;
  return tmp;
}
int main()
{
  BTNode* node1 = BuyBTNode(1);
  BTNode* node2 = BuyBTNode(2);
  BTNode* node3 = BuyBTNode(3);
  BTNode* node4 = BuyBTNode(4);
  BTNode* node5 = BuyBTNode(5);
  BTNode* node6 = BuyBTNode(6);
  node1->left = node2;
  node1->right = node4;
  node2->left = node3;
  node4->left = node5;
  node4->right = node6;
  return 0;
}

创建一个结构体里面有左右两个结构体指针分别指向自己左右孩子 ,像这样上面那个二叉树就使用代码实现了。


注意:上述代码并不是创建二叉树的方式,真正创建二叉树方式后序详解重点讲解。

再看二叉树基本操作前,再回顾下二叉树的概念,二叉树是:

1. 空树

2. 非空:根节点,根节点的左子树、根节点的右子树组成的。


从概念中可以看出,二叉树定义是递归式的,因此后序基本操作中基本都是按照该概念实现的。


二叉树的遍历

前序、中序和后序遍历

学习二叉树结构,最简单的方式就是遍历。所谓二叉树遍历(Traversal)是按照某种特定的规则,依次对二叉树中的节点进行相应的操作,并且每个节点只操作一次。访问结点所做的操作依赖于具体的应用问题。 遍历是二叉树上最重要的运算之一,也是二叉树上进行其它运算的基础。


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

1. 前序遍历(Preorder Traversal 亦称先序遍历)——访问根结点的操作发生在遍历其左右子树之前。

2. 中序遍历(Inorder Traversal)——访问根结点的操作发生在遍历其左右子树之中(间)。

3. 后序遍历(Postorder Traversal)——访问根结点的操作发生在遍历其左右子树之后。


简单点来说:

前序遍历:先访问根,在访问左子树,最后访问右子树。

中序遍历:先访问左子树,在访问根,最后访问右子树。

后序遍历:先访问左子树,在访问右子树,最后访问根。


由于被访问的结点必是某子树的根,所以N(Node)、L(Left subtree)和R(Right subtree)又可解释为根、根的左子树和根的右子树。NLR、LNR和LRN分别又称为先根遍历、中根遍历和后根遍历。



这里的N代表空(NULL)。

前序遍历

二叉树的遍历是使用函数递归实现的,从根开始一次分为左右子树向下遍历左右子树左右子树不存在的话为空(NULL)。


前序遍历图解

实现代码

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


首先判断根节点是否为空,如果为空则代表空树返回NULL;前序遍历是先访问的根,因此直接打印数据,在依次进入左右子树。这里文字很难讲解清楚,我们直接上函数递归调用图。


中序遍历

这里我就不给大家图解了,大家可以自己尝试画一下。

实现代码

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


首先判断根节点是否为空,如果为空则代表空树返回NULL;中序遍历是先访问的左子树,因此一直遍历到最后一个根指向的左子树为空时,打印此时根的数据,在依次进入左右子树。

后序遍历

实现代码

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

首先判断根节点是否为空,如果为空代表空树返回NULL;后序遍历也是先访问左子树,因此一直遍历到最后一个根指向的左子树为空时打印此时根的数据,在打印其父亲指向的右子树的数据,最后打印左右子树父亲的数据。


求结点个数

求总的结点个数

创建变量求结点

很多小伙伴,会想到创建一个变量函数递归依次就代表一个结点,每次递归就加加一次,这样根本不对,因为变量是开辟在栈区上的函数每次调用都会创建,出函数也都会销毁,这种办法根本行不通。


创建静态修饰变量

使用变量可能会销毁,那我就创建静态变量改变它的生命周期,这样就可以了,求一个二叉树的总节点个数可以,但是我们有多个二叉树呢?也可以解决:在每次求总结点个数前将变量置零就可以了,这是一个可行的办法。

ps:静态变量在函数递归是只会创建一次哦!!!


拆分左右子树加根

二叉树最重要的思想就是拆分二叉树,这里我们将二叉树分成左右子树,使用递归求左右子树的结点个数每次再加上根节点是不是就是总的节点数?

实现代码

int size = 0;
int TreeSize(BTNode* root)
{
  //静态区的变量只初始化一次
  // int size=0;
    //直接使用变量不推荐
  //static int size = 0;
  //if (root == NULL)
  //{
  //  return 0;
  //}
  //else
  //{
  //  ++size;
  //}
  //TreeSize(root->left);
  //TreeSize(root->right);
  //return size;
  //优化
  return root == NULL ? 0 : TreeSize(root->left) + TreeSize(root->right) + 1;
}


求叶子节点的个数

情况1:当为空树是叶子节点为空;

情况2:当左右子树为空时叶子结点为1;

情况3:还是使用拆分的方法将一个二叉树拆分,使用递归求解;

实现代码

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


求第K层结点个数

假设我们求第三层,还是使用递归拆分树的方法,拆分成左子树的第二层(k-1)加上右子树的第二层(k-1)层。

实现代码

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


完整代码

#define _CRT_SECURE_NO_WARNINGS 67
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef struct BinaryTreeNode 
{
  struct BinaryTreeNode * left;
  struct BinaryTreeNode* right;
  int  val;
}BTNode;
BTNode* BuyBTNode(int x)
{
  BTNode* tmp = (BTNode*)malloc(sizeof(BTNode));
  if (tmp == NULL)
  {
    perror("malloc failed");
    exit(-1);
  }
  tmp->val = x;
  tmp->left = NULL;
  tmp->right = NULL;
  return tmp;
}
void PrevOrder(BTNode* node)
{
  if (node == NULL)
  {
    printf("NULL ");
    return ;
  }
  printf("%d ", node->val);
  PrevOrder(node->left);
  PrevOrder(node->right);
}
void InOrder(BTNode* node)
{
  if (node == NULL)
  {
    printf("NULL ");
    return ;
  }
  InOrder(node->left);
  printf("%d ", node->val);
  InOrder(node->right);
}
void PostOrder(BTNode* node)
{
  if (node == NULL)
  {
    printf("NULL ");
    return ;
  }
  PostOrder(node->left);
  PostOrder(node->right);
  printf("%d ", node->val);
}
//求结点个数
int size = 0;
int TreeSize(BTNode* root)
{
  //静态区的变量只初始化一次
  // int size=0;
  //static int size = 0;
  //if (root == NULL)
  //{
  //  return 0;
  //}
  //else
  //{
  //  ++size;
  //}
  //TreeSize(root->left);
  //TreeSize(root->right);
  //return size;
  //优化
  return root == NULL ? 0 : TreeSize(root->left) + TreeSize(root->right) + 1;
}
//求叶子结点个数
int TreeLeafSize(BTNode* root)
{
  if (root == NULL)
  {
    return 0;
  }
  if (root->left == NULL && root->right == NULL)
  {
    return 1;
  }
  return TreeLeafSize(root->left) + TreeLeafSize(root->right);
}
//求第K层的结点个数
int TreeKlevel(BTNode* root,int k)
{
  assert(k > 0);
  if (root == NULL)
  {
    return 0;
  }
  if (k == 1)
  {
    return 1;
  }
  return TreeKlevel(root->left, k - 1) + TreeKlevel(root->right, k - 1);
}
int main()
{
  BTNode* node1 = BuyBTNode(1);
  BTNode* node2 = BuyBTNode(2);
  BTNode* node3 = BuyBTNode(3);
  BTNode* node4 = BuyBTNode(4);
  BTNode* node5 = BuyBTNode(5);
  BTNode* node6 = BuyBTNode(6);
  node1->left = node2;
  node1->right = node4;
  node2->left = node3;
  node4->left = node5;
  node4->right = node6;
  //前序遍历
  PrevOrder(node1);
  printf("\n");
  //中序遍历
  InOrder(node1);
  printf("\n");
  //后序遍历
  PostOrder(node1);
  printf("\n");
  printf("%d \n", TreeSize(node1));
  printf("%d\n", TreeLeafSize(node1));
  printf("%d\n", TreeKlevel(node1, 3));
  return 0;
}



总结: 二叉树的遍历是用函数递归实现的,确实比较难以理解,我们不仅仅是只做到代码实现层面,还好依据函数递归画出递归的展开图,以便于我们理解和加深印象,这块部分还得多画图。

相关文章
|
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天前
|
数据库
数据结构中二叉树,哈希表,顺序表,链表的比较补充
二叉搜索树,哈希表,顺序表,链表的特点的比较
数据结构中二叉树,哈希表,顺序表,链表的比较补充
|
5天前
|
数据采集 存储 算法
【C++数据结构——图】图的遍历(头歌教学实验平台习题) 【合集】
本文介绍了图的遍历算法,包括深度优先遍历(DFS)和广度优先遍历(BFS)。深度优先遍历通过递归方式从起始节点深入探索图,适用于寻找路径、拓扑排序等场景;广度优先遍历则按层次逐层访问节点,适合无权图最短路径和网络爬虫等应用。文中提供了C++代码示例,演示了如何实现这两种遍历方法,并附有测试用例及结果,帮助读者理解和实践图的遍历算法。
17 0
|
2月前
|
机器学习/深度学习 存储 算法
数据结构实验之二叉树实验基础
本实验旨在掌握二叉树的基本特性和遍历算法,包括先序、中序、后序的递归与非递归遍历方法。通过编程实践,加深对二叉树结构的理解,学习如何计算二叉树的深度、叶子节点数等属性。实验内容涉及创建二叉树、实现各种遍历算法及求解特定节点数量。
108 4
|
2月前
|
存储 搜索推荐 算法
【数据结构】树型结构详解 + 堆的实现(c语言)(附源码)
本文介绍了树和二叉树的基本概念及结构,重点讲解了堆这一重要的数据结构。堆是一种特殊的完全二叉树,常用于实现优先队列和高效的排序算法(如堆排序)。文章详细描述了堆的性质、存储方式及其实现方法,包括插入、删除和取堆顶数据等操作的具体实现。通过这些内容,读者可以全面了解堆的原理和应用。
117 16
|
2月前
|
C语言
【数据结构】二叉树(c语言)(附源码)
本文介绍了如何使用链式结构实现二叉树的基本功能,包括前序、中序、后序和层序遍历,统计节点个数和树的高度,查找节点,判断是否为完全二叉树,以及销毁二叉树。通过手动创建一棵二叉树,详细讲解了每个功能的实现方法和代码示例,帮助读者深入理解递归和数据结构的应用。
161 8
|
3月前
|
存储 算法 关系型数据库
数据结构与算法学习二一:多路查找树、二叉树与B树、2-3树、B+树、B*树。(本章为了解基本知识即可,不做代码学习)
这篇文章主要介绍了多路查找树的基本概念,包括二叉树的局限性、多叉树的优化、B树及其变体(如2-3树、B+树、B*树)的特点和应用,旨在帮助读者理解这些数据结构在文件系统和数据库系统中的重要性和效率。
37 0
数据结构与算法学习二一:多路查找树、二叉树与B树、2-3树、B+树、B*树。(本章为了解基本知识即可,不做代码学习)
|
3月前
|
存储 算法
探索数据结构:分支的世界之二叉树与堆
探索数据结构:分支的世界之二叉树与堆