【数据结构】二叉树的链式实现及遍历

文章目录

  • 一、二叉树的遍历
    • 1、前序遍历
    • 2、中序遍历
    • 3、后序遍历
    • 4、层序遍历
  • 二、二叉树结点个数及高度
    • 1、二叉树节点个数
    • 2、二叉树叶子节点个数
    • 3、二叉树第k层节点个数
    • 4、二叉树查找值为x的节点
  • 三、二叉树创建及销毁
    • 1、通过前序遍历数组创建二叉树
    • 2、二叉树的销毁
    • 3、判断是否为完全二叉树
  • 四、测试代码

在这里插入图片描述

一、二叉树的遍历

后文所有代码中的二叉树结点:

typedef char BTDataType;
//二叉树结点结构体
typedef struct BinaryTreeNode
{BTDataType data;struct BinaryTreeNode* left;struct BinaryTreeNode* right;
}BTNode;

1、前序遍历

前,中,后序遍历都可以采用分治递归的思想解决,将根节点和它的孩子结点分别处理。

// 二叉树前序遍历 
void BinaryTreePrevOrder(BTNode* root)
{if (root == NULL){printf("NULL ");return;}printf("%c ", root->data);BinaryTreePrevOrder(root->left);BinaryTreePrevOrder(root->right);
}

此处仅利用递归展开图分析前序遍历,中序和后序也是相同的思想:

在这里插入图片描述

在这里插入图片描述

2、中序遍历

// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root)
{if (root == NULL){printf("NULL ");return;}BinaryTreeInOrder(root->left);printf("%c ", root->data);BinaryTreeInOrder(root->right);
}

3、后序遍历

// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root)
{if (root == NULL){printf("NULL ");return;}BinaryTreePostOrder(root->left);BinaryTreePostOrder(root->right);printf("%c ", root->data);
}

4、层序遍历

层序遍历需要利用队列来进行,如果二叉树跟结点不为空,则让指向它的一个指针入队,然后将队头结点记录下来,先将它的值打印,然后判断它的左右孩子为非空则入队,然后删掉队头换下一个继续记录打印…直到队列为空则遍历完成。

例如对如图这个二叉树:

层序遍历结果为:12345
在这里插入图片描述
先将根节点1入队,打印1

在这里插入图片描述

然后将1的左右孩子2和3入队
在这里插入图片描述

删掉队头1,front换为2,打印2
在这里插入图片描述

然后将2的左孩子4入队
在这里插入图片描述

删掉队头2,front换为3,打印3
在这里插入图片描述

然后将3的右孩子5入队
在这里插入图片描述

… …

接着按这样打印4,5便完成了二叉树的层序遍历

在这里插入图片描述

程序代码利用了自己创建的队列,代码如下:

//层序遍历
void LevelOrder(BTNode* root)
{//创建队列Que q;QueueInit(&q);//如果根节点不为空,则放进队列if (root)QueuePush(&q, root);while (!QueueEmpty(&q)){//将队头打印BTNode* front = QueueFront(&q);printf("%c ", front->data);//判断front左右节点不为空则入队if (front->left)QueuePush(&q, front->left);if (front->right)QueuePush(&q, front->right);QueuePop(&q);}printf("\n");QueueDestroy(&q);
}

二、二叉树结点个数及高度

1、二叉树节点个数

采用分治法递归实现,当根节点为空时返回值为0,不为空则返回左右子树上的节点数加上自身1。

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

2、二叉树叶子节点个数

采用分治法递归实现,根节点为空时返回0,当根节点没有孩子结点时说明它是叶子节点,返回1,其他情况时只需左右子树上的叶子节点相加即可。

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);
}

3、二叉树第k层节点个数

需要保证k大于0才可,当根节点为空,则返回0,当k等于1时只有一层一个节点,返回1,k>1时第k层节点数就相当于它左右孩子的第k-1层节点数相加。

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

4、二叉树查找值为x的节点

