数据结构——(超详细,超简单)二叉树的实现

🌟一、二叉树的简述

树:通俗的说就是一个结点会分出多个分支;而二叉树也就是一个结点只能分出两个分叉。如:

在这里插入图片描述
上面就是一个简单的二叉树结构,每个数都有一个总的根结点,也就是 a,
由这个总结点分出两个分支,简称左子树右子树;而这个两个子分支又可以作为下一个分支的根结点。

🌟二、创建二叉树

1、问询法创建二叉树

创建一个二叉树,首先需要创建一个结构体,结构体里面有一个存放数据的变量,两个结构体指针存放左子树和右子树:

struct binary_tree
{
	char ch;//数据
	struct binary_tree* lsubtree, * rsubtree;//创建左子树和右子树
};

然后创建一个函数实现创建二叉树:

struct binary_tree* creat_tree(struct binary_tree* tree)
{
	char c;
	tree = new struct binary_tree;//开辟结构体内存空间
	cout << "请输入一个数据:";
	cin >> tree->ch;//输入数据
	cin.sync();//这个函数的作用是清除缓存区,读取 '\n'换行
	cout << "是否为 " << tree->ch<< " 创建左子树:(y / n)";
	cin >> c;
	cin.sync();
	if (c == 'y')
		tree->lsubtree = creat_tree(tree->lsubtree);//调用递归创建左子树
	else
		tree->lsubtree = nullptr;//不创建就设为空
	cout << "是否为 " << tree->ch <<" 创建右子树:(y / n)";
	cin >> c;
	cin.sync();
	if (c == 'y')
		tree->rsubtree = creat_tree(tree->rsubtree);//调用递归创建右子树
	else
		tree->rsubtree = nullptr;//不创建设为空
	return tree;//返回根结点
}

这个函数需要传递一个结构体指针,利用递归的调用来实现每个结点在这里插入代码片

tree->lsubtree = creat_tree(tree->lsubtree);//调用递归创建左子树

调用递归来创建左子树,然后在这个左子树的基础上创建其他左子树和右子树,也就是左子树的这个位置变成了一个根结点。
在这里插入图片描述
只要能理解递归就可以了。

2、补空法创建二叉树

  • 上面使用的是问询法创建二叉树,可是这个方法一些缺点:输入麻烦,容易出错
    优点:能够清楚的了解生成的过程
  • 补空法创建二叉树就比较简洁,只需要提前把二叉树先序遍历出来,当做一个字符串输入进去,
    先序遍历的过程在下面。

在这里插入图片描述
这就是补空的结果。
下面是实现的代码:


struct binary_tree* found_tree(struct binary_tree* tree)
{
	tree = new struct binary_tree;
	char ch;
	cin >> ch;//输入一串已经提前先序遍历好的二叉树字符串,
	//但要将空的结点换成#,但是它只读取第一个字符,
	//其余的都存在缓冲区里面,每递归一次读取一次
	
	if (ch == '#')//如果读取到#字符就将这个结点设为空
		tree = nullptr;
	else
	{
		tree = new struct binary_tree;//开辟一个结构体
		tree->ch = ch;//给结点数据赋值
		tree->lsubtree = found_tree(tree->lsubtree);//递归调用这个函数创建左子树
		tree->rsubtree = found_tree(tree->rsubtree);//递归调用这个函数创建右子树
	}

	return tree;
}

abdh###e##cf##g##
上面这个字符串转换过程:

