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

【数据结构】链式二叉树的实现和思路分析及二叉树OJ

【数据结构】链式二叉树的实现和思路分析及二叉树OJ

🔥个人主页大白的编程日记

🔥专栏数据结构


文章目录

  • 【数据结构】链式二叉树的实现和思路分析及二叉树OJ
    • 前言
    • 一.链式二叉树的定义及结构
    • 二.链式二叉树的遍历
      • 2.1前序遍历
      • 2.2中序遍历
      • 2.3后序遍历
      • 2.4层序遍历
      • 三.链式二叉树功能函数
      • 3.1节点个数
      • 3.2第k层节点的个数
      • 3.3查找值为x的节点
      • 3.4树的销毁
    • 四.二叉树OJ
      • 4.1二叉树遍历
      • 4.2左子叶之和
    • 后言

前言

哈喽,各位小伙伴大家好!上期讲的是用顺序表实现二叉树。今天咱们用链表的方式实现我们的二叉树。也就是链式结构。话不多说,咱们进入正题!向大厂冲锋!

一.链式二叉树的定义及结构

  • 树的定义
    我们链式二叉树用结构体定义。结构体内包含节点的数据。然后还有指向左右孩子节点的结构体指针
typedef int DataType;
typedef struct BinaryTreeNode
{DataType val;struct BinaryTreeNode* left;struct BinaryTreeNode* right;
}BTNode;
  • 节点的创建

节点的创建我们需要malloc一个结构体。检查节点是否开辟成功。然后将节点数据赋值为X即可。再将左右指针指向空。最后返回开辟好的节点。

BTNode* BuyNode(int x)//创建树的节点
{BTNode* node = (BTNode*)malloc(sizeof(BTNode));if (node == NULL){perror("malloc fail");exit(1);}node->a = x;node->left = node->right = NULL;return node;
}
  • 树的创建
    为了方便我们后面使用。我们先开辟一个树出来。
    我们只需要创建好节点。然后修改节点的指针使其成一棵树即可。
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;
}

这样一颗树二叉树就构建好了。

二.链式二叉树的遍历

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

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

  1. 前序遍历(Preorder Traversal 亦称先序遍历)——访问根结点的操作发生在遍历其左右子树之前。
  2. 中序遍历(Inorder Traversal)——访问根结点的操作发生在遍历其左右子树之中(间)。
  3. 后序遍历(Postorder Traversal)——访问根结点的操作发生在遍历其左右子树之后。

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

任何一颗树都要分成根 左子树 右子树去按顺序遍历。
左右子树的访问又看成一颗树继续按照顺序去遍历。

2.1前序遍历

前序遍历访问顺序就是 根 左子树 右子树。
然后子树继续按照 根 左子树 右子树访问。
那我们先把一棵树的按照根 左子树 右子树拆分

  • 代码实现
    我们前序遍历一颗树时分为两种情况
    一:树为空。那就不用访问了直接return结束。
    二:树不为空。那就先访问根节点(这里我们直接打印)。然后还需要继续左右子树前序遍历,那我们就递归函数解决。
void PrevOrder(BTNode* p)//前序遍历
{if (p == NULL){printf("N ");return;}printf("%d ", p->a);PrevOrder(p->left);PrevOrder(p->right);
}
  • 逻辑分析
    逻辑上我们就是将一颗树的前序遍历分为根的访问和左子树的遍历和右子树。
    左右子树的遍历又看成一棵树的前序遍历。所以我们递归左右子树即可。



  • 逻辑过程

我们都知道函数的调用需要在栈上开辟栈帧。
但是需要注意的是左子树开辟的栈帧函数调用结束销毁后
仍然存在内存中,调用右子树开辟的栈帧是重复利用左子树的栈帧。
所以函数的栈帧会重复利用。

2.2中序遍历

以此类推,中序就先递归左子树 再访问根 再递归右子树即可。