跟节点为空则找不到返回NULL,当根节点的值为要找的值时返回该节点,不相等则分别判断它的左右孩子节点,直到找到为止。

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

三、二叉树创建及销毁

1、通过前序遍历数组创建二叉树

读入用户输入的一串先序遍历字符串,根据此字符串建立一个二叉树(以指针方式存储)。 例如如下的先序遍历字符串: ABC##DE#G##F### 其中“#”表示的是空格,空格字符代表空树。建立起此二叉树以后,再对二叉树进行中序遍历,输出遍历结果。

#include <stdio.h>
#include<stdlib.h>
typedef char BTDataType;typedef struct BinaryTreeNode {BTDataType data;struct BinaryTreeNode* left;struct BinaryTreeNode* right;
} BTNode;
BTNode* BinaryTreeCreate(BTDataType* a, int* pi) {if (a[*pi] == '#') {++*pi;return NULL;}BTNode* root = (BTNode*)malloc(sizeof(BTDataType));root->data = a[*pi];++*pi;root->left = BinaryTreeCreate(a, pi);root->right = BinaryTreeCreate(a, pi);return root;
}
//中序遍历
void InOrder(BTNode* root)
{if(root==NULL){return;}InOrder(root->left);printf("%c ",root->data);InOrder(root->right);
}
int main() {char a[100];scanf("%s",a);int pi=0;BTNode* root=BinaryTreeCreate(a, &pi);InOrder(root);return 0;
}

2、二叉树的销毁

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

3、判断是否为完全二叉树

在二叉树层序遍历的基础上修改一下,让空节点也进入队列,遍历时遇到空节点则退出,继续遍历如果结束前还有非空节点则不是完全二叉树。

int BinaryTreeComplete(BTNode* root)
{//创建队列Que q;QueueInit(&q);//如果根节点不为空,则放进队列if (root)QueuePush(&q, root);while (!QueueEmpty(&q)){BTNode* front = QueueFront(&q);if (front == NULL){break;}QueuePush(&q, front->left);QueuePush(&q, front->right);QueuePop(&q);}//此时已经遇到空节点,如果再遇到非空节点则不是完全二叉树while (!QueueEmpty(&q)){BTNode* front = QueueFront(&q);if (front){QueueDestroy(&q);return false;}QueuePop(&q);}QueueDestroy(&q);return true;
}

四、测试代码

手动构建一个如下图的二叉树,对代码进行测试:
在这里插入图片描述
测试结果应该为:

前序:123874569
中序:832715469
后序:837259641

是否为完全二叉树:0
节点数:9
叶子节点数:4

BTNode* BuyNode(BTDataType x)
{BTNode* node = (BTNode*)malloc(sizeof(BTNode));if (node == NULL){perror("malloc fail");exit(-1);}node->data = x;node->left = NULL;node->right = NULL;return node;
}int main()
{// 手动构建BTNode* node1 = BuyNode('1');BTNode* node2 = BuyNode('2');BTNode* node3 = BuyNode('3');BTNode* node4 = BuyNode('4');BTNode* node5 = BuyNode('5');BTNode* node6 = BuyNode('6');BTNode* node7 = BuyNode('7');BTNode* node8 = BuyNode('8');BTNode* node9 = BuyNode('9');node1->left = node2;node1->right = node4;node2->left = node3;node4->left = node5;node4->right = node6;node2->right = node7;node3->left = node8;node6->right = node9;printf("前序遍历:");BinaryTreePrevOrder(node1);printf("\n");printf("中序遍历:");BinaryTreeInOrder(node1);printf("\n");printf("后序遍历:");BinaryTreePostOrder(node1);printf("\n");printf("层序遍历:");LevelOrder(node1);printf("\n");printf("BinaryTreeComplete:%d\n", BinaryTreeComplete(node1));printf("BinaryTreeSize:%d\n", BinaryTreeSize(node1));printf("BinaryTreeLeafSize:%d\n", BinaryTreeLeafSize(node1));BinaryTreeDestory(node1);node1 = NULL;return 0;
}

