【初阶数据结构】8.二叉树(3)

文章目录

  • 4.实现链式结构二叉树
    • 4.1 前中后序遍历
      • 4.1.1 遍历规则
      • 4.1.2 代码实现
    • 4.2 结点个数以及高度等
    • 4.3 层序遍历
    • 4.4 判断是否为完全二叉树
    • 4.5层序遍历和判断是否为完全二叉树完整代码


4.实现链式结构二叉树

用链表来表示一棵二叉树,即用链来指示元素的逻辑关系。 通常的方法是链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别用来给出该结点左孩子和右孩子所在的链结点的存储地址 ,其结构如下:

typedef int BTDataType;
// 二叉链
typedef struct BinaryTreeNode
{struct BinTreeNode* left; // 指向当前结点左孩子 struct BinTreeNode* right; // 指向当前结点右孩子 BTDataType val; // 当前结点值域 
}BTNode;

二叉树的创建方式比较复杂,为了更好的步入到二叉树内容中,我们先手动创建一棵链式二叉树:

BTNode* BuyBTNode(int val)
{BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));if (newnode == NULL){perror("malloc fail");return NULL;}newnode->val = val;newnode->left = NULL;newnode->right = NULL;return newnode;
}
BTNode* CreateTree()
{BTNode* n1 = BuyBTNode(1);BTNode* n2 = BuyBTNode(2);BTNode* n3 = BuyBTNode(3);BTNode* n4 = BuyBTNode(4);BTNode* n5 = BuyBTNode(5);BTNode* n6 = BuyBTNode(6);BTNode* n7 = BuyBTNode(7);n1->left = n2;n1->right = n4;n2->left = n3;n4->left = n5;n4->right = n6;n5->left = n7;return n1;
}

回顾二叉树的概念,二叉树分为空树和非空二叉树,非空二叉树由根结点、根结点的左子树、根结点的右子树组成的

img

根结点的左子树和右子树分别又是由子树结点、子树结点的左子树、子树结点的右子树组成的,因此二叉树定义是递归式的,后序链式二叉树的操作中基本都是按照该概念实现的。


4.1 前中后序遍历

二叉树的操作离不开树的遍历,我们先来看看二叉树的遍历有哪些方式

img


4.1.1 遍历规则

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

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

    访问顺序为:根结点、左子树、右子树

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

    访问顺序为:左子树、根结点、右子树

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

    访问顺序为:左子树、右子树、根结点


4.1.2 代码实现

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

以前序遍历为例:

img


4.2 结点个数以及高度等

Tree.h

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>//定义二叉树的链式结构
//二叉树结点的结构
//Binary 二进制的意思
typedef int BTDataType;typedef struct BinaryTreeNode {BTDataType data;struct BinaryTreeNode* left;struct BinaryTreeNode* right;
}BTNode;//前序遍历
void PreOrder(BTNode* root);//中序遍历--左根右
void InOrder(BTNode* root);//后序遍历 ---左右根
void PostOrder(BTNode* root);// ⼆叉树结点个数
int BinaryTreeSize(BTNode* root);// ⼆叉树叶⼦结点个数
int BinaryTreeLeafSize(BTNode* root);// ⼆叉树第k层结点个数
int BinaryTreeLevelKSize(BTNode* root, int k);//⼆叉树的深度/⾼度
int BinaryTreeDepth(BTNode* root);// ⼆叉树查找值为x的结点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x);// ⼆叉树销毁
void BinaryTreeDestory(BTNode** root);

