二叉树链式结构的实现

1.创建二叉树 

(1)创建结构体

typedef int BTDataType;typedef struct BinaryTreeNode
{BTDataType _data;struct BinaryTreeNode* _left;struct BinaryTreeNode* _right;
}BTNode;

(2)根据前序遍历构建树

 //通过前序遍历的数组"123##45##6##"构建二叉树
BTNode* BinaryTreeCreate(BTDataType* a, int n, int* pi)
{if (*pi >= n  || *pi == '#'){(*pi)++;return NULL;}BTNode* root = (BTNode*)malloc(sizeof(BTNode));root->_data = a[(*pi)++];root->_left = BinaryTreeCreate(a, n, pi);root->_right = BinaryTreeCreate(a, n, pi);return root;
}

2.前序、中序及后序遍历

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

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

1.前序遍历:访问根节点的操作在左右子树之前。

2.中序遍历:访问根节点的操作发生在遍历其左右子树之间。

3.后序编辑:访问根节点的操作发生在遍历其左右子树之后。

 由于被访问的节点必是某子树的根,所以N、L、R又可解释为根、根的左子树和根的右子树。NLR、LNR和LRN分别被称为先根遍历、中根遍历和后根遍历。

递归的本质

1. 当前问题

2. 子问题

3. 返回条件:最小规模的子问题

(1)二叉树前序遍历 

// 二叉树前序遍历 
void BinaryTreePrevOrder(BTNode* root)
{if (root == NULL)return;printf("%d ", root->_data);BinaryTreePrevOrder(root->_left);BinaryTreePrevOrder(root->_right);
}

图解: 

(2)中序遍历

// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root)
{if (root == NULL)return;BinaryTreePrevOrder(root->_left);printf("%d ", root->_data);BinaryTreePrevOrder(root->_right);
}

(3)后序遍历 

// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root)
{if (root == NULL)return;BinaryTreePrevOrder(root->_left);BinaryTreePrevOrder(root->_right);printf("%d ", root->_data);
}
前序遍历结果:1 2 3 4 5 6
中序遍历结果:3 2 1 5 4 6
后序遍历结果:3 2 5 6 4 1

3. 节点个数及高度等

(4)二叉树节点个数

// 二叉树节点个数
int BinaryTreeSize(BTNode* root)
{if (root == NULL)return 0;return BinaryTreeSize(root->_left) + BinaryTreeSize(root->_right) + 1;
}

(5)二叉树叶子节点个数

// 二叉树叶子节点个数
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);
}

(6)二叉树第K层节点个数

空节点:返回0

K == 1时,返回1

K > 1 时,转化成找左右子树的K-1层的节点个数

// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{if (root == NULL)return 0;if (k == 1)return 1;if (k > 1)return BinaryTreeLevelKSize(root->_left, k - 1) +BinaryTreeLevelKSize(root->_right, k - 1);
}

(7)二叉树查找值为x的节点

// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{if (root == NULL)return NULL;if (root->_data == x)return root;//要用left接收递归BTNode* left = BinaryTreeFind(root->_left, x);if (left)//如果在左树找到x节点,就直接返回,不用再进右子树中找return left;return BinaryTreeFind(root->_right, x);
}

4. 二叉树的销毁

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

5.层序遍历

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

思路: 

用到队列(先入先出),队列结构中的数据data指向的是二叉树结构体。

二叉树根节点不为空时,入队列,根节点出队列时带着左右孩子入队列。

