数据结构之二叉树的基本实现

在我们之前已经了解的堆这样的完全二叉树的实现,也对树型结构有了一些了解,那么今天我们来看看二叉树的一些性质。

因为二叉树是一种每个节点至多只有两个子树(即二叉树的每个节点的度不大于2),并且二叉树的子树有左右之分,其次序不能任意颠倒,因此它的结构也是比较独特的。

目录

1.二叉树的结构定义

2.节点构造

3.节点生成树

3.二叉树的遍历方式

先序遍历:

中序遍历:

后序遍历

层次遍历:

4.求树的所有节点个数

5.求叶子节点个数

6.求二叉树树深度

7.二叉树第K层节点个数

8.返回值为x的节点


1.二叉树的结构定义

二叉树是一种每个节点至多只有两个子树(即二叉树的每个节点的度不大于2),并且二叉树的子树有左右之分,其次序不能任意颠倒。

//定义树的节点
typedef int DATAtype;
typedef struct TreeNode
{
	DATAtype data;
	struct TreeNode* leftchild;
	struct TreeNode* rightchild;
}BTnode;

2.节点构造

简单的节点构造,如同链表的结点,不同的是这里有两个节点表示左孩子与右孩子。

//构造树的节点
BTnode* CreateNode(DATAtype x)
{
	BTnode* newnode = (BTnode*)malloc(sizeof(BTnode));
	if (newnode == NULL)
	{
		perror("malloc fali");
		return NULL;
	}
	newnode->data = x;
	newnode->leftchild = NULL;
	newnode->rightchild = NULL;
	return newnode;
}

3.节点生成树

我们是通过链接节点之间形成树的逻辑关系,这里的树如图:

 先链接了六个节点,之后又添加了一个节点

//利用节点生成一个树
BTnode* TreeCreat()
{
	BTnode* node1=CreateNode(1);
	BTnode* node2=CreateNode(2);
	BTnode* node3=CreateNode(3);
	BTnode* node4=CreateNode(4);
	BTnode* node5=CreateNode(5);
	BTnode* node6=CreateNode(6);
	BTnode* node7 = CreateNode(6);
	//建立连接关系
	node1->leftchild = node2;
	node1->rightchild = node4;
	node2->leftchild = node3;
	node4->leftchild = node5;
	node4->rightchild = node6;
	node6->leftchild = node7;
	//返回根
	return node1;
}

3.二叉树的遍历方式

二叉树的遍历方式主要有四种,分别是先序遍历、中序遍历、后序遍历和层次遍历。123

先序遍历:

先访问根节点,再访问左子树,最后访问右子树。

//前序遍历
void Preverorder(BTnode*root)
{
	//根 左子树 右子树
	if (root == NULL)
	{
		return;
	}
	printf("%d ", root->data);
	Preverorder(root->leftchild);
	Preverorder(root->rightchild);

}


中序遍历:

先访问左子树,再访问根节点,最后访问右子树。

void Inorder(BTnode* root)
{
	// 左子树 根  右子树
	if (root == NULL)
	{
		return;
	}
	
	Preverorder(root->leftchild);
	printf("%d ", root->data);
	Preverorder(root->rightchild);

}


后序遍历

:先访问左子树,再访问右子树,最后访问根节点。

void Postorder(BTnode* root)
{
	// 左子树 右子树 根 
	if (root == NULL)
	{
		return;
	}

	Preverorder(root->leftchild);
	Preverorder(root->rightchild);
	printf("%d ", root->data);
	
}


层次遍历:

按照从上到下、从左到右的顺序依次访问每个节点。

层序遍历我们使用队列实现,思路:先进先出,上一层出队时带下一层节点入队。

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include"queue.h"
#define CRT_SECURE_NO_WARNINGS 1

//定义树的节点
typedef int DATAtype;
typedef struct TreeNode
{
	DATAtype data;
	struct TreeNode* leftchild;
	struct TreeNode* rightchild;
}BTnode;