void InorOrder(BTNode* p)//中序遍历
{if (p == NULL){printf("N ");return;}InorOrder(p->left);printf("%d ", p->a);InorOrder(p->right);
}

2.3后序遍历

以此类推,中序就先访问根 递归左子树 再递归右子树即可。

void PostOrder(BTNode* p)//后序遍历
{if (p == NULL){printf("N ");return;}PostOrder(p->left);PostOrder(p->right);printf("%d ", p->a);
}
  • 验证

2.4层序遍历

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

  • 思路分析

上一层带下一层即可完成层序遍历。

  • 代码实现
void TreeLevelOrder(BTNode* p)//层序遍历
{Queue pq;QueueInit(&pq);//初始化队列if(p)QueuePush(&pq, p);//第一层入队列while (!QueueEmpty(&pq))//队列不为空{BTNode* head = QueueFron(&pq);//取出队头数据QueuePop(&pq);//出队列printf("%d ", head->a);//访问if (head->left){QueuePush(&pq, head->left);//左孩子入队列}if (head->right){QueuePush(&pq, head->right);//右孩子入队列}}QueueDestroy(&pq);//销毁队列
}

三.链式二叉树功能函数

我们链式二叉树的实现不只是实现遍历而已。
我们还需要对二叉树实现求节点个数,树的高度等等。

3.1节点个数

  • 遍历计数

我们很容易想到走一个遍历然后不为空用size++记录个数即可。
这确实是一种方法。那具体代码如何实现呢?

int TreeSize(BTNode* p)//树的节点个数
{int size;if (p == NULL){return 0;}size++;TreeSize(p->left);TreeSize(p->right);return  size;
}

这样写对吗?不对因为size是局部变量。每次函数调用size都会置为0.
这样就不能把节点个数累加起来。size之会累加第一次。

那我们是不是把他static改成静态让他的生命周期是全局的
这样每次size++都是同一个size就可以了呢?

int TreeSize(BTNode* p)//树的节点个数
{static int size=0;if (p == NULL){return 0;}size++;TreeSize(p->left);TreeSize(p->right);return  size;
}


我们发现结果确实是6。那我在调用一次呢?

我们发现第二次调用是12。为什么呢?因为局部静态变量只会初始化一次。
所以第二次调用6+6就是12.

int size;
int TreeSize(BTNode* p)//树的节点个数
{if (p == NULL){return 0;}size++;TreeSize(p->left);TreeSize(p->right);return  size;
}

那我们只能用全局的size然后每次函数调用前都要手动置0.

  • 分治递归
    我们可以用递归的思想把大问题拆分成小问题解决。
int TreeSize(BTNode* p)//树的节点个数
{if (p == NULL){return 0;}return  TreeSize(p->left)+TreeSize(p->right)+1;
}

3.2第k层节点的个数

现在我们要求树的第k层节点的个数。我们该怎么求呢?
还是用递归的思想。

把问题转化为下一层第k-1层的递归求解即可。

int TreeLevelKSize(BTNode* p, int k)//第k层的节点
{if (p == NULL){return 0;}if (k == 1){return 1;}return TreeLevelKSize(p->left, k - 1)+TreeLevelKSize(p->right, k - 1);
}

3.3查找值为x的节点

现在我们要查找值为x的节点。一棵树可能有多个节点值为x。我们就返回找到的第一个节点即可。

利用递归分治的思想。将一棵树x节点的查找分为根节点 左子树 右子树的查找即可。

  • 代码实现
BTNode* TreeFind(BTNode* p, int x)//查找值为k的节点
{if (p == NULL){return NULL;}if (p->a == x){return p;}BTNode* ret1 = TreeFind(p->left, x);BTNode* ret2 = TreeFind(p->right, x);return ret1 == NULL ? ret2 : ret1;
}

我们这样写对吗?
其实也算对。但是这样有小问题就是不管左子树存不存在x节点。都会去再查找右子树。这样就效率不太高。

