二叉树链式结构的实现(二叉树的遍历以及各种常用功能函数的实现)

之前也是把堆部分的知识点梳理完毕(即二叉树链式顺序的实现):堆的应用:堆排序和TOP-K问题

那么讲完了二叉树链式结构的实现。今天就进入二叉树链式结构的实现:


文章目录

  • 1.准备工作
  • 2.二叉树的遍历
    • 2.1前序遍历
    • 2.2中序遍历
    • 2.3后序遍历
    • 2.4层序遍历
  • 3.节点个数以及高度等
    • 3.1二叉树节点个数
    • 3.2二叉树叶子节点(度为1的节点)个数
    • 3.3树的高度
    • 3.4二叉树第k层节点个数
    • 3.5二叉树查找值为x的节点
  • 4.二叉树的构建及销毁
    • 4.1通过前序遍历的数组构建二叉树
  • 4.2二叉树的销毁
  • 5.判断二叉树是否是完全二叉树


1.准备工作

我们先手动快速创建一棵简单的二叉树来先进入二叉树操作,等对二叉树递归和结构有了一定的熟练后我们反过头再来看二叉树真正的创建方式

这不是真正的创建方法,是自己手动构建

#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<math.h>typedef int BTDataType;
typedef struct BinaryTreeNode//节点
{BTDataType data;//数据struct BinaryTreeNode* left;//左孩子struct BinaryTreeNode* right;//右孩子
}TreeNode;TreeNode* BuyTreeNode(int x)//创建数据为x的节点
{TreeNode* node = (TreeNode*)malloc(sizeof(TreeNode));assert(node);node->data = x;node->left = NULL;node->right = NULL;return node;
}TreeNode* CreateTree()//形成树(手动连接)
{TreeNode* node1 = BuyTreeNode(1);TreeNode* node2 = BuyTreeNode(2);TreeNode* node3 = BuyTreeNode(3);TreeNode* node4 = BuyTreeNode(4);TreeNode* node5 = BuyTreeNode(5);TreeNode* node6 = BuyTreeNode(6);//创建六个节点node1->left = node2;node1->right = node4;node2->left = node3;node4->left = node5;node4->right = node6;//进行连接return node1;
}

构建的二叉树如图:

请添加图片描述

2.二叉树的遍历

递归实现的关键在于将问题分解为规模更小的子问题,然后通过函数的递归调用来解决子问题。在二叉树遍历中,递归的思想可以很自然地应用,因为遍历左子树和右子树本质上就是对规模更小的子树进行遍历

二叉树遍历(Traversal)是指按照一定规则,依次访问二叉树中的所有节点,每个节点只被访问一次

二叉树的遍历有:前序/中序/后序的递归结构遍历和层序遍历

  1. 前序遍历(Preorder Traversal):先访问根节点,再访问左子树,最后访问右子树(根>左>右)
  2. 中序遍历(Inorder Traversal):先访问左子树,再访问根节点,最后访问右子树(左>根>右)
  3. 后序遍历(Postorder Traversal):先访问左子树,再访问右子树,最后访问根节点(左>右>根)
  4. 层序遍历是一种按层级顺序逐层遍历树结构的方法。它从树的根节点开始,逐层向下遍历,按照从左到右的顺序访问每一层的节点

2.1前序遍历

先访问根节点,再访问左子树,最后访问右子树(根>左>右);每进入一个新节点(先访问自身)

按照此规则之前构建的二叉树访问结果:1 2 3 NULL NULL NULL 4 5 NULL NULL 6 NULL NULL

方便大家理解,我就把NULL也写上:

请添加图片描述