Tree.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "Tree.h"//递归
//前序遍历
void PreOrder(BTNode* root)
{if (root == NULL)//走到为空就不遍历了{return;}printf("%d ", root->data);//打印根节点PreOrder(root->left);//左PreOrder(root->right);//右
}//中序遍历--左根右
void InOrder(BTNode* root)
{if (root == NULL){return;}InOrder(root->left);//左printf("%d ", root->data);//中InOrder(root->right);//右
}//后序遍历 ---左右根
void PostOrder(BTNode* root)
{if (root == NULL){//printf("NULL ");return;}PostOrder(root->left);//左PostOrder(root->right);//右printf("%d ", root->data);//中
}/*
// ⼆叉树结点个数
//这个会出现错误,因为size是全局变量,如果调用两次,会导致size的值变成两倍
int size = 0;
int BinaryTreeSize(BTNode* root)
{if (root == NULL){return 0;}++size;BinaryTreeSize(root->left);BinaryTreeSize(root->right);return size;
}// ⼆叉树结点个数
//错误的写法
//这个会出现错误,因为size是全局变量,如果调用两次,会导致size的值变成两倍
void BinaryTreeSize(BTNode* root,int* psize)
{if (root == NULL){return 0;}++(*psize);BinaryTreeSize(root->left,psize);BinaryTreeSize(root->right,psize);
}
*/// ⼆叉树结点个数
int BinaryTreeSize(BTNode* root)
{if (root == NULL){return 0;}return 1 + BinaryTreeSize(root->left) + BinaryTreeSize(root->right);
}// ⼆叉树叶⼦结点个数
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);
}// ⼆叉树第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);
}//⼆叉树的深度/⾼度
int BinaryTreeDepth(BTNode* root)
{if (root == NULL){return 0;}int leftDep = BinaryTreeDepth(root->left);int rightDep = BinaryTreeDepth(root->right);return leftDep > rightDep ? leftDep + 1 : rightDep + 1;
}// ⼆叉树查找值为x的结点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{if (root == NULL){return NULL;}if (root->data == x){return root;}BTNode* leftFind = BinaryTreeFind(root->left, x);if (leftFind){return leftFind;}BTNode* rightFind = BinaryTreeFind(root->right, x);if (rightFind){return rightFind;}return NULL;
}// ⼆叉树销毁
void BinaryTreeDestory(BTNode** root)
{if (*root == NULL){return;}BinaryTreeDestory(& ((*root) ->left)  );BinaryTreeDestory(& ((*root) ->right) );free(*root);*root = NULL;
}

test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "Tree.h"//创建节点
BTNode* buyNode(BTDataType x) {BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));if (newnode == NULL) {perror("malloc fail!");exit(1);}newnode->data = x;newnode->left = newnode->right = NULL;return newnode;
}void test01() {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;//node2->right = node5;//node3->left = node6;/*PreOrder(node1);printf("\n");InOrder(node1);printf("\n");PostOrder(node1);*/printf("size:%d\n", BinaryTreeSize(node1));// ⼆叉树结点个数printf("leaf size: %d\n", BinaryTreeLeafSize(node1));// ⼆叉树叶⼦结点个数printf("第K层 size:%d\n", BinaryTreeLevelKSize(node1, 2));// ⼆叉树第k层结点个数printf("depth/height:%d\n", BinaryTreeDepth(node1));//二叉树深度BTNode* find = BinaryTreeFind(node1, 33);printf("%s\n", find == NULL ? "未找到!" : "找到了!");// ⼆叉树查找值为x的结点BinaryTreeDestory(&node1);// ⼆叉树销毁
}int main() {test01();return 0;
}

4.3 层序遍历

除了先序遍历、中序遍历、后序遍历外,还可以对二叉树进行层序遍历

设二叉树的根结点所在层数为1,层序遍历就是从所在二叉树的根结点出发,首先访问第一层的树根结点,然后从左到右访问第2层上的结点,接着是第三层的结点,以此类推,自上而下,自左至右逐层访问树的结点的过程就是层序遍历.

实现层序遍历需要额外借助数据结构:队列

img

// 层序遍历
void LevelOrder(BTNode* root)
{Queue q;QueueInit(&q);QueuePush(&q, root);while (!QueueEmpty(&q)){BTNode* top = QueueFront(&q);printf("%c ", top->data);QueuePop(&q);if (top->_left) {QueuePush(&q, top->_left);}if (top->_right) {QueuePush(&q, top->_right);}}QueueDesTroy(&q);
}

4.4 判断是否为完全二叉树

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

4.5层序遍历和判断是否为完全二叉树完整代码