//构造树的节点
BTnode* CreateNode(DATAtype x)
{
	BTnode* newnode = (BTnode*)malloc(sizeof(BTnode));
	if (newnode == NULL)
	{
		perror("malloc fali");
		return NULL;
	}
	newnode->data = x;
	newnode->leftchild = NULL;
	newnode->rightchild = NULL;
	return newnode;
}
//利用节点生成一个树
BTnode* TreeCreat()
{
	BTnode* node1=CreateNode(1);
	BTnode* node2=CreateNode(2);
	BTnode* node3=CreateNode(3);
	BTnode* node4=CreateNode(4);
	BTnode* node5=CreateNode(5);
	BTnode* node6=CreateNode(6);
	BTnode* node7 = CreateNode(6);
	//建立连接关系
	node1->leftchild = node2;
	node1->rightchild = node4;
	node2->leftchild = node3;
	node4->leftchild = node5;
	node4->rightchild = node6;
	node6->leftchild = node7;
	//返回根
	return node1;
}

 层序遍历:

//层序遍历
void leverorder(BTnode* root)
{
	LTnode p;
	Queueinit(&p);
	Queuedestroy(&p);
	//先入根节点
	if (root)
	{
		LTpush(&p, root);
	}
	while (!LTempety(&p))
	{
		//队中数据全是树节点指针型
		BTnode* front = LTfront(&p);
			LTpop(&p);//出队头

			printf("%d", front->data);
         //判断孩子节点
			if (front->leftchild)
			{
				LTpush(&p, front->leftchild);
			}
			if (front->rightchild)
			{
				LTpush(&p, front->rightchild);
			}
	}
	printf("\n");
}

4.求树的所有节点个数

这里有两种方法,除了定义全局变量利用计数的方法来计算树的节点个数,但还需注意全局变量使用后需置零,其次我们也是利用递归的返回值累加计算出节点个数。

//求树的所有节点个数
int size = 0;
int Binarytreesize(BTnode* root)
{
	/*分治思想  从左子树到右子树再到根*/
	if (root == NULL)
	{
		return 0;
	}
	return (1 + Binarytreesize(root->leftchild) + Binarytreesize(root->rightchild));


	/*if (root)
	{
		size++;
		Binarytreesize(root->leftchild);
		Binarytreesize(root->rightchild);
	}
	return size;*/
}

5.求叶子节点个数

寻找递归条件,叶子节点没有左右孩子,否则就不返回一,符合条件的返回一相加。注意递归中返回值的设定。

//求叶子节点个数
int BTreeleavessize(BTnode* root)
{
	//自己的左子树与左右子树为空
	if (root == NULL)
	{
		return 0;
	}
	if (root->leftchild == NULL && root->rightchild == NULL)
	{
		return 1;
	}
	else
	{
		return BTreeleavessize(root->leftchild) + BTreeleavessize(root->rightchild);
	}
}

6.求二叉树树深度

分治思想,两边同时遍历,每有一层加一,左孩子层数与右孩子层数中较大的那个就是深度。

//求二叉树的深度
int BTreeheight(BTnode* root)
{
	//左右同时遍历,选最大的哪一个
	if (root == NULL)
	{
		return 0;
	}
	//这里注意用变量保存一下左 右子树的数目
	int left = BTreeheight(root->leftchild) + 1;
	int right= BTreeheight(root->rightchild) + 1;
	if (left > right)
	{
		return left;
	}
	else
	{
		return right;
	}
}

7.二叉树第K层节点个数

这里的递归主要是找的第k层,利用k==1作为递归返回条件。

//二叉树第k层结点的个数
int BTree_knumber(BTnode* root,int k)
{
	//分情况讨论
	if (root == NULL)
	{
		return 0;
	}
	if (k==1)
	{
		return 1;
	}

	return  BTree_knumber(root->leftchild, k - 1) +
			BTree_knumber(root->rightchild, k - 1);

	
	
}

8.返回值为x的节点

这里的难度在与返回值,我们知道在递归里面函数返回值不能直接返回,我们需要判断,对于返回值是需要我们好好检查的,在这里,我们从根,左孩子,右孩子的顺序逐个判断,对于左右孩子并保存返回值,来确定是当前节点。

