【数据结构】二叉树的链式结构

目录

一.二叉树的遍历

1.前序遍历

2.中序遍历

3.后序遍历

4.层序遍历

二.二叉树查询的基本操作

1.二叉树节点的个数

2.二叉树叶子节点的个数

3.二叉树的高度

4.二叉树第k层节点的个数

5.二叉树查询值为x的节点

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

7.二叉树基础oj练习

三.二叉树的创建和销毁

1.创建

2.销毁


一.二叉树的遍历

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

为了使遍历更加的形象,在这里我们先受搓一颗二叉树来说明二叉树的遍历。

typedef int BTDataType;

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

BTNode* BuyNode(BTDataType x)
{
    BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));
    if (newnode == NULL)
    {
        perror("malloc");
        return;
    }
    newnode->data = x;
    newnode->left = NULL;
    newnode->right = NULL;
    return newnode;
}

BTNode* CreateBinaryTree()
{
    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;
}

注意:上述代码并不是创建二叉树的方式 

再看二叉树基本操作前,再回顾下二叉树的概念,二叉树是:

1. 空树

2. 非空:根节点,根节点的左子树、根节点的右子树组成的。

从概念中可以看出,二叉树定义是递归式的,因此后序基本操作中基本都是按照该概念实现的。 

1.前序遍历

前序遍历是以根———左子树——右子树的顺序进行遍历的。

这里的N表示的是NULL。

用代码表示:

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

2.中序遍历

前序遍历是以左子树———根——右子树的顺序进行遍历的。

用代码表示:

void MiddleOrder(BTNode* root)
{
    if (root == NULL)
    {
        printf("N ");
        return;
    }
    MiddleOrder(root->left);
    printf("%d ", root->data);
    MiddleOrder(root->right);
}

3.后序遍历

前序遍历是以左子树———右子树——根的顺序进行遍历的。

用代码表示:

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

4.层序遍历

层序遍历需要用到队列的代码,所以我们要将之前队列的代码【数据结构】栈和队列-CSDN博客移动到现在所在的工程文件中。

思路:首先创建一个队列,将二叉树的根节点插入到队列当中。接着用while循环判断队列是否为空作为判断条件,pop掉队头的数据,然后将队头数据的左右子树分别带出来,前提是左右子树都不为空。最后销毁队列就可以了。

void TreeLevelOrder(BTNode* root)
{
    Queue pq;
    QueueInit(&pq);
    if (root)
    {
        QueuePush(&pq, root);
    }
    while (!QueueEmpty(&pq))
    {
        BTNode* front = QueueFront(&pq);
        QueuePop(&pq);
        printf("%d ", front->data);
        if (front->left)
        {
            QueuePush(&pq, front->left);
        }
        if (front->right)
        {
            QueuePush(&pq, front->right);
        }
    }
    QueueDestroy(&pq);
}

二.二叉树查询的基本操作

1.二叉树节点的个数

int BinaryTreeSize(BTNode* root)
{
    if (root == NULL)
    {
        return 0;
    }
    int left = BinaryTreeSize(root->left);
    int right = BinaryTreeSize(root->right);
    return left + right + 1;
}

在这里,我们采用递归的方法,递归的结束条件是节点为NULL,子问题是左右子树在加上根节点的数量(也就是1)。

2.二叉树叶子节点的个数

int BinaryTreeLeafSize(BTNode* root)
{
    if (root == NULL)
    {
        return 0;
    }
    if (root->left == NULL && root->right == NULL)
    {
        return 1;
    }
    int left = BinaryTreeLeafSize(root->left);
    int right = BinaryTreeLeafSize(root->right);
    return left + right;
}

递归的结束条件是左右子树都为空,且结束时需要返回1。

3.二叉树的高度

int BinaryTreeHeight(BTNode* root)
{
    if (root == NULL)
    {
        return 0;
    }
    int left = BinaryTreeHeight(root->left);
    int right = BinaryTreeHeight(root->right);
    return left > right ? left + 1 : right + 1;
}