void PreTraversal(TreeNode* root) 
{if (root == NULL) {printf("NULL ");//正常是不需要的,这里是为了让大家看到NULL(看的全过程)return;}// 访问根节点printf("%d ", root->data);// 遍历左子树PreTraversal(root->left);// 遍历右子树PreTraversal(root->right);
}int main()
{TreeNode* root = CreateTree();//创建好树PreTraversal(root);return 0;
}

结果:
请添加图片描述

2.2中序遍历

先访问左子树,再访问根节点,最后访问右子树(左>根>右);每进入一个新节点(先访问左子树)

按照此规则之前构建的二叉树访问结果:NULL 3 NULL 2 NULL 1 NULL 5 NULL 4 NULL 6 NULL

请添加图片描述

void InTraversal(TreeNode* root)
{if (root == NULL){printf("NULL ");//正常是不需要的,这里是为了让大家看到NULL(看的全过程)return;}// 遍历左子树InTraversal(root->left);// 访问根节点printf("%d ", root->data);// 遍历右子树InTraversal(root->right);
}int main()
{TreeNode* root = CreateTree();//创建好树InTraversal(root);return 0;
}

结果:

请添加图片描述

2.3后序遍历

先访问左子树,再访问右子树,最后访问根节点(左>右>根);每进入一个新节点(先访问右子树)

按照此规则之前构建的二叉树访问结果:NULL NULL 3 NULL 2 NULL NULL 5 NULL NULL6 4 1

请添加图片描述

void PostTraversal(TreeNode* root)
{if (root == NULL){printf("NULL ");//正常是不需要的,这里是为了让大家看到NULL(看的全过程)return;}// 遍历左子树PostTraversal(root->left);// 遍历右子树PostTraversal(root->right);// 访问根节点printf("%d ", root->data);
}int main()
{TreeNode* root = CreateTree();//创建好树PostTraversal(root);return 0;
}

结果:

请添加图片描述

2.4层序遍历

它从树的根节点开始,逐层向下遍历,按照从左到右的顺序访问每一层的节点

在 C 语言中,一般使用队列来实现层序遍历:

  1. 从根节点开始,将根节点入队列

  2. 循环执行以下步骤,直到队列为空:

    • 弹出队列的首节点,并访问该节点
    • 将该节点的左右子节点(如果存在)依次入队列;即弹出节点后把子节点入队列

    队列为空时,循环结束

void LevelOrder(TreeNode* root)
{Queue q;//创建队列,队列节点里的data储存root的地址QInit(&q);//初始化QPush(&q, root);//根进去了int LevelSize = 1;//要一层一层输出,来辅助while (!QEmpty(&q)){while (LevelSize--)//LevelSize是几就循环几次,一开始是1,输出一个就换行了,再Push后再次更新,来输出下一行{TreeNode* first = QFront(&q);//先存下来QPop(&q);//再pop掉第一个printf("%c ", first->data);if (first->left)//有左节点就push进来{QPush(&q, first->left);}if (first->right)//有右节点就push进来{QPush(&q, first->right);}}printf("\n");LevelSize = QSize(&q);}QDestroy(&q);
}

请添加图片描述


3.节点个数以及高度等

3.1二叉树节点个数

同样是递归

  1. 计算二叉树节点个数的问题就是计算左子树节点个数和计算右子树节点个数两个子问题
  2. 对于当前节点,我们需要计算以当前节点为根的子树的节点个数。这个节点本身占据一个节点(后面加一),所以我们将问题拆分为计算左子树节点个数和计算右子树节点个数两个子问题,然后将左右子树节点个数相加,并加上当前根节点,即 TreeSize(root->left) + TreeSize(root->right) + 1
  3. 对于左子树和右子树,我们同样可以按照上述步骤继续拆分分析,直到遇到空节点为止(递归终止条件)
int TreeSize(TreeNode* root)
{if (root == NULL){return 0;}return TreeSize(root->left) + TreeSize(root->right) + 1;
}int main()
{TreeNode* root = CreateTree();//创建好树printf("%d\n", TreeSize(root));return 0;
}

结果:

请添加图片描述

代码也可以这样写:

int TreeSize(TreeNode* root)
{return root == NULL ? 0 : TreeSize(root->left) + TreeSize(root->right) + 1;//利用三目运算符
}

3.2二叉树叶子节点(度为1的节点)个数

  1. 判断当前节点是否为空。如果为空,子树的叶子节点个数为 0,直接返回 0
  2. 节点不为空,我们需要判断当前节点是否为叶子节点。如果当前节点的左右子树均为空,即 root->left == NULL && root->right == NULL,则表示当前节点为叶子节点,返回 1
  3. 不是叶子节点,则需要计算以当前节点为根的子树的叶子节点个数。这个节点本身不是叶子节点,所以我们将问题拆分为计算左子树叶子节点个数和计算右子树叶子节点个数两个子问题,然后将左右子树叶子节点个数相加,即 TreeLeafSize(root->left) + TreeLeafSize(root->right)
  4. 对于左子树和右子树,我们同样可以按照上述步骤继续拆分,直到遇到空节点或叶子节点为止
int TreeLeafSize(TreeNode* root)
{if (root == NULL)//是NULL就返回0{return 0;}if (root->left == NULL && root->right == NULL)//是叶子就返回1{return 1;}return TreeLeafSize(root->left) + TreeLeafSize(root->right);//不是叶子就返回左子树叶子与右子树叶子之和
}int main()
{TreeNode* root = CreateTree();//创建好树//printf("%d\n", TreeSize(root));printf("%d\n", TreeLeafSize(root));return 0;
}

请添加图片描述

3.3树的高度

  1. 如果树为空,返回高度为 0
  2. 如果树不为空,则树的高度等于左子树的高度和右子树的高度中的较大值加 1,即 fmax(TreeHeight(root->left), TreeHeight(root->right)) + 1
  3. 对于左子树和右子树,我们同样可以按照上述步骤继续递归计算
int TreeHeight(TreeNode* root)
{if (root == NULL)return 0;return fmax(TreeHeight(root->left), TreeHeight(root->right)) + 1;
}int main()
{TreeNode* root = CreateTree();//创建好树//printf("%d\n", TreeSize(root));//printf("%d\n", TreeLeafSize(root));printf("%d\n", TreeHeight(root));return 0;
}

请添加图片描述

3.4二叉树第k层节点个数

  1. 如果树为空,返回节点数为 0
  2. 如果 k 等于 1,表示计算树的根节点,返回 1
  3. 如果 k 大于 1,则第 k 层节点数等于左子树的第 k-1 层节点数加上右子树的第 k-1 层节点数,即 TreeLevelNodes(root->left, k-1) + TreeLevelNodes(root->right, k-1)
  4. 对于左子树和右子树,我们同样可以按照上述步骤继续递归计算
int TreeLevelKSize(TreeNode* root, int k)
{if (root == NULL)return 0;if (k == 1)return 1;return TreeLevelKSize(root->left, k - 1) + TreeLevelKSize(root->right, k - 1);
}int main()
{TreeNode* root = CreateTree();//创建好树printf("%d\n", TreeLevelKSize(root,2));//求个第二层的return 0;
}

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

  1. 如果为空,那肯定找不到,直接返回NULL(同时也作为递归终止的情况之一)
  2. 先找跟节点,找到了即root->data == x,就返回该节点地址(同时也作为递归终止的情况之一)
  3. 找不到,就递归左节点,再右节点
  4. 都没有return的机会后,最后return NULL

递归停止的两种情况:要么找到了返回地址, 要么没找到返回NULL

  • 当不是NULL时,说明找到了,就一路返回上去,得到结果
  • 是NULL,就说明没找到,就去另外一边继续
TreeNode* TreeFind(TreeNode* root, BTDataType x)
{if (root == NULL){return NULL;}// 先看根节点if (root->data == x){return root;}// 看左子树TreeNode* ret1= TreeFind(root->left, x);//递归停止的两种情况:要么找到了返回地址// 要么没找到返回NULL//当不是NULL时,说明找到了,就一路返回上去,得到结果if (ret1 != NULL){return ret1;}// 看右子树TreeNode* ret2 = TreeFind(root->right, x);//如上if (ret2 != NULL){return ret2;}return NULL;
}int main()
{TreeNode* root = CreateTree();//创建好树//printf("%d\n", TreeSize(root));//printf("%d\n", TreeLeafSize(root));//printf("%d\n", TreeHeight(root));//printf("%d\n", TreeLevelKSize(root,2));//求个第二层的printf("%p\n", TreeFind(root, 5));return 0;
}

请添加图片描述

返回地址,说明找到了


4.二叉树的构建及销毁

4.1通过前序遍历的数组构建二叉树

通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树

先根据这个画出来(#为NULL就不画了):

请添加图片描述

TreeNode* CreateTreeTrue(char* arr, int* pi)
{if (arr[*pi] == '#'){(*pi)++;//调到下一个字符return NULL;}TreeNode* root = (TreeNode*)malloc(sizeof(TreeNode));assert(root);root->data = arr[*pi];//根创建且赋值好了(*pi)++;root->left=CreateTreeTrue(arr, pi);//创建左树root->right=CreateTreeTrue(arr, pi);//创建右树return root;
}

严格按照> 左子树> 右子树的顺序,每一个左子树又是作为一个根,有自己的左子树及右子树。所以以创建根的方法来创建左子树和右子树,直到遇到#来返回NULL进行终止

4.2二叉树的销毁

销毁我们都使用后序销毁

void TreeDestory(TreeNode** root)
{if (*root == NULL){return;}TreeDestory((*root)->left);TreeDestory((*root)->right);free(*root);*root = NULL;
}

因为最后要把root置空,传一级指针不行(没用,形参是临时拷贝),想要改变root本身就传入root的地址(即二级指针


5.判断二叉树是否是完全二叉树

也是利用队列,pop出一个后把他的左右节点push进去,遇到NULL就出循环,再看此时队列里是否全是NULL,如果全是NULL那就是完全二叉树,只要还有节点,那就不是完全二叉树

int BinaryTreeComplete(TreeNode* root)
{Queue q;QInit(&q);QPush(&q, root);//根进去了int LevelSize = 1;while (!QEmpty(&q)){TreeNode* first = QFront(&q);QPop(&q);if (first == NULL)break;//通过这个出循环QPush(&q, first->left);QPush(&q, first->right);}// 已经遇到空节点,如果此时队列中后面的节点还有非空,就不是完全二叉树while (!QEmpty(&q)){TreeNode* first = QFront(&q);QPop(&q);if (first != NULL){QDestroy(&q);return 0;}}QDestroy(&q);return 1;
}

终于啊,二叉树中较为基本的地方都已经梳理完毕了,相信在不久的将来,还会有二叉树的内容,大家敬请期待吧!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/595533.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

User Friendly大会 | 每日互动刘宇分享AIGC时代的数智营销变革

近日&#xff0c;第十九届暨2023年User Friendly国际用户体验大会在深圳召开。本次大会以“开智启能&#xff0c;体验无界”为主题&#xff0c;邀请了各行业领袖精英齐聚&#xff0c;分享前沿新观点&#xff0c;碰撞体验新思潮。每日互动高级副总裁刘宇作为业内资深专家&#x…

二维码地址门牌管理系统:物业管理的未来趋势

文章目录 前言一、数字化管理与便捷服务二、身份认证与安全保障三、业主便利与贴心服务四、未来发展趋势 前言 在数字化时代&#xff0c;物业管理面临着不断增加的挑战。为了提高管理效率、服务业主&#xff0c;二维码门牌管理系统应运而生。本文将探讨这一新型管理方式&#…

navicat密码解密

https://github.com/HyperSine/how-does-navicat-encrypt-password 获取加密的密码 reg query HKEY_CURRENT_USER\SOFTWARE\PremiumSoft\Navicat\Servers /s /v host reg query HKEY_CURRENT_USER\SOFTWARE\PremiumSoft\Navicat\Servers /s /v pwd或者通过导出连接之后查看文…

Unable to connect to Redis server

报错内容&#xff1a; Exception in thread "main" org.redisson.client.RedisConnectionException: java.util.concurrent.ExecutionException: org.redisson.client.RedisConnectionException: Unable to connect to Redis server: 175.24.186.230/175.24.186.230…

【实用安装教程】在win系统下制作Mac OS镜像启动U盘

第一步&#xff1a;制作Mac OS系统引导镜像启动U盘 准备一个8G&#xff08;或以上&#xff09;的U盘插入到win系统的电脑上 去下载TransMac(Mac启动盘制作工具)v10.4按照说明安装好 插入准备好的U盘&#xff0c;U盘数据要转移&#xff0c;打开TransMac&#xff0c;右键U盘选…

ssm基于echarts的基金交易网站的设计与实现论文

摘 要 计算机网络发展到现在已经好几十年了&#xff0c;在理论上面已经有了很丰富的基础&#xff0c;并且在现实生活中也到处都在使用&#xff0c;可以说&#xff0c;经过几十年的发展&#xff0c;互联网技术已经把地域信息的隔阂给消除了&#xff0c;让整个世界都可以即时通话…

基于Spring Boot+Vue的学生信息管理系统【附源码】

介绍 1)登录模块&#xff1a;用户通过输入可以输入编号、邮箱、手机号和密码来登录系统&#xff0c;如果输入错误会返回登录界面&#xff0c;并提示错误信息&#xff0c;成功登录后用户的登录信息会存储在浏览器中&#xff0c;系统会根据这些信息判断该用户的操作权限。 2)系统…

Windows反调试技术学习

Windows反调试 前言元旦快乐&#xff01;&#xff01;&#xff01;通过 API 调用IsDebuggerPresentCheckRemoteDebuggerPresent&#xff08;NtQueryInformationProcess&#xff09;OutputDebugStringZwSetInformationThread&#xff08;ThreadHideFromDebugger&#xff09; 手动…

【占用网络】VoxFormer 基于视觉的3D语义场景方案 CVPR 2023

前言 本文分享“占用网络”方案中&#xff0c;来自CVPR2023的VoxFormer&#xff0c;它基于视觉实现3D语义场景补全。 使用Deformable Attention从图像数据中&#xff0c;预测三维空间中的体素占用情况和类别信息。 VoxFromer是一个两阶段的框架&#xff1a; 第一个阶段&…

理解UML中的依赖关系

理解UML中的依赖关系 在面向对象的设计中&#xff0c;理解各种类之间的关系对于构建一个清晰、可维护的系统至关重要。UML&#xff08;统一建模语言&#xff09;为我们提供了一种可视化这些关系的方式。今天&#xff0c;我们将深入探讨UML中的依赖关系&#xff08;Dependency&a…

苹果Vision Pro将于1月27日上市!

在无数期待中&#xff0c;苹果全新产品Vision Pro头显终于定下上市日期。 彭博社记者马克古曼&#xff08;Mark Gurman&#xff09;于近日在X&#xff08;前推特&#xff09;平台爆料了这一信息&#xff0c;预计苹果Vision Pro头显将于2024年1月27日率先在美国上市。 在过去看…

如何计算非线性负载的功率需求?

非线性负载的功率需求计算是一个相对复杂的过程&#xff0c;因为非线性负载的电流和电压之间的关系不是简单的正比关系。在计算非线性负载的功率需求时&#xff0c;需要考虑负载的特性、工作状态以及电源电压等因素。 确定负载的类型&#xff1a;首先需要了解负载的具体类型&am…

RocketMQ单机部署完整学习笔记

文章目录 前言一、RocketMQ是什么&#xff1f;二、使用步骤1.安装MQ1.安装JDK2.安装mq3.MQ配置(核心) 2.搭建可视化dashboard1.下载源码2.修改配置3.启动 3.整合java1.生产者2.消费者3.启动生产者4.启动消费者5.dashboard添加消费组 三、总结全部的配置 前言 本文是基于4.X版本…

静态网页设计——电影角(HTML+CSS+JavaScript)

前言 声明&#xff1a;该文章只是做技术分享&#xff0c;若侵权请联系我删除。&#xff01;&#xff01; 使用技术&#xff1a;HTMLCSSJS 主要内容&#xff1a;本网页主要利用HTML语言编写&#xff0c;简要介绍世界上一些主要国家&#xff0c;例如&#xff0c;中&#xff0c;…

【已解决】You have an error in your SQL syntax

报错讯息 java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ‘desc,target_url,sort,status,create_by,modify_by,created,last_update_time FROM…

跑通CLAM

项目场景&#xff1a; 从github上下载CLAM代码&#xff0c;上传Camelyon-16中的部分WSI图像&#xff0c;将代码跑通。 CLAM项目地址&#xff1a; GitHub - mahmoodlab/CLAM: Data-efficient and weakly supervised computational pathology on whole slide images - Nature …

高级RAG(四):RAGAs评估

之前我完成了父文档检索器和llamaIndex从小到大的检索这两篇博客&#xff0c;我在这两篇博客中分别介绍了使用langchain和llamaIndex进行文档检索的方法和步骤&#xff0c;其中包含了不同的RAG的检索策略&#xff0c;通常来说一个典型的RAG系统一般包含两个主要的部件&#xff…

程序媛的mac修炼手册--MacOS系统更新升级史

啊&#xff0c;我这个口罩三年从未感染过新冠的天选免疫王&#xff0c;却被支原体击倒&#x1f637;大意了&#xff0c;前几天去医院体检&#xff0c;刚检查完出医院就摘口罩了&#x1f926;大伙儿还是要注意戴口罩&#xff0c;保重身体啊&#xff01;身体欠恙&#xff0c;就闲…

‘react-native‘ 不是内部或外部命令,也不是可运行的程序或批处理文件。

原因&#xff1a;没有下载react-native 解决下载react-native npm i -g react-native-cli

向日葵远程工具的使用Mysql5.7的安装与配置

目录 一、向日葵远程安装与使用 二、Mysql 5.7 安装与配置 2.1 安装 2.2 Navicat Premium 12 测试连接 本机测试连接 外部访问MySQL测试连接 三、思维导图 一、向日葵远程安装与使用 简介&#xff1a; 向日葵远程控制是一款用于对远程PC进行管理和服务的软件,拥有5秒快速…