tree.h

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <stdbool.h>//定义二叉树的链式结构
//二叉树结点的结构
//Binary 二进制的意思
typedef int BTDataType;typedef struct BinaryTreeNode {BTDataType data;struct BinaryTreeNode* left;struct BinaryTreeNode* right;
}BTNode;// ⼆叉树销毁
void BinaryTreeDestory(BTNode** root);//定义队列结构
//typedef int QDataType;
//typedef struct BinaryTreeNode* QDataType;
typedef struct BTNode* QDataType;typedef struct QueueNode {QDataType data;struct QueueNode* next;
}QueueNode;typedef struct Queue {QueueNode* phead;//队头:删QueueNode* ptail;//队尾:插int size;//保存队列有效数据个数
}Queue;//初始化队列
void QueueInit(Queue* pq);// 入队列,队尾
void QueuePush(Queue* pq, QDataType x);//队列判空
bool QueueEmpty(Queue* pq);// 出队列,队头
void QueuePop(Queue* pq);//取队头数据
QDataType QueueFront(Queue* pq);//销毁队列
void QueueDestroy(Queue* pq);//层序遍历
//借助数据结构---队列
void LevelOrder(BTNode* root);

tree.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "LevelOrder.h"//初始化队列
void QueueInit(Queue* pq) {assert(pq);pq->phead = pq->ptail = NULL;pq->size = 0;
}// 入队列,队尾
void QueuePush(Queue* pq, QDataType x) {assert(pq);//申请新结点QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));if (newnode == NULL) {perror("malloc fail!");exit(1);}newnode->data = x;newnode->next = NULL;if (pq->phead == NULL) {//判断队列是否为空pq->phead = pq->ptail = newnode;}else {//队列不为空pq->ptail->next = newnode;pq->ptail = newnode;}pq->size++;//入一次,size++ 一次
}//队列判空
bool QueueEmpty(Queue* pq) {assert(pq);return (pq->phead == NULL) && (pq->ptail == NULL);
}// 出队列,队头
void QueuePop(Queue* pq) {assert(pq);assert(!QueueEmpty(pq));//只有一个结点的情况,避免ptail变成野指针if (pq->ptail == pq->phead) {free(pq->phead);pq->phead = pq->ptail = NULL;}else {//删除队头元素QueueNode* next = pq->phead->next;free(pq->phead);pq->phead = next;}--pq->size;//出一次,size-- 一次
}//取队头数据
QDataType QueueFront(Queue* pq) {assert(pq);assert(!QueueEmpty(pq));//判断队列不为空return pq->phead->data;
}//销毁队列
void QueueDestroy(Queue* pq) {assert(pq);/*注意下面这行在这里要注释掉,不然会报错*///assert(!QueueEmpty(pq));//判断队列不为空,空队列不需要销毁QueueNode* pcur = pq->phead;while (pcur) {QueueNode* Next = pcur->next;free(pcur);pcur = Next;}pq->phead = pq->ptail = NULL;pq->size = 0;
}// ⼆叉树销毁
void BinaryTreeDestory(BTNode * *root)
{if (*root == NULL){return;}BinaryTreeDestory(&((*root)->left));BinaryTreeDestory(&((*root)->right));free(*root);*root = NULL;
}//层序遍历
//借助数据结构---队列
void LevelOrder(BTNode * root)
{Queue q;QueueInit(&q);QueuePush(&q, root);while (!QueueEmpty(&q)){//取队头,打印BTNode* front = QueueFront(&q);printf("%d ", front->data);QueuePop(&q);//队头节点的左右孩子入队列if (front->left)QueuePush(&q, front->left);if (front->right)QueuePush(&q, front->right);}//队列为空QueueDestroy(&q);
}//判断二叉树是否为完全二叉树
//---队列
bool BinaryTreeComplete(BTNode * root)
{Queue q;QueueInit(&q);//初始化QueuePush(&q, root);//入队列while (!QueueEmpty(&q))//不为空就循环取队头{BTNode* front = QueueFront(&q);//取队头QueuePop(&q);//让队头出队,确保下一次出来的是新的队头if (front == NULL){break;}QueuePush(&q, front->left);//把队头左孩子入队列QueuePush(&q, front->right);//把队头右孩子入队列}//队列不一定为空while (!QueueEmpty(&q)){BTNode* front = QueueFront(&q);//取队头QueuePop(&q);//出队列if (front != NULL)//如果不为空,说明不是完全二叉树{QueueDestroy(&q);//销毁return false;}}QueueDestroy(&q);//销毁return true;
}

test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "LevelOrder.h"//创建节点
BTNode* buyNode(BTDataType x) {BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));if (newnode == NULL) {perror("malloc fail!");exit(1);}newnode->data = x;newnode->left = newnode->right = NULL;return newnode;
}void test01() {BTNode* node1 = buyNode(1);BTNode* node2 = buyNode(2);BTNode* node3 = buyNode(3);BTNode* node4 = buyNode(4);node1->left = node2;node1->right = node3;node2->left = node4;LevelOrder(node1);bool ret = BinaryTreeComplete(node1);printf("%s\n", ret == false ? "不是完全二叉树!" : "是完全二叉树!");BinaryTreeDestory(&node1);// ⼆叉树销毁
}int main() {test01();return 0;
}

