【数据结构】二叉树链式结构(一)

简介: 【数据结构】二叉树链式结构(一)

前言


 在之前的二叉树的顺序结构中我们发现,该二叉树对于堆(一种完全二叉树)非常实用,但是对于非完全二叉树就会出现以下的结构,造成空间浪费:


13cabbb51138c0399744af0e51de2a98_a6f85b7b919149058289a3e3c7741cbb.png


 所以这里我们还是要通过链式结构来实现二叉树。但是其实普通二叉树是没有什么意义的,增删查改没有多大的意义。真正有意义的是搜索二叉树:


926c3320a853e105912bd334bc2deb0f_7b551911732347a1bb1fbacab3ebb150.png


 搜索二叉树的特点是左子树比根大/小,右子树比根小/大。这里的二叉树可以用来搜索,也可以用来进行插入,删除等操作,拥有实际的意义。所以对于普通二叉树,我们不用学习他的增删查改,只用学习他的遍历等操作,并且复习一下递归的相关知识。


一.二叉树的理解:


我们先回顾一下回顾下二叉树的概念,二叉树是:


空树

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

 对于一颗二叉树,我们看到它就要把他分为:根,左子树,右子树。对于左子树,在把他分为:根,左子树,右子树。对于右子树,在把他分为:根,左子树,右子树……以此类推直到左右子树都为空才停止。



18c9a78baf59d9fe05b0e97afbe778d1_b0e22861f861484d9b0f35a1cfd025a9.png


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


二.二叉树的遍历:


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


 按照规则,二叉树的遍历有:前序/中序/后序的递归结构遍历。我们以这颗二叉树为例:


bae1e37c695d03053dfcbe4b0285d116_1244137c0473401f8ac8fe54faa250b0.png


创建二叉树:


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");
  return;
  }
  node->left = NULL;
  node->right = NULL;
  node->data = x;
  return node;
}
BTNode* CreatTree()
{
  BTNode* node1 = BuyNode(1);
  BTNode* node2 = BuyNode(2);
  BTNode* node3 = BuyNode(3);
  BTNode* node4 = BuyNode(4);
  BTNode* node5 = BuyNode(5);
  BTNode* node6 = BuyNode(6);
  BTNode* node7 = BuyNode(7);
  node1->left = node2;
  node1->right = node4;
  node2->left = node3;
  node4->left = node5;
  node4->right = node6;
  node3->right = node7;
  return node1;
}


bae1e37c695d03053dfcbe4b0285d116_1244137c0473401f8ac8fe54faa250b0.png


1.前序遍历:


 前序遍历的顺序是:根,左子树,右子树。



22c5090bee08855442830cacda5d6a72_1854663bc3c049a8a7439e929e0a9647.png


代码实现:


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


结果:


a24eea2afb29c6ed71a8bb84b945107a_b77b3ee0652c40869148c49992c044de.png


756635ee15db5224fb8a709feb05158f_c56fe4ad56e4480db1c8be038d06edae.jpeg


2.中序遍历:


 中序遍历的顺序是:左子树,根,右子树。


d670b839ce51b3173229051e0e1393a5_265b33b4aff142b1b63979ccb18fa75f.png


代码实现:


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


结果:


e685e2a0a52ebd4d97b828cfd889903e_cfc50d1e8b4e4ee89aa7310731effd77.png


3.后序遍历:


 后序遍历的顺序是:左子树,右子树,根。


7b41d877cc27802de36d2f7d83ddd7a7_0e8f661640f3437b847538ca3f342fd5.png


代码实现:


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


结果:


7be4e34f35ea3dd5250d779e42bede2b_eb05e7c256e74218bf3d70c752d1e9e1.png


 其实上面的三种遍历就是通过s三句代码的顺序导致结果的不同,当然他们的递归过程都能用下面这张图来代表:


762167a9edf9b651661d76f0ce19eee6_9e89ed30265b4287afcb1cd3590cad29.png


4.层序遍历:


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



 那么如何实现层序遍历呢?这里我们就要用到我们之前所学的队列了!

 在这里,我们将二叉树的根先进队列,然后将其出队列,每出一次,就让其左右子结点进入队列,随后在出一个队列,将其左右子节点加入队列……这样通过队列的push和pop就能实现层序遍历


be7f467e562eded3ffe479e2b17f5a0a_5a3f89d8e37145c0a7f203f7b7331213.png


我们首先将队列的代码导入即可,就可以创建队列了:


403333128a85e37ed96dcc20e9cfad83_7ff4e8aca3d3400db28b12c567bc3975.png


代码实现:


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);
  }
  }
  QueueDestroy(&q);
}


注意:

 这里我们放入队列的不是要打印的数据,而是树结点的指针,为什么呢?如果我们存入的是要打印的数据(整形数据),那么我们无法找到他们的子节点!所以我们每次pop出一个指针,然后push这个指针(结点)的子节点即可。图解如下:


