当前位置: 首页 > news >正文

快速通关二叉树秘籍(下)

在前面我们讲了底层是数组的二叉树,今天讲以链表为底层的链式二叉树。

1. 链式二叉树基本结构

typedef int BTDataType;
typedef struct BinaryTreeNode
{int data;struct TreeNode* left;struct TreeNode* right;
}BTNode;

注意:由于链式二叉树的限制太少了,一般不进行数据的插入,因为在任意位置都能插入/删除数据,限制太少,意义小。 

2. 手动创建链式二叉树

BTNode* BuyNode(int data)
{BTNode* node = (BTNode*)malloc(sizeof(BTNode));if (node == NULL){perror("malloc fail!");exit(1);}node->data = data;node->left = node->right = NULL;return node;
}void test()
{BTNode BT;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 = node3;node2->left = node4;node3->left = node5;node3->right = node6;BTNode* root = node1;
}

3. 前中后序遍历

二叉树的数据查找离不开二叉树的遍历,二叉树的遍历方式有三种。

3.1 前序遍历

什么是前序遍历,前序遍历就是从根结点开始遍历,再遍历根结点的左子树,最后遍历根结点的右子树,即遍历顺序为根左右。

  • 二叉树是由根结点,左子树和右子树构成,从根结点开始遍历,遍历到左子树,而左子树又是一棵二叉树,同理不断地递归,直到没有子树。
  • 遍历二叉树的本质是递归。
  • 这里前中后序遍历:主要是根结点的遍历的顺序。

void PreOrder(BTNode* root)
{//判断递归结束的条件 -- 遇到空结点返回if (root == NULL){printf("NULL ");return;}//前序遍历 -- 根左右printf("%c ", root->data);PreOrder(root->left);PreOrder(root->right);
}

3.2 中序遍历 

中序遍历:即先遍历左子树,再遍历根结点,最后遍历右子树。

//中序遍历
void InOrder(BTNode* root)
{if(root== NULL){printf("NULL ");return;}//左子树->根结点->右子树InOrder(root->left);printf("%c ", root->data);InOrder(root->right);
}

3.3 后序遍历 

后序遍历:即先遍历左子树,再遍历右子树,最后遍历根结点。

//后序遍历
void PostOrder(BTNode* root)
{if (root == NULL){printf("NULL ");return;}//左子树->右子树->根结点PostOrder(root->left);PostOrder(root->right);printf("%c ", root->data);
}

4. 结点个数以及高度

4.1 结点个数

已知:二叉树是由根结点,左子树和右子树组成。

那是否意味:我只要知道左子树的结点个数和右子树的结点个数?

而左子树又相当于一棵子树,又是由根结点,左子树和右子树构成!

这时你会发现一个规律:根结点+左子树结点个数+右子树结点个数=总结点个数

有了规律我们就可以使用递归来解决问题!

// ⼆叉树结点个数
int BinaryTreeSize(BTNode* root)
{if(root==NULL){return 0;}return 1 + BinaryTreeSize(root->left) + BinaryTreeSize(root->right);
}

有同学提出了另一种想法:我只要遍历的结点不为空结点,我就利用变量count++,最后返回count的值不就行了?

这个想法好像也对欸,我们试一试!

 改成传地址

传地址最好是调用一次,如果多次调用,count会累加!

4.2 二叉树叶子节点个数

回顾上一节的叶子结点:即没有左右孩子(左右孩子=NULL)

那跟上面的思考是一样的:左子树叶子结点个数+右子树结点个数=总叶子结点个数。

// ⼆叉树叶⼦结点个数
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);
}

4.3 二叉树第K层结点个数

既然要找第K层结点的个数,那么在传参的时候,就得知道K的值,那怎么找是不是第K层呢?

从第一层开始找,只要不是K=1,k--;只要K=1,就返回。

同理跟上面的思路一样:找左子树第K层结点个数+右子树第K层结点个数

不断地往下递归,直到K=1,返回1。

//⼆叉树第k层结点个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{if (root == NULL){return 0;}if (k == 1){return 1;}return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1);
}

4.4 二叉树的深度/高度 

我们知道:二叉树的根结点为1层,再去遍历左右子树的层数

这里如果左右子树层数不一样,我们要得到他最大层数,所以遍历左右子树层数之后还需要比较。