注意:这里根节点出队列时,释放的是队列的节点,不影响树的节点

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
//结构体指针
typedef struct BinaryTreeNode* QDataType;
// 链式结构:表示队列
typedef struct QListNode
{struct QListNode* next;QDataType data;
}QNode;//队列的结构
typedef struct Queue
{QNode* phead;QNode* ptail;int size;
}Queue;// 初始化队列
void QueueInit(Queue* q);
// 队尾入队列
void QueuePush(Queue* q, QDataType data);
// 队头出队列
void QueuePop(Queue* q);
// 获取队列头部元素
QDataType QueueFront(Queue* q);
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
bool QueueEmpty(Queue* q);
// 销毁队列
void QueueDestroy(Queue* q);// 初始化队列
void QueueInit(Queue* q)
{assert(q);q->phead = NULL;q->ptail = NULL;q->size = 0;
}
// 队尾入队列
void QueuePush(Queue* q, QDataType data)
{assert(q);QNode* newnode = (QNode*)malloc(sizeof(QNode));if (newnode == NULL){perror("malloc fail");return;}newnode->next = NULL;newnode->data = data;if (q->ptail == NULL){q->phead = q->ptail = newnode;}else{q->ptail->next = newnode;q->ptail = newnode;}q->size++;
}
// 队头出队列
void QueuePop(Queue* q)
{assert(q);assert(q->size);if (q->phead->next == NULL)//只有最后一个节点{free(q->phead);q->phead = q->ptail = NULL;}else//多个节点{QNode* next = q->phead->next;free(q->phead);q->phead = next;}q->size--;
}
// 获取队列头部元素
QDataType QueueFront(Queue* q)
{assert(q);return q->phead->data;
}// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
bool QueueEmpty(Queue* q)
{assert(q);return q->size == 0;
}
// 销毁队列
void QueueDestroy(Queue* q)
{assert(q);QNode* cur = q->phead;while (cur){QNode* next = cur->next;free(cur);cur = next;}q->phead = q->ptail = NULL;q->size = 0;
}// 层序遍历
//用队列,上一层带下一层
void BinaryTreeLevelOrder(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 != NULL)QueuePush(&q, front->_left);if (front->_right != NULL)QueuePush(&q, front->_right);}QueueDestroy(&q);
}

6.判断二叉树是否为完全二叉树

完全二叉树是基于数组存储的,其存储特点是数组中最后一层数据是连续存放的(可以不存满),空节点和非空节点是连续的(如果后面全空就是完全二叉树,后面有非空就不是完全二叉树)。

思路:参考上面层序遍历的思想,用队列来实现 

根节点出队列时带着左右孩子进队列,当队列出现第一个空节点就开始判断,当后序再出现非空节点就说明不是完全二叉树,后面全空就是完全二叉树。

注意:

不可能出现遇到空节点,但后面还有非空节点没进队列的情况。

当空节点后面有非空,那么该非空是前面非空节点的孩子。

当层序出到空时,前面非空都出完了,那他的孩子一定进队列了。 

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
//结构体指针
typedef struct BinaryTreeNode* QDataType;
// 链式结构:表示队列
typedef struct QListNode
{struct QListNode* next;QDataType data;
}QNode;//队列的结构
typedef struct Queue
{QNode* phead;QNode* ptail;int size;
}Queue;// 初始化队列
void QueueInit(Queue* q);
// 队尾入队列
void QueuePush(Queue* q, QDataType data);
// 队头出队列
void QueuePop(Queue* q);
// 获取队列头部元素
QDataType QueueFront(Queue* q);
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
bool QueueEmpty(Queue* q);
// 销毁队列
void QueueDestroy(Queue* q);// 初始化队列
void QueueInit(Queue* q)
{assert(q);q->phead = NULL;q->ptail = NULL;q->size = 0;
}
// 队尾入队列
void QueuePush(Queue* q, QDataType data)
{assert(q);QNode* newnode = (QNode*)malloc(sizeof(QNode));if (newnode == NULL){perror("malloc fail");return;}newnode->next = NULL;newnode->data = data;if (q->ptail == NULL){q->phead = q->ptail = newnode;}else{q->ptail->next = newnode;q->ptail = newnode;}q->size++;
}
// 队头出队列
void QueuePop(Queue* q)
{assert(q);assert(q->size);if (q->phead->next == NULL)//只有最后一个节点{free(q->phead);q->phead = q->ptail = NULL;}else//多个节点{QNode* next = q->phead->next;free(q->phead);q->phead = next;}q->size--;
}
// 获取队列头部元素
QDataType QueueFront(Queue* q)
{assert(q);return q->phead->data;
}// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
bool QueueEmpty(Queue* q)
{assert(q);return q->size == 0;
}
// 销毁队列
void QueueDestroy(Queue* q)
{assert(q);QNode* cur = q->phead;while (cur){QNode* next = cur->next;free(cur);cur = next;}q->phead = q->ptail = NULL;q->size = 0;
}// 判断二叉树是否是完全二叉树
//数组中最后一层数据是连续存放的(可以不满)
//空和非空分别是连续的
//出上一层带下一层进去,当遇到第一个空节点开始判断
//如果后面全空就是完全二叉树,但如果有非空就不是完全二叉树//不可能出现,遇到空时,后面还有非空没进队列
//后面非空,一定是前面非空项的孩子
//当层序出到空时,前面非空都出完了,那他的孩子一定进队列了
bool BinaryTreeComplete(BTNode* root)
{Queue q;QueueInit(&q);if (root != NULL)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){//再遇到第一个空后,出现了非空,说明不是完全二叉树BinaryTreeDestory(front);return false;}QueuePush(&q, front->_left);QueuePush(&q, front->_right);}return true;
}