打印:

img

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

减轻幻觉新SOTA,7B模型自迭代训练效果超越GPT-4,上海AI lab发布

LLMs在回答各种复杂问题时&#xff0c;有时会“胡言乱语”&#xff0c;产生所谓的幻觉。解决这一问题的初始步骤就是创建高质量幻觉数据集训练模型以帮助检测、缓解幻觉。 但现有的幻觉标注数据集&#xff0c;因为领域窄、数量少&#xff0c;加上制作成本高、标注人员水平不一…

php反序列化--前置知识

&#x1f3bc;个人主页&#xff1a;金灰 &#x1f60e;作者简介:一名简单的大一学生;易编橙终身成长社群的嘉宾.✨ 专注网络空间安全服务,期待与您的交流分享~ 感谢您的点赞、关注、评论、收藏、是对我最大的认可和支持&#xff01;❤️ &#x1f34a;易编橙终身成长社群&#…

文件共享功能无法使用提示错误代码0x80004005【笔记】

环境情况&#xff1a; 其他电脑可以正常访问共享端&#xff0c;但有一台电脑访问提示错误代码0x80004005。 处理检查&#xff1a; 搜索里输入“启用或关闭Windows功能”按回车键&#xff0c;在“启用或关闭Windows功能”里将“SMB 1.0/CIFS文件共享支持”勾选后&#xff08;故…

不同行情下算法的具体使用!

上一篇我们说到了不同公司算法交易的区分&#xff0c;有朋友提出了不同的行情下的算法交易应该怎么使用&#xff0c;小编今天就带大家了解下&#xff01;当然具体实际状况百出&#xff0c;这种可以实际为准&#xff08;韭菜修养全拼实际探讨交流&#xff09;&#xff01; 我们在…

qt做的分页控件

介绍 qt做的分页控件 如何使用 创建 Pagination必须基于一个QWidget创建&#xff0c;否则会引发错误。 Pagination* pa new Pagination(QWidget*);设置总页数 Pagination需要设置一个总的页数&#xff0c;来初始化页码。 pa->SetTotalItem(count);设置可选的每页数量…

Java 每日一题: for 与 foreach 的区别 ?

for 循环&#xff1a;是最基本的循环结构&#xff0c;可以通过初始化语句、循环条件和迭代语句来控制循环的执行。 foreach 循环&#xff08;也称为增强型 for 循环&#xff09;&#xff1a;用于遍历集合或数组中的元素&#xff0c;简化了遍历过程&#xff0c;没有显式地控制索…

[STM32]HAL库实现自己的BootLoader-BootLoader与OTA-STM32CUBEMX

目录 一、前言 二、BootLoader 三、BootLoader的实现 四、APP程序 五、效果展示 六、拓展 一、前言 听到BootLoader大家一定很熟悉&#xff0c;在很多常见的系统中都会存在BootLoader。本文将介绍BootLoader的含义和简易实现&#xff0c;建议大家学习前掌握些原理基础。 …

全链路追踪 性能监控,GO 应用可观测全面升级

作者&#xff1a;古琦 01 介绍 随着 Kubernetes 和容器化技术的普及&#xff0c;Go 语言不仅在云原生基础组件领域广泛应用&#xff0c;也在各类业务场景中占据了重要地位。如今&#xff0c;越来越多的新兴业务选择 Golang 作为首选编程语言。得益于丰富的 RPC 框架&#xff…

编程类精品GPTs

文章目录 编程类精品GPTs前言种类ChatGPT - GrimoireProfessional-coder-auto-programming 总结 编程类精品GPTs 前言 代码类的AI, 主要看以下要点: 面对含糊不清的需求是否能引导出完整的需求面对完整的需求是否能分步编写代码完成需求编写的代码是否具有可读性和可扩展性 …

力扣算法题:矩阵(玄幻不变量法),链表(虚拟头节点,递归法)