char ch;
if(ch == ‘#’) ——>就将当前结点设为空
if(ch != ‘#’) ——> 就再开辟内存,继续创建左子树和右子树。

将上面图片中的序列输入进去,就可以创建一个二叉树了。

🌟三、遍历二叉树

1、先序遍历

先遍历根结点,再遍历左子树,再遍历右子树

在这里插入图片描述
依次访问,依次输出。
在这里插入图片描述
代码:

void preorder_tree(struct binary_tree* tree)
{
	if (tree)
	{
		cout << tree->ch;//输出结点数据
		preorder_tree(tree->lsubtree);//递归遍历左子树的数据
		preorder_tree(tree->rsubtree);//递归遍历右子树的数据
	}
}

这个遍历代码也是采用递归调用,递归用起来就是有点难理解,可是非常方便。

2、中序遍历

先遍历左子树,再遍历根结点,最后遍历右子树
在这里插入图片描述

代码:
·

void inorder_tree(struct binary_tree* tree)
{
	if (tree)
	{
		inorder_tree(tree->lsubtree);//递归遍历左子树的数据
		cout << tree->ch;//输出结点数据
		inorder_tree(tree->rsubtree);//递归遍历右子树的数据
	}
}

也是采用递归调用。
运行结果:在这里插入图片描述

3、后序遍历

先遍历左子树,再遍历右子树,最后遍历根结点

在这里插入图片描述
·
代码:
·

void epilogue_tree(struct binary_tree* tree)
{
	if (tree)
	{
		epilogue_tree(tree->lsubtree);//递归遍历左子树的数据
		epilogue_tree(tree->rsubtree);//递归遍历右子树的数据
		cout << tree->ch;//输出结点数据
	}
	
}

也是采用递归调用。

4、层次遍历

一层一层的遍历

在这里插入图片描述

但是层次遍历和上面有点不同,不能简单的调用递归来遍历,而是要借用到一个队列来辅助一下队列。代码如下:


int head = 0;//队列的头
int tile = 0;//队列的尾
struct binary_tree queue[SIZE];//定义一个队列

int is_full();//判断队列是否为满
int is_tem();//判断队列是否为空
void enqueue(struct binary_tree tree);//入列
struct binary_tree dequeue();//出列

int is_full()
{
	return (tile + 1) % SIZE == head;//尾结点+1等于首结点就为满
}

int is_tem()
{

	return head == tile;//尾结点等于首结点就为空
}

void enqueue(struct binary_tree tree)
{
	queue[tile] = tree;
	tile = (tile + 1) % SIZE;//更新尾结点的位置
}

struct binary_tree dequeue()
{
	struct binary_tree tree;
	tree = queue[head];
	head = (head + 1) % 100;
	return tree;
}

队列已经定义好了,然后在定义一个函数来进行层次遍历
代码:
·

void level_traverse(struct binary_tree* tree)
{
	struct binary_tree node;
	enqueue(*tree);//存入第一个根结点
	while (!is_tem())//只要队列不为空就一个循环
	{
		node = dequeue();//取出队列中第一个结点
		cout << node.ch;//输出结构体中的数据
		if (node.lsubtree)//如果左子树不为空,就将左子树存入队列
			enqueue(*node.lsubtree);
		if (node.rsubtree)//如果右子树不为空,就将左子树存入队列
			enqueue(*node.rsubtree);
	}
}

存入根结点,再看左子树和右子树是否存在,存在就存入,然后递归依次取出。

在这里插入图片描述

🌟四、二叉树的计算

1、计算二叉树的深度

什么是二叉树的深度:
就是左子树或者右子树的深度+1

在这里插入图片描述
代码实现


int depth_tree(struct binary_tree* tree)
{
	if (tree == nullptr)
		return 0;//树为空就返回0
	else
	{
		int depth1 =  depth_tree(tree->lsubtree);//获得左子树的深度
		int depth2 = depth_tree(tree->rsubtree);//获得右子树的深度
		return (depth1 > depth2) ? (depth1 + 1) : (depth2 + 1);
		//条件表达式,返回左子树和右子树中较大的深度+1
	}

也是递归调用,有点难理解,需要自己慢慢理解一下。

2、计算二叉树的叶子树

什么是叶子数:
在二叉树中度为零的数,度:二叉树的分叉数。 度为1就表示只有一个分叉,度为2就表示有两个分叉,经典的就是二叉树;度为0就是没有分叉,当前结点就是叶子,就比如我们生活中叶子不会再分叉一样。叶子数就是有多少个没有分叉的结点。

在这里插入图片描述

代码:

int leaf_tree(struct binary_tree* tree)
{
	if (tree == nullptr)
		return 0;//树为空就返回0
	else if (tree->lsubtree == nullptr && tree->rsubtree == nullptr)
		return 1;//只有根结点就返回1个叶子数
	else
		return leaf_tree(tree->lsubtree) + leaf_tree(tree->rsubtree);
		//递归调用遍历左子树和右子树的叶子数相加
		
}

if (tree == nullptr) ——> 如果树为空,就没有叶子数,返回0
else if (tree->lsubtree == nullptr && tree->rsubtree == nullptr)
如果只有根结点,没有其他分叉,叶子数那么就返回1。
else ——> 如果有左子树和右子树,那么遍历左子树和右子树再将它们的叶子数相加。

3、计算二叉树的结点数

计算二叉树的结点数,只需要将左子树的结点加上右子树的结点再加上根结点:
在这里插入图片描述

代码:

int node_tree(struct binary_tree* tree)
{
	if (tree == nullptr)
		return 0;//树为空就返回0
	else
		return node_tree(tree->lsubtree) + node_tree(tree->rsubtree) + 1;
		//递归调用遍历左子树和右子树的结点数相加,最后再加一个最上面的根结点
}

🌟五、总代码

---binary.h 
#pragma once

#ifndef BINARY_H
#define BINARY_H

#include<iostream>

#define SIZE 100

struct binary_tree
{
	char ch;//数据
	struct binary_tree* lsubtree, * rsubtree;//创建左子树和右子树
};

struct binary_tree* creat_tree(struct binary_tree*);//问询法创建二叉树
struct binary_tree* found_tree(struct binary_tree*);//补空法创建二叉树

void preorder_tree(struct binary_tree* );//显示先序遍历的结果
void inorder_tree(struct binary_tree* );//显示中序遍历的结果
void epilogue_tree(struct binary_tree* );//显示后序遍历的结果

int is_full();//判断队列是否为满
int is_tem();//判断队列是否为空
void enqueue(struct binary_tree tree);//入列
struct binary_tree dequeue();//出列

void level_traverse(struct binary_tree*);//显示层次遍历的结果

int depth_tree(struct binary_tree*);//计算二叉树的深度
int leaf_tree(struct binary_tree*);//计算二叉树的叶子数
int node_tree(struct binary_tree* tree);//计算二叉树的结点数


#endif 


----binary_tree.cpp

#include"binary.h"


int head = 0;//队列的头
int tile = 0;//队列的尾
struct binary_tree queue[SIZE];//定义一个队列

using std::cin;
using std::cout;

//问询法创建二叉树
struct binary_tree* creat_tree(struct binary_tree* tree)
{
	char c;
	tree = new struct binary_tree;//开辟结构体内存空间
	cout << "请输入一个数据:";
	cin >> tree->ch;//输入数据
	cin.sync();//这个函数的作用是清除缓存区,读取 '\n'换行
	cout << "是否为 " << tree->ch<< " 创建左子树:(y / n)";
	cin >> c;
	cin.sync();
	if (c == 'y')
		tree->lsubtree = creat_tree(tree->lsubtree);//调用递归创建左子树
	else
		tree->lsubtree = nullptr;//不创建就设为空
	cout << "是否为 " << tree->ch <<" 创建右子树:(y / n)";
	cin >> c;
	cin.sync();
	if (c == 'y')
		tree->rsubtree = creat_tree(tree->rsubtree);//调用递归创建右子树
	else
		tree->rsubtree = nullptr;//不创建设为空
	return tree;//返回根结点
}

//补空法创建二叉树
struct binary_tree* found_tree(struct binary_tree* tree)
{
	tree = new struct binary_tree;
	char ch;
	cin >> ch;
	//输入一串已经提前先序遍历好的二叉树字符串,但要将空的结点换成#,
	//但是它只读取第一个字符,其余的都存在缓冲区里面,每递归一次读取一次
	
	if (ch == '#')//如果读取到#字符就将这个结点设为空
		tree = nullptr;
	else
	{
		tree = new struct binary_tree;//开辟一个结构体
		tree->ch = ch;//给结点数据赋值
		tree->lsubtree = found_tree(tree->lsubtree);//递归调用这个函数创建左子树
		tree->rsubtree = found_tree(tree->rsubtree);//递归调用这个函数创建右子树
	}

	return tree;
}

void preorder_tree(struct binary_tree* tree)
{
	if (tree)
	{
		cout << tree->ch;//输出结点数据
		preorder_tree(tree->lsubtree);//递归遍历左子树的数据
		preorder_tree(tree->rsubtree);//递归遍历右子树的数据
	}
}

void inorder_tree(struct binary_tree* tree)
{
	if (tree)
	{
		inorder_tree(tree->lsubtree);//递归遍历左子树的数据
		cout << tree->ch;//输出结点数据
		inorder_tree(tree->rsubtree);//递归遍历右子树的数据
	}
}

void epilogue_tree(struct binary_tree* tree)
{
	if (tree)
	{
		epilogue_tree(tree->lsubtree);//递归遍历左子树的数据
		epilogue_tree(tree->rsubtree);//递归遍历右子树的数据
		cout << tree->ch;//输出结点数据
	}
	
}


void level_traverse(struct binary_tree* tree)
{
	struct binary_tree node;
	enqueue(*tree);//存入第一个根结点
	while (!is_tem())
	{
		node = dequeue();//取出队列中第一个结点
		cout << node.ch;//输出结构体中的数据
		if (node.lsubtree)//如果左子树不为空,就将左子树存入队列
			enqueue(*node.lsubtree);
		if (node.rsubtree)//如果右子树不为空,就将左子树存入队列
			enqueue(*node.rsubtree);
	}
}

int is_full()
{
	return (tile + 1) % SIZE == head;//尾结点+1等于首结点就为满
}

int is_tem()
{

	return head == tile;//尾结点等于首结点就为空
}

void enqueue(struct binary_tree tree)
{
	queue[tile] = tree;
	tile = (tile + 1) % SIZE;//更新尾结点的位置
}

struct binary_tree dequeue()
{
	struct binary_tree tree;
	tree = queue[head];//头结点的位置出队列
	head = (head + 1) % 100;
	return tree;
}

int depth_tree(struct binary_tree* tree)
{
	if (tree == nullptr)
		return 0;//树为空就返回0
	else
	{
		int depth1 =  depth_tree(tree->lsubtree);//获得左子树的深度
		int depth2 = depth_tree(tree->rsubtree);//获得右子树的深度
		return (depth1 > depth2) ? (depth1 + 1) : (depth2 + 1);
		//条件表达式,返回左子树和右子树中较大的深度+1
	}
}

int leaf_tree(struct binary_tree* tree)
{
	if (tree == nullptr)
		return 0;//树为空就返回0
	else if (tree->lsubtree == nullptr && tree->rsubtree == nullptr)
		return 1;//只有根结点就返回1个叶子数
	else
		return leaf_tree(tree->lsubtree) + leaf_tree(tree->rsubtree);
		//递归调用遍历左子树和右子树的叶子数相加
		
}

int node_tree(struct binary_tree* tree)
{
	if (tree == nullptr)
		return 0;//树为空就返回0
	else
		return node_tree(tree->lsubtree) + node_tree(tree->rsubtree) + 1;
		//递归调用遍历左子树和右子树的结点数相加,最后再加一个最上面的根结点
}


-----main.cpp

#include"binary.h"
using namespace std;

int main()
{
	struct binary_tree *maker = nullptr;//创建一个根结点
   // maker = creat_tree(maker);
   //问询法创建,作为参数传进去,并把根结点返回给自己
    cout << "请输入带有补孔('#')的先序遍历出来的数据:";
	maker = found_tree(maker);//用补空法创建二叉树
	
	preorder_tree(maker);//显示先序遍历
	cout << endl;

	inorder_tree(maker);//显示中序遍历
	cout << endl;

	epilogue_tree(maker);//显示后序遍历
	cout << endl;

	level_traverse(maker);//层次遍历
	cout << endl;
	
	cout << depth_tree(maker) << endl;//显示深度
	cout << leaf_tree(maker) << endl;//显示叶子数
	cout << node_tree(maker) << endl;//显示结点数
	return 0;
}

还有一些操作等我慢慢补齐。有用的话点个赞吧。

  • 10
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小白学编程*

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值