2c42f2500013fed3f240429192187be1_ad2b710d76f6486d9656b6d94bdbb42e.png

目录
相关文章
|
22小时前
|
存储 算法 测试技术
【C++数据结构——树】二叉树的遍历算法(头歌教学实验平台习题) 【合集】
本任务旨在实现二叉树的遍历,包括先序、中序、后序和层次遍历。首先介绍了二叉树的基本概念与结构定义,并通过C++代码示例展示了如何定义二叉树节点及构建二叉树。接着详细讲解了四种遍历方法的递归实现逻辑,以及层次遍历中队列的应用。最后提供了测试用例和预期输出,确保代码正确性。通过这些内容,帮助读者理解并掌握二叉树遍历的核心思想与实现技巧。
14 2
|
14天前
|
数据库
数据结构中二叉树,哈希表,顺序表,链表的比较补充
二叉搜索树,哈希表,顺序表,链表的特点的比较
数据结构中二叉树,哈希表,顺序表,链表的比较补充
|
18小时前
|
C++
【C++数据结构——树】二叉树的性质(头歌实践教学平台习题)【合集】
本文档介绍了如何根据二叉树的括号表示串创建二叉树,并计算其结点个数、叶子结点个数、某结点的层次和二叉树的宽度。主要内容包括: 1. **定义二叉树节点结构体**:定义了包含节点值、左子节点指针和右子节点指针的结构体。 2. **实现构建二叉树的函数**:通过解析括号表示串,递归地构建二叉树的各个节点及其子树。 3. **使用示例**:展示了如何调用 `buildTree` 函数构建二叉树并进行简单验证。 4. **计算二叉树属性**: - 计算二叉树节点个数。 - 计算二叉树叶子节点个数。 - 计算某节点的层次。 - 计算二叉树的宽度。 最后,提供了测试说明及通关代
25 10
|
19小时前
|
Java C++
【C++数据结构——树】二叉树的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现二叉树的基本运算。​ 相关知识 创建二叉树 销毁二叉树 查找结点 求二叉树的高度 输出二叉树 //二叉树节点结构体定义 structTreeNode{ intval; TreeNode*left; TreeNode*right; TreeNode(intx):val(x),left(NULL),right(NULL){} }; 创建二叉树 //创建二叉树函数(简单示例,手动构建) TreeNode*create
25 12
|
2月前
|
机器学习/深度学习 存储 算法
数据结构实验之二叉树实验基础
本实验旨在掌握二叉树的基本特性和遍历算法,包括先序、中序、后序的递归与非递归遍历方法。通过编程实践,加深对二叉树结构的理解,学习如何计算二叉树的深度、叶子节点数等属性。实验内容涉及创建二叉树、实现各种遍历算法及求解特定节点数量。
105 4
|
2月前
|
存储 搜索推荐 算法
【数据结构】树型结构详解 + 堆的实现(c语言)(附源码)
本文介绍了树和二叉树的基本概念及结构,重点讲解了堆这一重要的数据结构。堆是一种特殊的完全二叉树,常用于实现优先队列和高效的排序算法(如堆排序)。文章详细描述了堆的性质、存储方式及其实现方法,包括插入、删除和取堆顶数据等操作的具体实现。通过这些内容,读者可以全面了解堆的原理和应用。
112 16
|
2月前
|
C语言
【数据结构】二叉树(c语言)(附源码)
本文介绍了如何使用链式结构实现二叉树的基本功能,包括前序、中序、后序和层序遍历,统计节点个数和树的高度,查找节点,判断是否为完全二叉树,以及销毁二叉树。通过手动创建一棵二叉树,详细讲解了每个功能的实现方法和代码示例,帮助读者深入理解递归和数据结构的应用。
151 8
|
3月前
|
存储 算法 关系型数据库
数据结构与算法学习二一:多路查找树、二叉树与B树、2-3树、B+树、B*树。(本章为了解基本知识即可,不做代码学习)
这篇文章主要介绍了多路查找树的基本概念,包括二叉树的局限性、多叉树的优化、B树及其变体(如2-3树、B+树、B*树)的特点和应用,旨在帮助读者理解这些数据结构在文件系统和数据库系统中的重要性和效率。
36 0
数据结构与算法学习二一:多路查找树、二叉树与B树、2-3树、B+树、B*树。(本章为了解基本知识即可,不做代码学习)
|
3月前
|
存储 算法 搜索推荐
数据结构与算法学习十七:顺序储存二叉树、线索化二叉树
这篇文章主要介绍了顺序存储二叉树和线索化二叉树的概念、特点、实现方式以及应用场景。
43 0
数据结构与算法学习十七:顺序储存二叉树、线索化二叉树
|
3月前
|
存储 算法
探索数据结构:分支的世界之二叉树与堆
探索数据结构:分支的世界之二叉树与堆
下一篇
开通oss服务