20240725 一、矩阵54.螺旋矩阵(循环不变量) 二、链表1 移除链表元素1.1 原链表删除元素&#xff1a;1.2 虚拟头节点&#xff08;&#xff01;&#xff01;&#xff01;&#xff09; 2. 设计链表206. 反转链表(双向指针和递归)双指针递归 交换链表中的元素虚拟头节点法递归法 删…

如何解决 Nginx 与边缘计算节点的集成问题?

&#x1f345;关注博主&#x1f397;️ 带你畅游技术世界&#xff0c;不错过每一次成长机会&#xff01; 文章目录 如何解决 Nginx 与边缘计算节点的集成问题&#xff1f;一、理解集成的需求和目标二、解决网络配置问题三、优化 Nginx 配置四、处理安全与认证问题五、监控与调试…

STM32是使用的内部时钟还是外部时钟

STM32是使用的内部时钟还是外部时钟&#xff0c;经常会有人问这个问题。 1、先了解时钟树&#xff0c;见下图&#xff1a; 2、在MDK中&#xff0c;使用的是HSEPLL作为SYSCLK&#xff0c;因此需要对时钟配置寄存器&#xff08;RCC_CFGR&#xff09;进行配置&#xff0c;寄存器内…

Jacoco 单元测试配置

前言 编写单元测试是开发健壮程序的有效途径&#xff0c;单元测试写的好不好可以从多个指标考量&#xff0c;其中一个就是单元测试的覆盖率。单元测试覆盖率可以看到我们的单元测试覆盖了多少代码行、类、分支等。查看单元测试覆盖率可以使用一些工具帮助我们计算&#xff0c;…

在IDEA中切换分支没有反应

说明&#xff1a;记录一次在IDEA中切换分支没有反应的情况&#xff0c;新建一个分支后&#xff0c;准备暂存代码&#xff0c;切换到其他分支去&#xff0c;发现怎么切都没有反应&#xff0c;也没有切过去&#xff1b; 解决&#xff1a;首先&#xff0c;我想到是不是当前新分支…

如何解决 Nginx 与无服务器架构的集成问题?

&#x1f345;关注博主&#x1f397;️ 带你畅游技术世界&#xff0c;不错过每一次成长机会&#xff01; 文章目录 如何解决 Nginx 与无服务器架构的集成问题&#xff1f; 如何解决 Nginx 与无服务器架构的集成问题&#xff1f; 在当今的云计算时代&#xff0c;无服务器架构因…

机器学习驱动的智能化电池管理技术与应用

目录 主要内容 电池管理技术概述 电池的工作原理与关键性能指标 电池管理系统的核心功能 SOC估计 SOH估计 寿命预测 故障诊断 人工智能机器学习 基础 人工智能的发展 机器学习的关键概念 机器学习在电池管理中的应用案例介绍 人工智能在电池荷电状态估计中的…

AttributeError: ‘list‘ object has no attribute ‘text‘

AttributeError: ‘list‘ object has no attribute ‘text‘ 目录 AttributeError: ‘list‘ object has no attribute ‘text‘ 【常见模块错误】 【解决方案】 示例代码 欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 欢迎来到我的主页&#xff0c;我是博主英…

谷粒商城实战笔记-63-商品服务-API-品牌管理-OSS获取服务端签名

文章目录 一&#xff0c;创建第三方服务模块thrid-party1&#xff0c;创建一个名为gulimall-third-party的模块2&#xff0c;nacos上创建third-party命名空间&#xff0c;用来管理这个服务的所有配置3&#xff0c;配置pom文件4&#xff0c;配置文件5&#xff0c;单元测试6&…

模型剪枝中有哪些经验|mobile-yolov5-pruning-distillation项目中剪枝知识分析

项目地址&#xff1a;https://github.com/Syencil/mobile-yolov5-pruning-distillation 项目时间&#xff1a;2022年 mobile-yolov5-pruning-distillation是一个以yolov5改进为主的开源项目&#xff0c;主要包含3中改进方向&#xff1a;更改backbone、模型剪枝、知识蒸馏。这里…

路由表与IP数据报的转发

前言&#xff1a;本博客仅作记录学习使用&#xff0c;部分图片出自网络&#xff0c;如有侵犯您的权益&#xff0c;请联系删除 一、相关知识 1、路由类型 路由表中有3类路由&#xff1a;直连路由、静态路由、动态路由 直连路由&#xff1a;一般指去往路由器接口直接连接网络的…