7.练习题

已知二叉树的前序遍历和中序遍历,画出其图:

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

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

相关文章

docker以挂载目录启动容器报错问题的解决

拉取镜像&#xff1a; docker pull elasticsearch:7.4.2 docker pull kibana:7.4.2 创建实例&#xff1a; mkdir -p /mydata/elasticsearch/configmkdir -p /mydata/elasticsearch/dataecho "http.host: 0.0.0.0" >> /mydata/elasticsearch/config/elasti…

vs2019 c++20 规范 STL库中关于时间的模板 ratio<T,U> , duration<T,U> , time_point<T,U>等

(探讨一)在学习线程的时候&#xff0c;一些函数会让线程等待或睡眠一段时间。函数形参是时间单位&#xff0c;那么在 c 中是如何记录和表示时间的呢&#xff1f;以下给出模板简图&#xff1a; &#xff08;2 探讨二&#xff09;接着给出对模板类 duration_values 的成员函数的测…

【Leetcode 705 】设计哈希集合——数组嵌套链表(限制哈希Key)

题目 不使用任何内建的哈希表库设计一个哈希集合&#xff08;HashSet&#xff09;。 实现 MyHashSet 类&#xff1a; void add(key) 向哈希集合中插入值 key 。bool contains(key) 返回哈希集合中是否存在这个值 key 。void remove(key) 将给定值 key 从哈希集合中删除。如果…

免费企业域名备案手把手教程

走的阿里云的备案服务&#xff0c;全程免费 前提 主办者&#xff1a;你的企业主办者负责人&#xff1a;当前登录的阿里云账户的人&#xff0c;不是企业法人的话&#xff0c;得准备委托书&#xff0c;会有地方提供模板&#xff0c;打印一下&#xff0c;签字扫描上传就行域名的…

【强化学习】DPO(Direct Preference Optimization)算法学习笔记

【强化学习】DPO&#xff08;Direct Preference Optimization&#xff09;算法学习笔记 RLHF与DPO的关系KL散度Bradley-Terry模型DPO算法流程参考文献 RLHF与DPO的关系 DPO&#xff08;Direct Preference Optimization&#xff09;和RLHF&#xff08;Reinforcement Learning f…

Kaggle线上零售 CRM分析(RFM+BG-NBD+生存分析+PySpark)

数据集地址&#xff1a;数据集地址 我的NoteBook地址&#xff1a;NoteBook地址 这个此在线零售数据集包含2009年12月1日至2011年12月9日期间的在线零售的所有交易。该公司主要销售独特的各种场合礼品。这家公司的许多客户都是批发商。本文将通过pyspark对数据进行导入与预处理&…

思迈特受邀参加工信部等权威机构行业盛会,探讨AI领域前沿技术

近日&#xff0c;思迈特软件作为国产BI领域知名厂商&#xff0c;多次受邀出席行业盛会&#xff0c;与众多业内专家学者、行业精英及知名企业代表等汇聚一堂共襄盛会&#xff0c;探讨行业前沿热点研究及最新趋势&#xff0c;分享企业数字化建设创新成果与成功实践&#xff0c;共…

Spring高手之路19——Spring AOP注解指南

文章目录 1. 背景2. 基于AspectJ注解来实现AOP3. XML实现和注解实现AOP的代码对比4. AOP通知讲解5. AOP时序图 1. 背景 在现代软件开发中&#xff0c;面向切面编程&#xff08;AOP&#xff09;是一种强大的编程范式&#xff0c;允许开发者跨越应用程序的多个部分定义横切关注点…

深度解析:重庆耶非凡科技有限公司的人力rpo项目

在当今竞争激烈的商业环境中&#xff0c;人力资源项目的成功与否往往决定了一个企业的长远发展。重庆耶非凡科技有限公司作为行业内的佼佼者&#xff0c;其人力资源项目备受瞩目。本文将深入探讨该公司的人力资源项目&#xff0c;特别是其独特的选品师项目和人力RPO服务。 首先…