BTNode* TreeFind(BTNode* p, int x)//查找值为k的节点
{if (p == NULL){return NULL;}if (p->a == x){return p;}BTNode* ret1 = TreeFind(p->left, x);if (ret1 != NULL){return ret1;}BTNode* ret2 = TreeFind(p->right, x);if (ret2 != NULL){return ret2;}return NULL;
}

所以我们最好对左子树的返回值检查一下。
如果不为空说明找到。直接return返回节点即可。

3.4树的销毁

那树如何销毁呢?

把树的销毁看成根的销毁 左右子树的销毁。
左右子树又是树的销毁 递归即可。

void TreeDestroy(BTNode* p)//树的销毁
{if (p == NULL){return ;}TreeDestroy(p->left);//销毁左子树TreeDestroy(p->right);//销毁右子树free(p);//销毁根节点
}
  • 判断完全二叉树
    现在我们要判断一棵树是否时完全二叉树如何判断呢?

    我们只需要走一个层序遍历。然后出队列时孩子节点入队列即可。
  • 代码实现
bool FullTree(BTNode* p)//判断是否满二叉树
{Queue pq;QueueInit(&pq);if (p)QueuePush(&pq, p);while (!QueueEmpty(&pq))//入队列{BTNode* head = QueueFron(&pq);QueuePop(&pq);//出队列if (head == NULL){break;}QueuePush(&pq, head->left);//左孩子入队列QueuePush(&pq, head->right);//右孩子入队列}while (!QueueEmpty(&pq)){BTNode* head = QueueFron(&pq);QueuePop(&pq);if (head)//找是否有非空节点{QueueDestroy(&pq);return false;}}QueueDestroy(&pq);return true;
}

四.二叉树OJ

4.1二叉树遍历

  • 题目
    二叉树遍历
  • 思路分析

这里我们还是用递归的方式。
根据前序遍历的思想构建树。然后走中序遍历即可。

  • 代码实现