运行结果:

在这里插入图片描述
运行结果与预测结果一致。

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

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

相关文章

Pytest单元测试框架 —— Pytest+Allure+Jenkins的应用

一、简介 pytestallurejenkins进行接口测试、生成测试报告、结合jenkins进行集成。 pytest是python的一种单元测试框架&#xff0c;与python自带的unittest测试框架类似&#xff0c;但是比unittest框架使用起来更简洁&#xff0c;效率更高 allure-pytest是python的一个第三方…

IMX6ULL移植篇-Linux内核源码文件表

一. Linux内核源码目录 我们在分析 Linux 之前&#xff0c;一定要先在 Ubuntu 中编译一下 Linux &#xff0c;因为编译过程会生成一些文件&#xff0c;而生成的这些恰恰是分析 Linux 不可或缺的文件。 二. Linux内核源码重要文件含义 编译后的 Linux内核源码重要的文件…

修改接口,字段的内容允许清空,避免歧义,参数校验:@NotNull

1. 问题描述 修改接口&#xff0c;字段的内容允许清空&#xff0c;是否应该做参数校验&#xff1f;如何做参数校验&#xff1f; 2. 说明 2.1. 需要对字段进行校验。 因为不校验&#xff0c;字段可能不传&#xff0c;或者字段的值为null&#xff1b;这样无法判断出&#xff…

【Linux基础】第27讲 Linux 查找和过滤命令(二)——grep命令

Grep命令 grep是根据文件的内容进行查找&#xff0c;会对文件的每一行按照给定的模式&#xff08;patter&#xff09;进行匹配查找 基本格式&#xff1a; grep [options]范围 [options] 主要参数 -c: 只输出匹配行的计数 -i : 不区分大小写 -n: 显示匹配行及行号 -w: 显示整个…

[Linux入门]---文本编辑器vim使用

文章目录 1.Linux编辑器-vim使用2.vim的基本概念4.vim正常模式命令集从正常模式进入插入模式从插入模式转换为命令模式移动光标删除文字复制替换撤销更改跳至指定行 5.vim末行模式命令集5.总结 1.Linux编辑器-vim使用 vi/vim作为Linux开发工具之一&#xff0c;从它的键盘操作图…

驱动开发练习,platform实现如下功能

实验要求 驱动代码 #include <linux/init.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/mod_devicetable.h> #include <linux/of_gpio.h> #include <linux/unistd.h> #include <linux/interrupt…

PDCA循环

目录 1.认识PDCA&#xff1a; 2.PDCA循环的经典案例 3.PDCA的四个阶段和八个步骤 4.PDCA循环的优缺点&#xff1a; 5.案例 6.其他作用 1.认识PDCA&#xff1a; PDCA循环最早由美国质量统计控制之父Shewhat&#xff08;休哈特&#xff09;提出的PDS&#xff08;Plan Do Se…

hadoop3.x搭建到集群调优

一、基础环境安装 https://blog.csdn.net/fen_dou_shao_nian/article/details/120945221 二、hadoop运行环境搭建 2.1 模板虚拟机环境准备 0&#xff09;安装模板虚拟机&#xff0c;IP 地址 192.168.10.100、主机名称 hadoop100、内存 4G、硬盘 50G 1&#xff09;hadoop100…

【Html】用CSS定义咖啡 - 咖啡配料展示

显示效果 代码 index.html <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><title>CodePen - For The Love Of Coffee</title><link rel"stylesheet" href"./style.css">&l…

阿里云服务器部署安装hadoop与elasticsearch踩坑笔记

2023-09-12 14:00——2023.09.13 20:06 目录 00、软件版本 01、阿里云服务器部署hadoop 1.1、修改四个配置文件 1.1.1、core-site.xml 1.1.2、hdfs-site.xml 1.1.3、mapred-site.xml 1.1.4、yarn-site.xml 1.2、修改系统/etc/hosts文件与系统变量 1.2.1、修改主机名解…