即返回1+max(左,右子树层数)

//⼆叉树的深度/⾼度
int BinaryTreeDepth(BTNode* root)
{if (root == NULL){return 0;}//左子树层数int left = BinaryTreeDepth(root->left);//右子树层数int right = BinaryTreeDepth(root->right);return 1 + (left > right ? left : right);
}

4.5 二叉树查找值为x的结点

遍历二叉树,在左子树查找值为x的结点,再到右子树查找值为x的结点,如果找到了,直接返回!

// ⼆叉树查找值为x的结点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{if (root == NULL){return NULL;}if(root->data==x){return root;}int left = BinaryTreeFind(root->left, x);if (left){return left;}int right = BinaryTreeFind(root->right, x);if (right){return right;}//到这里说明没有找到return NULL;
}

4.6 二叉树销毁

这里销毁我们要从最后一个结点开始销毁,不能先销毁根结点

如果先销毁根结点,就无法找到后面的子树 !!!这里就需要遍历二叉树销毁,就需要利用到前面的遍历方式,最好的就是后续遍历,最后销毁根结点。

//⼆叉树销毁
void BinaryTreeDestory(BTNode** root)//root发生改变
{if(*root==NULL){return;}BinaryTreeDestory(&((*root)->left));BinaryTreeDestory(&((*root)->right));free(*root);*root = NULL;
}

5. 层序遍历 

层序遍历是指首先访问第一层的树根结点,然后从左到右访问第2 层上的结点,接着是第三层的结点,以此类推,自上而下,自左至右逐层访问树的结点的过程。

层序遍历需要借助数据结构 -- 队列。

看定义我有点看不懂,来看图。

//层序遍历
void LevelOrder(BTNode* root)
{Queue q;QueueInit(&q);//将根结点入队列QueuePush(&q, root);while (!QueueEmpty(&q)){//取队头元素BTNode* top = QueueFront(&q);QueuePop(&q);printf("%c ", top->data);//入左右孩子if (top->left){QueuePush(&q, top->left);}if (top->right){QueuePush(&q, top->right);}}QueueDestory(&q);
}

链式二叉树到这里就结束了!下次分享一些关于二叉树的OJ题目!敬请期待~

http://www.lryc.cn/news/589476.html

相关文章:

  • Rocky Linux 9 源码包安装php8
  • ChatTongyi × LangChain:开启多模态AI应用创新之门
  • 共射级放大电路的频率响应Multisim电路仿真——硬件工程师笔记
  • 50天50个小项目 (Vue3 + Tailwindcss V4) ✨ | DoubleClickHeart(双击爱心)
  • [设计模式]C++单例模式的几种写法以及通用模板
  • Kubernetes 架构原理与集群环境部署
  • 降本增效!自动化UI测试平台TestComplete并行测试亮点
  • 2025最新国产用例管理工具评测:Gitee Test、禅道、蓝凌测试、TestOps 哪家更懂研发协同?
  • ESLint 除了在packages.json还能在哪里配置?
  • 实测两款效率工具:驾考刷题和证件照处理的免费方案
  • CF37E Trial for Chief 题解
  • 【LeetCode 热题 100】226. 翻转二叉树——DFS
  • Python 数据建模与分析项目实战预备 Day 6 - 多模型对比与交叉验证验证策略
  • Zookeeper入门安装与使用详解
  • CAS单点登录架构详解
  • 关于实习的经验贴
  • 鸿蒙和Android知识点
  • 软件测试面试经历分享?
  • iOS App 上架工具选型与跨平台开发 iOS 上架流程优化实录
  • 文心一言4.5企业级部署实战:多模态能力与Docker容器化测评
  • x86版的ubuntu上使用qemu运行arm版ubuntu
  • PHP连接MySQL数据库的多种方法及专业级错误处理指南
  • Postgresql源码(147)Nestloop流程与Instrumentation简单分析
  • python实现自动化sql布尔盲注(二分查找)
  • 03 51单片机之独立按键控制LED状态
  • 论文 视黄素与细胞修复
  • 小型客厅如何装修设计?
  • 微信小程序开发-桌面端和移动端UI表现不一致问题记录
  • [ROS 系列学习教程] ROS动作通讯(Action):通信模型、Hello World与拓展
  • Linux操作系统之信号:保存与处理信号