//返回为x的树节点
BTnode* BTreenode(BTnode* root, DATAtype x)
{
	if (root == NULL)
	{
		return NULL;
	}
	if (root->data == x)
	{
		return root;
	}
	
	BTnode* left = BTreenode(root->leftchild,x);
	
	if (left->data==x)
	{
		return left;
	}
	BTnode* right = BTreenode(root->rightchild,x);
	if (right->data==x)
	{
		return right;
	}
	return NULL;
}

一些测试用例:

int main()
{
	BTnode* root = TreeCreat();
	/*Preverorder(root);
	printf("\n");
	Inorder(root);
	printf("\n");
	Postorder(root);*/
	//Binarytreesize(root);
	// BTreeleavessize(root);//BTreeheight(root);
	int x = BTree_knumber(root, 2);

	printf("%d ", BTreenode(root, 2)->data);
	//printf("%d", x);
	return 0;
}

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

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

相关文章

Shell脚本攻略:shell函数应用

目录 一、理论 1.shell函数 2.函数传参 3.函数变量的作用范围 4.递归 5.函数位置变量与脚本位置变量区别 6.创建库 二、实验 1.实验一 一、理论 1.shell函数 &#xff08;1&#xff09;概念 将命令序列按格式写在一起&#xff0c;可方便重复使用命令序列。 ① 避免…

Docker容器与虚拟机(VM)大对比

Docker是一个开源应用容器引擎。Docker可以将应用程序与基本架构分开&#xff0c;从而快速交付软件。 传统虚拟机的运行需要占用较高的资源&#xff0c;包括磁盘空间、内存和处理器性能。每个虚拟机都需要完整的操作系统和应用程序副本&#xff0c;这在资源利用和启动时间上存…

js实现PDF 预览和文件下载

在开发过程中要求对 PDF 类型的发票提供 预览 和 下载 功能&#xff0c;PDF 类型文件的来源又包括 H5 移动端 和 PC 端&#xff0c;而针对这两个不同端的处理会有些许不同&#xff0c;下文会有所提及。 针对 PDF 预览 的文章不在少数&#xff0c;但似乎都没有提及可能遇到的问…

Markdown可以在线编辑吗?这个办法很好用

Markdown是一种轻量级标记语言&#xff0c;它使用简单的语法来创建文本&#xff0c;非常易于学习。它最初被设计为一种用于写作的格式&#xff0c;但现在已经成为了一种非常受欢迎的文本编辑工具。 作为一个较为方便的在线文本编辑器&#xff0c;它可以用代码代替文字&#xf…

27事务管理AOP

一、MySQL事务回顾 二、Spring事务管理 Spring框架的第一大核心&#xff1a;IOC控制反转 在DeptServiceImpl下删除部门方法下新加一个删除员工信息的操作&#xff0c;注意&#xff1a;此时的id是部门id。 1、问题分析 2、Transactional-Spring事务管理 一般是在Service实现类的…

Visual Studio内引用Lua解释器,编译Lua源码,执行Lua脚本

前言 本篇在讲什么 在Visual Studio中引入lua的解释器 使用C调用Lua文件 本篇适合什么 适合初学Lua的小白 适合需要C/C和lua结合开发的人 本篇需要什么 对Lua语法有简单认知 对C/C语法有简单认知 依赖Lua5.1的环境 依赖VS 2017编辑器 本篇的特色 具有全流程的图文…

springboot中将logback切换为log4j2

前言 springboot默认使用logback作为日志记录框架&#xff0c;常见的日志记录框架有log4j、logback、log4j2。这篇文章我们来学习怎样将logbak替换为log4j2。 一、为什么使用log4j2&#xff1f; 我们在项目中经常使用一个叫SLF4J的依赖&#xff0c;它是做什么的呢&#xff1f; …

Activity的预览窗口StartingWindow添加

Activity的预览窗口StartingWindow添加 1、Activity组件启动2、ActivityStarter.java#startActivityInner() > 主要查看Task.java#startActivityLocked3、ActivityRecord.java#addStartingWindow到WindowManagerService.java#addWindow3.1 ActivityRecord.java#addStartingW…

C/C++开发,libiec61850库学习及运用

