链式二叉树及相关操作(前,中,后,层序遍历)

简介: 链式二叉树及相关操作(前,中,后,层序遍历)

5b23414bffe528876c0747faf9d497e8_d14a1b90ee2949a0a716694e2cced809.png

“春来无事,只为花忙。”


前言:

上一期给大家介绍了二叉树的一种顺序结构:堆,这一期承接上一期,给大家继续介绍二叉树的另一种结构:链式结构。


目录

🐽Part1:链式二叉树?

1.前情提要

2.创建一颗二叉树

🐷Part2:相关操作实现

1.遍历操作

1.1前序遍历

1.2中序遍历

1.3后序遍历

1.4层序遍历

2.其他操作

2.1二叉树结点个数

2.2二叉树高度

2.3第k层结点个数

2.4判断是否为完全二叉树

2.5查找为x的结点

2.6二叉树销毁  



Part1:链式二叉树?


1.前情提要


这里的链式二叉树其实就是利用每个结点的左孩子和右孩子关系来创建的,方便快速手搓一个简单的二叉树。


2.创建一颗二叉树


其实方法很简单:

确定结点的结构 --> 根据左右孩子关系链接

//结点的结构
typedef int BTDataType;
typedef struct BinaryTreeNode
{
  BTDataType data;
  struct BinaryTreeNode* left;
  struct BinaryTreeNode* right;
}BTNode;
//新结点的创建
BTNode* BuyNode(BTDataType x)
{
  BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));
  if (newnode == NULL)
  {
    perror("malloc fail");
    return NULL;
  }
  newnode->data = x;
  newnode->left = NULL;
  newnode->right = NULL;
  return newnode;
}
//链接
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);
  node1->left = node2;
  node1->right = node4;
  node2->left = node3;
  node4->left = node5;
  node4->right = node6;
  return node1;
}

到这里,这颗二叉树的样子就有了:

c114b20b56f830f50f79d4e1991ea30f_94fd2bd041c24b48836c0911d5d35442.png


Part2:相关操作实现


1.遍历操作


遍历可以清晰的明确二叉树的结构。

二叉树的遍历是按照某种特定的规则,依次对二叉树中的结点进行相应的操作,并且每个结点只操作一次。


按照规则,二叉树的遍历(按访问顺序)有:

前序遍历:根节点-->左节点-->右结点;

中序遍历:左节点-->根节点-->右结点;

后序遍历:左节点-->右结点-->根节点;

层序遍历:简单说,就是二叉树由上到下,由左到右遍历。


1.1前序遍历


由于每次遍历都是 根节点-->左节点-->右结点,所以非常适合用递归来实现遍历。

ddd9fbad7d620be2da5f92b7c7ac6c16_a0d7dddbfa494515bdb7d789e20b0602.png

前序遍历图解

面对二叉树遍历,既要看到大树,又能看到小树;

可以看出,首先由顶部根节点进入,再递归左边,遇到空结点返回,再递归右结点... ...依次反复

那么就可以上代码了:

//前序遍历
void PreOder(BTNode* root)
{
  if (root == NULL)
  {
    printf("NULL ");
    return;
  }
  printf("%d ", root->data);
  PreOder(root->left);
  PreOder(root->right);
}

为了更详细的理解,这里画一下代码的递归图解:

c4d43d06e297dbde0c07601d540388dc_ac601087a1d54a1c8134e5f40c541168.png

代码递归图解 

学会了前序遍历,剩余的中序遍历和后序遍历也就会了,它们之间只有访问结点的顺序不同。


1.2中序遍历


//中序遍历
void InOder(BTNode* root)
{
  if (root == NULL)
  {
    printf("NULL ");
    return;
  }
  InOder(root->left);
  printf("%d ", root->data);
  InOder(root->right);
}


1.3后序遍历


//后序遍历
void PostOder(BTNode* root)
{
  if (root == NULL)
  {
    printf("NULL ");
    return;
  }
  PostOder(root->left);
  PostOder(root->right);
  printf("%d ", root->data);
}


1.4层序遍历


层序遍历与前三种遍历不同;

这里直接上演示图吧:

image.gif

简单解释:

双亲结点弹出,两个孩子节点就入 队列

是的,这里用到了队列 先入先出 的特点。

上代码:

// 层序遍历
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);
}


2.其他操作


2.1二叉树结点个数


这里利用了分治思想,

即让每个根节点去统计孩子结点个数,再依次向上汇报;

还是利用递归:

// 二叉树节点个数  分治思想
int BinaryTreeSize(BTNode* root)
{
  return root == NULL ? 0 :
           BinaryTreeSize(root->left)
           + BinaryTreeSize(root->right)
           + 1; // +1是自己
}


2.2二叉树高度


二叉树的高度,是取左子树和右子树高度中最大者,

所以可以先利用递归分别保存左右子树的高度,再返回较大值:

//二叉树高度
int BinaryTreeHeight(BTNode* root)
{
  if (root == NULL)
    return 0;
  int Heightleft = BinaryTreeHeight(root->left) + 1;
  int Heightright = BinaryTreeHeight(root->right) + 1;
  return Heightleft >= Heightright ? Heightleft : Heightright;
}


2.3第k层结点个数


同样的,第k层结点个数包含左子树结点个数与右子树结点个数:

//第k层节点个数
int BinaryTreeKLevel(BTNode* root, int k)
{
  assert(k > 0);
  if (root == NULL)
    return 0;
  if (k == 1) // 顶部根节点的特殊情况
    return 1;
  return BinaryTreeKLevel(root->left, k - 1) 
    + BinaryTreeKLevel(root->right, k - 1);
}


2.4判断是否为完全二叉树


还记得完全二叉树吗?

它的特点是 空结点连续 ,我们就可以利用这一个特点来判断;

判断一层连不连续,正好用到前面的层序遍历思想,利用 队列 来实现:

// 判断二叉树是否是完全二叉树
bool BinaryTreeComplete(BTNode* root)
{
  Queue q;
  QueueInit(&q);
  if (root)
    QueuePush(&q, root);
  while (!QueueEmpty(&q))
  {
    BTNode* front = QueueFront(&q);
    QueuePop(&q);
    if (front == NULL)
    {
      break;
    }
    else
    {
      QueuePush(&q, front->left);
      QueuePush(&q, front->right);
    }
  }
  //判断
  while (!QueueEmpty(&q))
  {
    BTNode* front = QueueFront(&q);
    QueuePop(&q);
    if (front)
    {
      QueueDestroy(&q);
      return false;
    }
  }
  QueueDestroy(&q);
  return true;
}


2.5查找为x的结点


分左右结点来查找,若有多个满足条件的结点,返回先找到的结点指针:

//查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
  if (root == NULL)
    return NULL;
  if (root->data == x)
    return root;
  BTNode* lret = BinaryTreeFind(root->left, x);
  if (lret)
    return lret;
  BTNode* rret = BinaryTreeFind(root->right, x);
  if (rret)
    return rret;
  return NULL;
}


2.6二叉树销毁


这个销毁也是要逐个销毁的,也要用到递归,分左子树销毁和右子树销毁:

// 二叉树销毁
void BinaryTreeDestory(BTNode* root)
{
  if (root == NULL)
    return;
  BinaryTreeDestory(root->left);
  BinaryTreeDestory(root->right);
  free(root);
}

总结:

链式二叉树的实现~ 主要掌握四种遍历:前中后序遍历层序遍历,尽量搞懂是如何递归的,才会有更深刻的记忆~

目录
相关文章
|
前端开发 API
服务端渲染-nextjs如何发起请求
服务端渲染-nextjs如何发起请求
1036 0
|
XML 存储 数据格式
【30】yolov5的数据集准备 | 处理Pascal voc格式的数据集
【30】yolov5的数据集准备 | 处理Pascal voc格式的数据集
791 0
【30】yolov5的数据集准备 | 处理Pascal voc格式的数据集
|
8月前
|
数据采集 NoSQL 关系型数据库
Python爬虫去重策略:增量爬取与历史数据比对
Python爬虫去重策略:增量爬取与历史数据比对
|
消息中间件 Java Kafka
在Java中实现分布式事务的常用框架和方法
总之,选择合适的分布式事务框架和方法需要综合考虑业务需求、性能、复杂度等因素。不同的框架和方法都有其特点和适用场景,需要根据具体情况进行评估和选择。同时,随着技术的不断发展,分布式事务的解决方案也在不断更新和完善,以更好地满足业务的需求。你还可以进一步深入研究和了解这些框架和方法,以便在实际应用中更好地实现分布式事务管理。
1033 161
|
存储 消息中间件 人工智能
ApsaraMQ Serverless 能力再升级,事件驱动架构赋能 AI 应用
本文整理自2024年云栖大会阿里云智能集团高级技术专家金吉祥的演讲《ApsaraMQ Serverless 能力再升级,事件驱动架构赋能 AI 应用》。
485 96
|
前端开发 关系型数据库 MySQL
ThingsGateway:一款基于.NET8开源的跨平台高性能边缘采集网关
ThingsGateway:一款基于.NET8开源的跨平台高性能边缘采集网关
381 2
|
11月前
|
人工智能 算法 测试技术
AI 研发产品进化论:从 AI 编码助手到 AI 程序员
本次分享由阿里云资深技术专家陈鑫主讲,主题为“AI研发产品进化论:从AI编码助手到AI程序员”。内容涵盖通义灵码在落地过程中的挑战与突破,包括精准度提升、企业级检索增强、自定义扩展及智能体的应用。通过全工程理解、个性化适配和智能体的引入,通义灵码已实现代码补全、单元测试生成、缺陷修复等核心功能,并显著提升了开发者的工作效率。目前,通义灵码已在Vs Code和JetBrains插件市场上获得超过500万次下载,月均采纳率超过30%,并持续优化中。
334 9
|
存储 Go C语言
Python 的整数是怎么实现的?这篇文章告诉你答案
Python 的整数是怎么实现的?这篇文章告诉你答案
302 7
|
存储 关系型数据库 API
必看!淘宝商品详情数据接口调用,助力商城上货实战全流程(仅供参考)
本文介绍了一个实战案例,通过调用淘宝商品详情数据接口,实现商品信息的自动获取与上架至自建电商平台。主要步骤包括需求分析、技术选型、接口调用、数据存储、自动上货及定时更新,旨在提升工作效率,减少人工操作。
lxml.etree.XPathEvalError: Invalid expression
lxml.etree.XPathEvalError: Invalid expression
192 4