文章目录
🌟一、二叉树的简述
树:通俗的说就是一个结点会分出多个分支;而二叉树也就是一个结点只能分出两个分叉。如:
上面就是一个简单的二叉树结构,每个数都有一个总的根结点,也就是 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;
}
还有一些操作等我慢慢补齐。有用的话点个赞吧。