基于ENC28J60+uIP1.0+STM32的UDP Server实现,以及主动发送数据,几个关键的问题可算整明白了!

ENC28J60&#xff0c;是一款SPI接口的以太网PHYMAC芯片&#xff0c;实现以太网物理层和MAC层硬件通信。uIP是一个TCP/IP软件协议栈&#xff0c;实现TCP、UDP、ARP、ICMP等网络协议。STM32F103RCT6通过SPI接口与ENC28J60通讯&#xff0c;并移植uIP协议&#xff0c;实现一个小型的…

利用Linux虚拟化技术实现资源隔离和管理

在现代计算机系统中&#xff0c;资源隔离和管理是非常重要的&#xff0c;特别是在多租户环境下。通过利用Linux虚拟化技术&#xff0c;我们可以实现对计算资源&#xff08;如CPU、内存和存储&#xff09;的隔离和管理&#xff0c;以提供安全、高效、稳定的计算环境。下面将详细…

如何将内网ip映射到外网?快解析内网穿透

关于内网ip映射到外网的问题&#xff0c;就是网络地址转换&#xff0c;私网借公网。要实现这个&#xff0c;看起来说得不错&#xff0c;实际上是有前提条件的。要实现内网ip映射到外网&#xff0c;首先要有一个固定的公网IP&#xff0c;可以从运营商那里得到。当你得到公网IP后…

Flink——Flink检查点(checkpoint)、保存点(savepoint)的区别与联系

Flink checkpoint Checkpoint是Flink实现容错机制最核心的功能&#xff0c;能够根据配置周期性地基于Stream中各个Operator的状态来生成Snapshot&#xff0c;从而将这些状态数据定期持久化存储下来&#xff0c;从而将这些状态数据定期持久化存储下来&#xff0c;当Flink程序一…

FPGA设计时序约束一、主时钟与生成时钟

​目录 一、主时钟create_clock 1.1 定义 1.2 约束设置格式 1.3 Add this clock to the existing clock 1.4 示例 1.5 差分信号 二、生成时钟generate_clock 2.1 定义 2.2 格式 2.2.1 by clock frequency 2.2.2 by clock edges 2.2.3 示例 2.2.4 自动生成时钟 2.…

MongoDB-1入门介绍

NoSQL NoSQL(NoSQL Not Only SQL)&#xff0c;意即反SQL运动&#xff0c;指的是非关系型的数据库 优点 1、对数据库高并发读写。 2、对海量数据的高效率存储和访问。 3、对数据库的高可扩展性和高可用性。 弱点&#xff1a; 1、数据库事务一致性需求 2、数据库的写实时性…

flink集群与资源@k8s源码分析-集群

0 介绍 本文是flink集群与资源@k8s源码分析系列的第二篇-集群 1 场景 下面详细分析各用例 2 启动k8s集群 k8s集群支持session和application模式,job模式将会被废弃,本文分析session模式集群 Configuration作为配置容器,几乎所有的构建需要从配置类获取配置项,这里不显示…

将docker镜像打成tar包

# 打包 docker save -o zookeeper.tar bitnami/zookeeper:3.9.0-debian-11-r11# 解压 docker load -i zookeeper.tar

day27IO(异常File综合案例)

1. 异常 1.1 异常概念 异常&#xff0c;就是不正常的意思。在生活中:医生说,你的身体某个部位有异常,该部位和正常相比有点不同,该部位的功能将受影响.在程序中的意思就是&#xff1a; 异常 &#xff1a;指的是程序在执行过程中&#xff0c;出现的非正常的情况&#xff0c;最…

事务碰上锁好似那油锅里进了火

目录 前言 场景 代码复现 提出疑问 该怎么解决呢 1.使用编程式事务 2.将事务独立出一个方法 前言 很多时候我们谈起事务都是如虎色变&#xff0c;一想起来都是脑袋懵懵的 事务的隔离级别及传播机制是什么Spring的事务底层实现原理了解吗哪几种情况下事务会失效 …