#include <stdio.h>
typedef char DataType;
typedef struct BinaryTreeNode {DataType a;struct BinaryTreeNode* left;struct BinaryTreeNode* right;
} BTNode;
void InorOrder(BTNode* p)//中序遍历
{if (p == NULL){return;}InorOrder(p->left);printf("%c ", p->a);InorOrder(p->right);
}
BTNode* CreatTree(char* p, int* i) //构建树
{ //前序遍历if (p[*i] == '#')//不为空{(*i)++;return NULL;}BTNode* ret = (BTNode*)malloc(sizeof(BTNode));//创建一个节点if (ret == NULL){perror("malloc fali");exit(1);}ret->a = p[(*i)++];//赋值ret->left = CreatTree(p, i);//左节点ret->right = CreatTree(p, i);//右节点return ret;//返回节点}
int main() {char s[100];scanf("%s", &s);int i = 0;BTNode* ret = CreatTree(s, &i);//构建二叉树InorOrder(ret);//中序遍历return 0;
}

4.2左子叶之和

  • 题目
    左子叶之和

  • 思路分析
    在这里插入图片描述
    这里我们还是用递归的思想将树的左子叶之和分为
    左子树左子叶+右子树左子叶之和即可。

  • 代码实现

int sumOfLeftLeaves(struct TreeNode* root){if(root==NULL){return 0;}if(root->left&&(root->left->left==NULL&&root->left->right==NULL)){return root->right==NULL?root->left->val:sumOfLeftLeaves(root->right)+root->left->val;}return sumOfLeftLeaves(root->left)+sumOfLeftLeaves(root->right);
}

后言

这就是链式二叉树的实现以及二叉树OJ。这是数据结构中比较难也是重点内容。
大家一定要好好消化。今天就分享到这。感谢各位的耐心垂阅!咱们下期见!拜拜~

在这里插入图片描述

相关文章:

【数据结构】链式二叉树的实现和思路分析及二叉树OJ

【数据结构】链式二叉树的实现和思路分析及二叉树OJ &#x1f525;个人主页&#xff1a;大白的编程日记 &#x1f525;专栏&#xff1a;数据结构 文章目录 【数据结构】链式二叉树的实现和思路分析及二叉树OJ前言一.链式二叉树的定义及结构二.链式二叉树的遍历2.1前序遍历2.2中…...

项目成功秘诀:工单管理系统如何加速进程

国内外主流的10款项目工单管理系统对比&#xff1a;PingCode、Worktile、浪潮云工单管理系统、华为企业智能工单系统、金蝶云苍穹、紫光软件管理系统、Jira、Asana、ServiceNow、Smartsheet。 在管理日益复杂的个人项目时&#xff0c;找到一款能够真正符合需求的管理软件&#…...

OpenGauss和GaussDB有何不同

OpenGauss和GaussDB是两个不同的数据库产品&#xff0c;它们都具有高性能、高可靠性和高可扩展性等优点&#xff0c;但是它们之间也有一些区别和相似之处。了解它们之间的关系、区别、建议、适用场景和如何学习&#xff0c;对于提高技能和保持行业敏感性非常重要。本文将深入探…...

星环科技携手东华软件推出一表通报送联合解决方案

随着国家金融监督管理总局“一表通”试点工作的持续推进&#xff0c;星环科技携手东华软件推出了基于星环科技分布式分析型数据库ArgoDB和大数据基础平台TDH的一表通报送联合解决方案&#xff0c;并已在多地实施落地中得到充分验证。 星环科技与东华软件作为战略合作伙伴&…...

YOLOv10环境搭建、训练自己的目标检测数据集、实际验证和测试

1 环境搭建 1.1 在官方仓库的给定的使用python3.9版本&#xff0c;则使用conda创建对应虚拟环境。 conda create -n yolov10 python3.9 1.2 切换到对应虚拟环境 conda activate yolov10 1.3 在指定目录下克隆yolov10官方仓库代码 git clone https://github.com/THU-MIG/yo…...

Harmony Next -- 通用标题栏:高度自定义,可设置沉浸式状态,正常状态下为:左侧返回、居中标题,左中右均可自定义视图。

hm_common_title_bar OpenHarmony三方库中心仓&#xff1a;https://ohpm.openharmony.cn/#/cn/detail/common_title_bar 介绍 一款通用标题栏&#xff0c;支持高度自定义&#xff0c;可设置沉浸式状态&#xff0c;正常状态下为&#xff1a;左侧返回、居中标题&#xff0c;左…...

甄选范文“论数据分片技术及其应用”软考高级论文,系统架构设计师论文

论文真题 数据分片就是按照一定的规则,将数据集划分成相互独立、正交的数据子集,然后将数据子集分布到不同的节点上。通过设计合理的数据分片规则,可将系统中的数据分布在不同的物理数据库中,达到提升应用系统数据处理速度的目的。 请围绕“论数据分片技术及其应用”论题…...

【elementui】记录el-table设置左、右列固定时,加大滚动条宽度至使滚动条部分被固定列遮挡的解决方法

当前elementui版本&#xff1a;2.8.2 现象&#xff1a;此处el-table__body-wrapper默认的滚动条宽度为8px&#xff0c;我加大到10px&#xff0c;如果不设置fixed一切正常&#xff0c;设置fixed后会被遮挡一点 el-table__fixed-right::before, .el-table__fixed::before 设置…...

Python人工智能:一、语音合成和语音识别

在Python中&#xff0c;语音合成&#xff08;Text-To-Speech, TTS&#xff09;和语音识别&#xff08;Speech-To-Text, STT&#xff09;是两个非常重要的功能&#xff0c;它们在人工智能、自动化、辅助技术以及许多其他领域都有广泛的应用。下面将分别介绍这两个领域在Python中…...

C/C++进阶 (8)哈希表(STL)

个人主页&#xff1a;仍有未知等待探索-CSDN博客 专题分栏&#xff1a;C 本文着重于模拟实现哈希表&#xff0c;并非是哈希表的使用。 实现的哈希表的底层用的是线性探测法&#xff0c;并非是哈希桶。 目录 一、标准库中的哈希表 1、unordered_map 2、unordered_set 二、模…...

2024电赛H题参考方案(+视频演示+核心控制代码)——自动行驶小车

目录 一、题目要求 二、参考资源获取 三、TI板子可能用到的资源 1、环境搭建及工程移植 2、相关模块的移植 四、控制参考方案 1、整体控制方案视频演示 2、视频演示部分核心代码 五、总结 一、题目要求 小编自认为&#xff1a;此次控制类类型题目的H题&#xff0c;相较于往年较…...

设计模式14-享元模式

设计模式14-享元模式 由来动机定义与结构代码推导特点享元模式的应用总结优点缺点使用享元模式的注意事项 由来动机 在很多应用中&#xff0c;可能会创建大量相似对象&#xff0c;例如在文字处理器中每个字符对象。在这些场景下&#xff0c;如果每个对象都独立存在&#xff0c…...

Javascript中canvas与svg详解

Canvas 在JavaScript中&#xff0c;<canvas> 元素用于在网页上绘制图形&#xff0c;如线条、圆形、矩形、图像等。它是一个通过JavaScript和HTML的<canvas>元素来工作的绘图表面。<canvas> 元素自身并不具备绘图能力&#xff0c;它仅仅提供了一个绘图环境&a…...

【BUG】已解决:No Python at ‘C:Users…Python Python39python. exe’

No Python at ‘C:Users…Python Python39python. exe’ 目录 No Python at ‘C:Users…Python Python39python. exe’ 【常见模块错误】 【解决方案】 欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 欢迎来到我的主页&#xff0c;我是博主英杰&#xff0c;211科班…...

Flink SQL 的工作机制

前言 Flink SQL 引擎的工作流总结如图所示。 从图中可以看出&#xff0c;一段查询 SQL / 使用TableAPI 编写的程序&#xff08;以下简称 TableAPI 代码&#xff09;从输入到编译为可执行的 JobGraph 主要经历如下几个阶段&#xff1a; 将 SQL文本 / TableAPI 代码转化为逻辑执…...

[AI Mem0] 源码解读,带你了解 Mem0 的实现

Mem0 的 CRUD 到底是如何实现的&#xff1f;我们来看下源码。 使用 先来看下&#xff0c;如何使用 Mem0 import os os.environ["OPENAI_API_KEY"] "sk-xxx"from mem0 import Memorym Memory()# 1. Add: Store a memory from any unstructured text re…...

【LLM】-10-部署llama-3-chinese-8b-instruct-v3 大模型

目录 1、模型下载 2、下载项目代码 3、启动模型 4、模型调用 4.1、completion接口 4.2、聊天&#xff08;chat completion&#xff09; 4.3、多轮对话 4.4、文本嵌入向量 5、Java代码实现调用 由于在【LLM】-09-搭建问答系统-对输入Prompt检查-CSDN博客 关于提示词注入…...

C语言 之 理解指针(4)

文章目录 1. 字符指针变量2. 数组指针变量2.1 对数组指针变量的理解2.2 数组指针变量的初始化 3. 二维数组传参的本质4. 函数指针变量4.1 函数指针变量的创建4.2 函数指针变量的使用 5. 函数指针数组 1. 字符指针变量 我们在前面使用的主要是整形指针变量&#xff0c;现在要学…...

Java设计模式—单例模式(Singleton Pattern)

目录 一、定义 二、应用场景 三、具体实现 示例一 示例二 四、懒汉与饿汉 饿汉模式 懒汉模式 五、总结 六、说明 一、定义 二、应用场景 ‌单例模式的应用场景主要包括以下几个方面&#xff1a; ‌日志系统&#xff1a;在应用程序中&#xff0c;通常只需要一个日…...

AV1帧间预测(二):运动补偿

运动补偿(Motion Compensation,MC)是帧间预测最基础的工具&#xff0c;AV1支持两种运动补偿方式&#xff0c;一种是传统的平移运动补偿&#xff0c;另一种是仿射运动补偿。下面分别介绍这两种运动补偿方法。 平移运动补偿 平移运动补偿是最传统的运动补偿方式&#xff0c;H.26…...

React Native 导航系统实战(React Navigation)

导航系统实战&#xff08;React Navigation&#xff09; React Navigation 是 React Native 应用中最常用的导航库之一&#xff0c;它提供了多种导航模式&#xff0c;如堆栈导航&#xff08;Stack Navigator&#xff09;、标签导航&#xff08;Tab Navigator&#xff09;和抽屉…...

23-Oracle 23 ai 区块链表(Blockchain Table)

小伙伴有没有在金融强合规的领域中遇见&#xff0c;必须要保持数据不可变&#xff0c;管理员都无法修改和留痕的要求。比如医疗的电子病历中&#xff0c;影像检查检验结果不可篡改行的&#xff0c;药品追溯过程中数据只可插入无法删除的特性需求&#xff1b;登录日志、修改日志…...

【位运算】消失的两个数字(hard)

消失的两个数字&#xff08;hard&#xff09; 题⽬描述&#xff1a;解法&#xff08;位运算&#xff09;&#xff1a;Java 算法代码&#xff1a;更简便代码 题⽬链接&#xff1a;⾯试题 17.19. 消失的两个数字 题⽬描述&#xff1a; 给定⼀个数组&#xff0c;包含从 1 到 N 所有…...

解锁数据库简洁之道:FastAPI与SQLModel实战指南

在构建现代Web应用程序时&#xff0c;与数据库的交互无疑是核心环节。虽然传统的数据库操作方式&#xff08;如直接编写SQL语句与psycopg2交互&#xff09;赋予了我们精细的控制权&#xff0c;但在面对日益复杂的业务逻辑和快速迭代的需求时&#xff0c;这种方式的开发效率和可…...

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …...

DBAPI如何优雅的获取单条数据

API如何优雅的获取单条数据 案例一 对于查询类API&#xff0c;查询的是单条数据&#xff0c;比如根据主键ID查询用户信息&#xff0c;sql如下&#xff1a; select id, name, age from user where id #{id}API默认返回的数据格式是多条的&#xff0c;如下&#xff1a; {&qu…...

MySQL中【正则表达式】用法

MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现&#xff08;两者等价&#xff09;&#xff0c;用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例&#xff1a; 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...

C++.OpenGL (20/64)混合(Blending)

混合(Blending) 透明效果核心原理 #mermaid-svg-SWG0UzVfJms7Sm3e {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-icon{fill:#552222;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-text{fill…...

深入浅出Diffusion模型:从原理到实践的全方位教程

I. 引言&#xff1a;生成式AI的黎明 – Diffusion模型是什么&#xff1f; 近年来&#xff0c;生成式人工智能&#xff08;Generative AI&#xff09;领域取得了爆炸性的进展&#xff0c;模型能够根据简单的文本提示创作出逼真的图像、连贯的文本&#xff0c;乃至更多令人惊叹的…...

【UE5 C++】通过文件对话框获取选择文件的路径

目录 效果 步骤 源码 效果 步骤 1. 在“xxx.Build.cs”中添加需要使用的模块 &#xff0c;这里主要使用“DesktopPlatform”模块 2. 添加后闭UE编辑器&#xff0c;右键点击 .uproject 文件&#xff0c;选择 "Generate Visual Studio project files"&#xff0c;重…...