递归的结束条件是节点为空,子问题是计算左右子树的高度并且返回高度比较高的子树。

4.二叉树第k层节点的个数

int BinaryTreeLevelKSize(BTNode* root, int k)
{
    if (root == NULL)
    {
        return 0;
    }
    if (k == 1)
    {
        return 1;
    }
    int left = BinaryTreeLevelKSize(root->left, k - 1);
    int right = BinaryTreeLevelKSize(root->right, k - 1);
    return left + right;
}

递归的结束条件是k==1,并且返回1。

如果k的值为3,则可以得到如下流程图:

5.二叉树查询值为x的节点

BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
    if (root == NULL)
    {
        return NULL;
    }
    if (root->data == x)
    {
        return root;
    }
    BTNode* left = BinaryTreeFind(root->left, x);
    if (left)
    {
        return left;
    }
    BTNode* right = BinaryTreeFind(root->right, x);
    if (right)
    {
        return right;
    }
    return NULL;
}

这里思路是遍历一遍二叉树,若找到,则返回给上一层。最后,若左子树或者右子树不为空,则说明找到了指定的节点。若没有找到,则返回NULL。

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

思路:这道题需要我们用到层序遍历,与层序遍历不同的是,我们要将NULL也带入到队列之中。当遇到第一个NULL时,break跳出来。然后判断之后队列之中的指针是否为空,若有空指针,返回假。若循环完,则返回真。

// 判断二叉树是否是完全二叉树
bool BinaryTreeComplete(BTNode* root)
{
    Queue pq;
    QueueInit(&pq);
    if (root)
    {
        QueuePush(&pq, root);
    }
    while (!QueueEmpty(&pq))
    {
        BTNode* front = QueueFront(&pq);
        QueuePop(&pq);
        if (front == NULL)
        {
            break;
        }
        QueuePush(&pq, front->left);
        QueuePush(&pq, front->right);
    }
    while (!QueueEmpty(&pq))
    {
        BTNode* front = QueueFront(&pq);
        QueuePop(&pq);
        if (front!=NULL)
        {
            QueueDestroy(&pq);
            return false;
        }
    }
    QueueDestroy(&pq);
    return true;
}

7.二叉树基础oj练习

接下来,我们根据刚才介绍的几种基本操作为基础完成几道oj练习:

965. 单值二叉树 - 力扣(LeetCode)

思路:为了方便操作,我们可以在创建一个函数,将根节点的值作为参数传给所创建的函数。依然采用递归的方法,结束条件是root为空或者root的值不等于val值。子问题是左右子树是否为单值二叉树。

100. 相同的树 - 力扣(LeetCode)

思路:用递归,结束条件是两个节点都为空,返回true,或者一个为空一个不为空,返回false,或者两个节点的值不相等,返回false。子问题是左右子树是否相等。

 101. 对称二叉树 - 力扣(LeetCode)

 思路:这道题同样是先创建一个新的函数,将左右子树的根节点传过去,再使用递归解决问题,结束条件是两个节点都为空,返回true,或者一个为空一个不为空,返回false,或者两个节点的值不相等,返回false。子问题是左子树的右节点是否跟右子树的左节点是否相等。

144. 二叉树的前序遍历 - 力扣(LeetCode)

思路:先用递归将二叉树遍历一遍,计算出二叉树节点的个数returnSize。在开辟returnSize个整形的空间,然后使用前序遍历将二叉树中的节点放到开辟的空间里面。

572. 另一棵树的子树 - 力扣(LeetCode)

思路:先创建一个函数,作用是判断两颗二叉树是否相等,参数是两颗二叉树的根节点。然后使用递归的方法,结束条件是节点为空,返回NULL,或者判断两颗是相等的,返回true。子问题是遍历一遍二叉树,判断是否有子树和指定的数相等。

三.二叉树的创建和销毁

1.创建

关于二叉树的创建,我们利用一道OJ题目来展示:

二叉树遍历_牛客题霸_牛客网 (nowcoder.com)

这道题输入的数据展开的数据所组成的二叉树为上图。

思路:将输入的数组组成一颗二叉树,再将二叉树一中序遍历的方法输出出来。

2.销毁

void TreeDestory(BTNode* root)
{
    if (root == NULL)
        return;

    TreeDestory(root->left);
    TreeDestory(root->right);
    free(root);
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Python编程实践2/3】Python图像处理模块(上)

目录 引言 目标 安装模块 Windows系统 macOS系统 路径 Windows路径 ​编辑macOS路径 windows路径报错 windows路径前的r 示例代码 windows快速查看路径 macOS快速查看路径 打开图片 展示图片 下节预告 总结 引言 欢迎各位大佬垂阅本篇Python实践博客&a…

一文读懂python同级目录的调用附Demo(详细解读)

目录 前言1. 问题所示2. 原理分析3. 解决方法3.1 添加父目录3.2 相对路径3.3 添加init 前言 通过制作简易的Demo,让其更加深入的了解如何使用 1. 问题所示 发现python的同级目录相互调用会出Bug E:\software\anaconda3\envs\py3.10\python.exe F:\python_project…

python之生成xmind

今天为啥要说这个呢,因为前几天做接口测试,还要写测试用例,我觉得麻烦,所以我就用了python里面xmind的插件。自动生成了测试用例,数据来源是json。 🍦 第一步安装 pip install xmind 🍦 第二…

01_Spring Ioc(详解) + 思维导图

文章目录 思维导图一.概念实操Maven父子工程 二. IOC和DI入门案例【重点】1 IOC入门案例【重点】问题导入1.1 门案例思路分析1.2 实现步骤2.1 DI入门案例思路分析2.2 实现步骤2.3 实现代码2.4 图解演示 三、Bean的基础配置问题导入问题导入1 Bean是如何创建的【理解】2 实例化B…

【408真题】2009-26

“接”是针对题目进行必要的分析,比较简略; “化”是对题目中所涉及到的知识点进行详细解释; “发”是对此题型的解题套路总结,并结合历年真题或者典型例题进行运用。 涉及到的知识全部来源于王道各科教材(2025版&…

Camunda BPM主要组件

Camunda BPM是使用java开发的,核心流程引擎运行在JVM里,纯java库,不依赖其他库或者底层操作系统。可以完美地与其他java框架融合,比如Spring。除了核心流程引擎外,还提供了一系列的管理,操作和监控工具。 1,工作流引擎 既适用于服务或者微服务编排,也适用于人工任务管…

VBA技术资料MF159:实现某个区域内的数据滚动

我给VBA的定义:VBA是个人小型自动化处理的有效工具。利用好了,可以大大提高自己的工作效率,而且可以提高数据的准确度。“VBA语言専攻”提供的教程一共九套,分为初级、中级、高级三大部分,教程是对VBA的系统讲解&#…

详解 Spark 的运行架构

一、核心组件 1. Driver Spark 驱动器节点,用于执行 Spark 任务中的 main 方法,负责实际代码的执行工作主要负责: 将用户程序转化为作业 (job)在 Executor 之间调度任务 (task)跟踪 Executor 的执行情况通过 UI 展示查询运行情况 2. Exec…

Springboot 实战运用

一&#xff0c;基本配置 1&#xff0c;pom文件配置介绍 1.1继承 <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.5.2</version><relativePath/> <…

MATLAB分类与判别模型算法:基于模糊神经网络的嘉陵江水质评价【含Matlab源码 MX_004期】

算法思路介绍&#xff1a; 基于模糊神经网络的水质预测系统&#xff0c;整体思路分为以下几个模块&#xff1a; 1. 数据准备模块 加载数据&#xff1a;从文件中加载训练和测试数据&#xff0c;包括输入数据和输出数据。数据归一化&#xff1a;对加载的数据进行归一化处理&am…

第十九节:带你梳理Vue2: 父组件向子组件传参(props传参)

1. 组件嵌套 1.1 组件的嵌套使用 之前有说过,Vue组件跟Vue实例是一样的,因此在Vue中一个组件中也可以定义并使用自己的局部组件,这就是组件的嵌套使用 例如:示例代码如下: <div id"app"><!-- 3. 使用组件 --><my-component></my-component&…

教学基本功包括什么技能有哪些

教师的工作不仅仅是传授知识&#xff0c;更多是引导学生探索&#xff0c;激发他们的创造力。要做到这一点&#xff0c;需要具备一些基本技能。 扎实的专业知识。这是教师的根基&#xff0c;如果教师自己对所教的科目都不熟悉&#xff0c;那么教学就会失去方向。不断学习更新自己…

告别虚拟机,在Windows10启动Linux子系统

背景 如果要在自己的windows电脑安装一个Linux系统,一般是用虚拟机软件例如VMware软件来创建。但是这种方式显得非常的笨重。而Windows10自带的Linux子系统则非常的方便。 分析 在Windows10中启用子系统的方式来安装Linux,用于学习和开发是非常方便的。子系统的实用就和一个…

基于深度学习的中文情感分析系统python flask

基于python的毕业设计 基于深度学习的中文情感分析系统(flask)(源码说明文档演示) 毕业设计课程设计期末大作业、课程设计、高分必看&#xff0c;下载下来&#xff0c;简单部署&#xff0c;就可以使用。 包含&#xff1a;项目源码、数据库脚本、软件工具等&#xff0c;该项目…

3D模型三角面转四角面操作指南---模大狮模型网

在3D建模的过程中&#xff0c;三角面(Triangles)和四角面(Quads)是两种常见的多边形类型。虽然三角面在渲染速度和计算效率上有其优势&#xff0c;但四角面在模型编辑和纹理映射上通常更为方便。因此&#xff0c;将三角面转换为四角面是建模过程中常见的需求。 一、选择合适的建…

py黑帽子学习笔记_scapy

简介 代码简洁&#xff1a;相比于前两个博客总结&#xff0c;很多socket操作&#xff0c;如果使用scapy仅需几行代码即可实现 获取邮箱身份凭证 编写基础嗅探器&#xff0c;脚本可显示任何收到的一个包的详细情况 直接运行 尝试监听邮件收发&#xff0c;监听指定端口&#x…

高性价比、超强功能的开源工单解决方案

在企业日常运营中&#xff0c;工单管理系统是不可或缺的工具。高效的工单管理不仅能提升工作效率&#xff0c;还能显著提高客户满意度。今天&#xff0c;我们为您推荐搭贝工单派单系统——一款超高性价比、功能齐全的开源工单管理系统。 &#x1f50d; 为什么选择搭贝工单派单…

通过vlan实现同一网段下的网络隔离

现有两个电脑通过交换机直接连接在一起 pc1&#xff1a; pc2&#xff1a; 正常状态下是可以ping成功的 现在先进入交换机命令行界面&#xff0c;创建两个vlan <Huawei>system-view Enter system view, return user view with CtrlZ. [Huawei]vlan 10 [Huawei-vlan10…

Python面向对象学习笔记

Python面向对象编程 记录人&#xff1a; 李思成 时间&#xff1a; 2024/05/01至2024/05/23 课程来源&#xff1a; B站Python面向对象 1.面向对象编程概述 官方概述 程序是指令的集合&#xff0c;运行程序时&#xff0c;程序中的语句会变成一条或多条指令&#xff0c;然后…

数组-给出最大容量,求能获得的最大值

一、问题描述 二、解题思路 这个题目其实是求给出数组中&#xff0c;子数组和不大于M中&#xff0c;和最大值的子数组。 求子数组使用双指针就可以解决问题&#xff0c;相对比较简单。&#xff08;如果是子序列&#xff0c;则等价于0-1背包问题&#xff0c;看题目扩展中的问题…