目录 一、libiec61850库下载编译 1.1 下载 1.2 linux编译&#xff1a; 1.3 win编译 二、案例编译测试 2.1 CMakeLists.txt调整(server_example_goose) 2.2 模型static_model.h/static_model.cpp生成 2.3 案例编译(server_goose) 2.4 客户端编译 2.5 运行测试 一、libiec61850…

【Python开发】FastAPI 03:请求参数—请求体

除了路径参数和查询参数&#xff0c;还有请求体&#xff0c;其用于传递 JSON、XML 或其他格式的数据&#xff0c;以便服务器能够读取并做出相应的处理&#xff0c;可以说请求体的作用更为强大。试想一下&#xff0c;如果存在七八个参数&#xff0c;路径参数和查询是不是就招架不…

Android播放器拖动进度条的小图预览

Android播放器拖动进度条的小图预览 背景效果图关键代码1. 获取指定位置的视频帧2. 预览图的显示和隐藏 完整代码1. xml布局文件activity_video.xml2. Activity文件VideoActivity.java 背景 我们在使用一些播放器时&#xff0c;拖动进度条会有一个预览框&#xff0c;上一篇博客…

Docker容器 和 Kubernetes容器集群管理系统

一、快速了解Docker 1. 什么是Docker的定义 Docker 是一个开源的应用容器引擎&#xff0c;基于Go语言并遵从 Apache2.0 协议开源。Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中&#xff0c;然后发布到任何流行的 Linux 机器上&#xff0c;也可以…

javaScript 给图片加水印

背景 在很多地方&#xff0c;我们都可以看到&#xff0c;上传图片的时候&#xff0c;图片都会被加上默认的水印&#xff0c;水印的作用主要体现在以下几个方面&#xff1a; 1.版权保护&#xff1a;在商业用途的照片中添加水印可以帮助保护作者的版权&#xff0c;防止他人未经…

IOS复杂震动AHAP文件编辑指南

简介 目前部分游戏会在播放一些特定的音乐音效时&#xff0c;令设备产生贴合音效的复杂震动&#xff0c;给玩家一个更好的游戏体验。这种复杂震动就是通过苹果的CoreHaptics库实现的。 下面是关于CoreHaptics的官方文档 ​​​​​​​Core Haptics | Apple Developer Docum…

C++ Qt项目实战:构建高效的代码管理器

C Qt项目实战&#xff1a;构建高效的代码管理器 一、项目概述&#xff08;Introduction&#xff09;1.1 项目背景&#xff08;Project Background&#xff09;1.2 项目目标&#xff08;Project Goals&#xff09;1.3 项目应用场景&#xff08;Project Application Scenarios&am…

《操作系统》期末主观题梳理

操作系统简答题 文章目录 操作系统简答题第一章第二章第三章第四章第五章第六章第七章第八章第九章 第一章 在计算机系统上配置OS(operating system, 操作系统)的目标是什么?作用主要表现在哪几个方面? 在计算机系统上配置OS, 主要目标是实现&#xff1a;方便性、有效性、可…

加速数实融合,数据交易3.0模式上新

数据交易市场将迎来真正的突破&#xff1f; 目前看的确如此。随着去年底“数据二十条”的颁布&#xff0c;业界普遍认为数据基础制度将加速走向落地与完善&#xff0c;数据要素化今年有望迎来全面提速&#xff0c;将极大促进数据交易市场走向规模化。 IDC预测&#xff0c;到2…

css3新增特性

1. 初始化 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"widthdevice-width, …

【Cpp】哈希之手撕闭散列/开散列

文章目录 unorderedunordered系列关联式容器unordered_map和unordered_set概述unordered_map的文档介绍unordered_map的接口说明 底层结构 哈希哈希/散列表 概念哈希冲突哈希函数哈希函数设计原则&#xff1a;常见哈希函数 哈希冲突解决闭散列线性探测二次探测 开散列 哈希表的…

mysql学习

DISTINCT 检索不同行 该关键字的作用就是用来去重&#xff0c;可以将你所要展示的数据中完全相同的去重&#xff0c;只展示一个&#xff1b; LIMIT 限制结果 该关键字的作用就是你限制它返回几条数据&#xff0c;比如你想要获得前面5行的据&#xff0c;就可以使用limit 5&…
最新文章