2024年06月在线IDE流行度最新排名

点击查看最新在线IDE流行度最新排名&#xff08;每月更新&#xff09; 2024年06月在线IDE流行度最新排名 TOP 在线IDE排名是通过分析在线ide名称在谷歌上被搜索的频率而创建的 在线IDE被搜索的次数越多&#xff0c;人们就会认为它越受欢迎。原始数据来自谷歌Trends 如果您相…

数字光强测量仪OPT3001

外观 参考价格 原理图 频谱 特性 说明 OPT3001 传感器用于测量可见光的密度。传感器的光 谱响应与人眼的视觉响应紧密匹配&#xff0c;其中具有很高的红 外线阻隔。 OPT3001 是一款可如人眼般测量光强的单芯片照度 计。OPT3001 器件兼具精密的频谱响应和较强的 IR 阻隔功能&a…

基于Weaviate构建多模态检索和多模态检索增强(RAG): Building Multimodal Search and RAG

Building Multimodal Search and RAG 本文是学习 https://www.deeplearning.ai/short-courses/building-multimodal-search-and-rag/ 这门课的学习笔记。 What you’ll learn in this course Learn how to build multimodal search and RAG systems. RAG systems enhance an …

在iPhone上恢复已删除的Safari历史记录的最佳方法

您是否正在寻找恢复 iPhone 上已删除的 Safari 历史记录的最佳方法&#xff1f;好吧&#xff0c;这篇文章提供了 4 种在有/无备份的情况下恢复 iPhone 上已删除的 Safari 历史记录的最佳方法。现在按照分步指南进行操作。 iPhone 上的 Safari 历史记录会被永久删除吗&#xff1…

kafka 发送文件二进制流及使用header发送附属信息

文章目录 背景案例发送方接收方 背景 需要使用kafka发送文件二进制以及附属信息 案例 发送方 import org.apache.kafka.clients.producer.KafkaProducer; import org.apache.kafka.clients.producer.ProducerRecord;import java.io.InputStream; import java.nio.charset.S…

Halo DB 魔法之 pg_pcpu_limit

↑ 关注「少安事务所」公众号&#xff0c;欢迎⭐收藏&#xff0c;不错过精彩内容~ 前情回顾 前面已经介绍了“光环”数据库的基本情况和安装办法&#xff0c;今天来介绍一个新话题。 哈喽&#xff0c;国产数据库&#xff01;Halo DB! 三步走&#xff0c;Halo DB 安装指引 ★ Ha…

Java Agent利器

一、JavaAgent技术 1.1 什么是JavaAgent JavaAgent是一种特殊的Java程序&#xff0c;是Instrumentation的客户端。它与普通Java程序通过main方法启动不同&#xff0c;JavaAgent并不是一个可以单独启动的程序&#xff0c;它必须依附在一个Java应用程序&#xff08;JVM&#xf…

java并发常见问题

1.死锁&#xff1a;当两个或多个线程无限期地等待对方释放锁时发生死锁。为了避免这种情况&#xff0c;你应该尽量减少锁定资源的时间&#xff0c;按顺序获取锁&#xff0c;并使用定时锁尝试。 2.竞态条件&#xff1a;当程序的行为依赖于线程的执行顺序或输入数据到达的顺序时…

Lagrange ZK Coprocessor:革新区块链领域的大数据应用

1. 引言 2024年5月11日&#xff0c;Lagrange Labs宣称获得由Founders Fund领投&#xff08;Archetype Ventures, 1kx, Maven11, Fenbushi Capital, Volt Capital, CMT Digital, Mantle Ecosystem Fund和其它天使投资人跟头&#xff09;的1320万美金种子轮融资&#xff0c;致力于…

springboot高校网上选课系统-计算机毕业设计源码85583

摘 要 本论文主要论述了如何使用JAVA语言开发一个高校网上选课系统&#xff0c;本系统将严格按照软件开发流程进行各个阶段的工作&#xff0c;采用B/S架构&#xff0c;面向对象编程思想进行项目开发。在引言中&#xff0c;作者将论述高校网上选课系统的当前背景以及系统开发的目…

typescript --object对象类型

ts中的object const obj new Object()Object 这里的Object是Object类型&#xff0c;而不是JavaScript内置的Object构造函数。 这里的Object是一种类型&#xff0c;而Object()构造函数表示一个值。 Object()构造函数的ts代码 interface ObjectConstructor{readonly prototyp…