文件结构 :
文件名字 | 用途 |
CmakeList.txt | cmake文件 |
how.md | 简述思路以及其他说明 |
main.cpp | 主测试程序 |
Tree.cpp | 核心实现文件 |
Tree.h | 核心头文件 |
注意 : test.md是对二叉树可视化的markdown文件,程序会自动生成关于二叉树样貌的mermaid流程图代码,请放入markdown文件中
Tree.cpp
//
// Created by A Luck Boy on 2023/1/31.
//
#include "Tree.h"
BinaryTree::BinaryTree(const int *args) {
H = args[0];
int N = (int)pow(2, H)-1;
data = new int[N];
// 这里就不检测层数是否不小于1
// 同时也不检测一个数组按照从索引1与树状图对应,是否是二叉树,比如说 : 树的某结点不存在,其后面结点也不存在
// 但是我的程序可以自动修复用户的数据,不满足添加的换成0(即不存在结点),用户的数据存在data了,正确的存在_true_data里面
// 另外,如果你给的层数为k,那么最大结点数目为2^k-1个,但是如果你的数组数据超过了,代码只会取2^k-1个;
// 不够的话,程序可能会分配内存中的脏数据,导致程序不正确
for (int i=0;i<N;i++){
data[i] = args[1+i];
}
_true_data = new int[N];
for (int i=1;i<H;i++){
// 第i层最多有2^(i-1)个
for (int j=(int) pow(2, i-1);j<(int) pow(2, i);j++){
int pa = data[j-1];
int ch1 = data[ 2 * j - 1];
int ch2 = data[ 2 * j];
if (pa == 0){
data [2 * j - 1] = 0;
data[2 * j] = 0;
_true_data[j-1] = 0;
_true_data[j * 2-1] = 0;
_true_data[j * 2] = 0;
} else if (pa != 0 && ch1 == 0 && ch2 != 0){
_true_data[j-1] = pa;
_true_data[j * 2-1] = 0;
_true_data[j * 2] = ch2;
} else if (pa != 0 && ch2 == 0 && ch1 != 0){
_true_data[j-1] = pa;
_true_data[j * 2-1] = ch1;
_true_data[j * 2] = 0;
} else if (pa != 0 && ch1 == 0 && ch2 == 0){
_true_data[j-1] = pa;
_true_data[j * 2-1] = 0;
_true_data[j * 2] = 0;
} else // here is pa != 0 && ch1 != 0 && ch2 != 0
{
_true_data[j-1] = pa;
_true_data[j * 2-1] = ch1;
_true_data[j * 2] = ch2;
}
}
}
}
BinaryTree::~BinaryTree() {
delete[] data;
delete[] _true_data;
}
void BinaryTree::show() {
cout << "```mermaid" << endl << "graph TD" << endl;
for (int i=1;i<H;i++){
// 第i层最多有2^(i-1)个
for (int j=(int) pow(2, i-1);j<(int) pow(2, i);j++){
// 按照正确的结点返回
cout << j << "[" << data[j-1] << "]" << "-->" << j * 2 << "[" << _true_data[ 2 * j - 1] << "]" <<endl;
cout << j << "[" << data[j-1] << "]" << "-->" << j * 2 + 1 << "[" << _true_data[ 2 * j] << "]" <<endl;
}
}
cout << "```"<<endl;
}
int BinaryTree::getSize() const {
int _size =0;
for (int i=0;i< pow(2, H)-1;i++){
if (_true_data[i ] != 0) {
_size++;
}
}return _size;
}
int BinaryTree::depth() const {
// 考虑一个问题 : 如果传入不合法数据,导致最后一层全是0,即不存在结点
// 解决 : 第k行的0数目不等于2^(k-1)个,说明此行存在结点
int _true_H = 0;
for (int i=1;i<1+H;i++) {
int line_0 = 0;
for (int j = (int) pow(2, i - 1); j < (int) pow(2, i); j++) {
if (_true_data[j - 1] != 0) {
_true_H++;
break;
} else{
line_0++;
if (line_0 == (int ) pow(2, i-1)){
return _true_H;
} else{ continue;}
}
}
}
}
int BinaryTree::getValue(int i, int j) const {
// 先判断是否在范围内,不在就返回0(代表不支持结点,下同)
if (pow(2, i-1) < j || i > H){
return 0;
} else{
return _true_data[(int ) pow(2, i-1)+j-2];
}
}
int BinaryTree::getValue(int k) const {
// 先看看超出范围了吗
if (pow(2, H)-1 < k){
return 0;
} else{
return _true_data[k-1];
}
}
bool BinaryTree::isFullTree() const {
return getSize() == (int) pow(2, H)-1;
}
bool BinaryTree::isCompleted() const {
// 只需要从_true_data中的尾部开始查看
// 从尾部开始连续n个是0(n可以为0,0代表不存在),其他不是
int default_not = 0;
for (int i=1;i<1+H;i++) {
for (int j = (int) pow(2, i - 1); j < (int) pow(2, i); j++) {
if (_true_data[j -1] != 0){
default_not++;
}else{
return getSize() == default_not;
}
}
}
return default_not == getSize();
}
BinaryTree BinaryTree::childTree(char direct) const {
// direct必须是l和r之一,否则程序强制退出
// 不考虑以下特例 ;只有根的树(H=1)
if (direct == 'r'){
int *_dat = new int[(int ) pow(2, H-1)];
// 获取右子树所有数据,包含不存在的0,下同
_dat[0] = H - 1;
int index = 1;
for (int i=2;i<H+1;i++){
for (int j=(int) (pow(2, i-1) + pow(2, i-2));j<(int ) pow(2, i);j++){
_dat[index] = _true_data[j-1];
cout << " J == " << j << " " ;
index++;
}cout <<endl;
}
return BinaryTree(_dat);
} else if (direct == 'l'){
int *_dat = new int[(int ) pow(2, H-1)];
// 获取右子树所有数据,包含不存在的0,下同
_dat[0] = H - 1;
int index = 1;
for (int i=2;i<H+1;i++){
for (int j=(int)pow(2, i-1);j<(int )(pow(2, i-2)+pow(2, i-1));j++){
_dat[index] = _true_data[j-1];
index++;
}
}
return BinaryTree(_dat);
}else{
cout << "The parameter direct must be either r or l"<<endl;
exit(EXIT_FAILURE);
}
}
Tree.h
//
// Created by A Luck Boy on 2023/1/31.
//
// 顺序存储实现二叉树
// 但是有明显的缺点 : 对空结点需要存储,这里用0代表此结点不放值
#include <iostream>
#include <cmath>
using namespace std;
#define Waring cout << "此处代码错误!\n";
// 数组索引1开始数据的位置,代表结点的位置
class BinaryTree{
private:
// 用户存入的数据(但不一定是符合二叉树的值)
int *data;
// 真值,符合二叉树
int *_true_data;
// 用户传入的最大层数(但不一定是真实的,但是不影响其他核心程序)
int H;
BinaryTree * leftTree;
BinaryTree * rightTree;
public:
// methods
// arg是数组,按照满二叉树对应位置布局,第0个索引处放入层数(算入根结点所在层),第一个索引值代表根结点
explicit BinaryTree(const int *args);
~BinaryTree();
// 遍历
// 生成可在markdown中展示的mermaid代码,复制下来就可以在markdown中查看树形结构布局
void show();
// 获取合理结点
[[nodiscard]] int getSize() const;
// 获取真实的深度
[[nodiscard]] int depth() const;
// 访问第i层,第j个结点,或者说,按照满二叉树的顺序,访问第k个元素
// i, j, k都是正常访问位置,不是索引位置
// 比如说 二叉树 :
// 1
// 11 21
// 10 15 2 23
// 按照第一种方法 :第2行,第1个是11
// 按照第二种方法 :第4个元素是10
[[nodiscard]] int getValue(int i, int j) const;
[[nodiscard]] int getValue(int k) const;
// 判断是不是满二叉树
[[nodiscard]] bool isFullTree() const;
// 判断是不是完全二叉树
[[nodiscard]] bool isCompleted() const;
// 返回左(右)子树
[[nodiscard]] BinaryTree childTree(char direct) const;
};
main.cpp
//
// Created by A Luck Boy on 2023/1/31.
//
#include "Tree.h"
int main() {
// 传参方式是数组,数组第一个数必须是层数,其他是结点数据,不存在结点用0表示
int array[] = {4,9, 0, 5, 78, 0, 6, 0, 5
, 9, 7, 8, 10, 0, 0, 0};
BinaryTree tree(array);
tree.show();
// 查看合理的结点个数 (答案是7,和mermaid流程图显示的一样,通过计算也是如此)
cout << "total : " << tree.getSize() <<endl;
// 两种方式获取值
cout << tree.getValue(1) << endl;
cout << tree.getValue(1, 1) << endl;
cout << tree.getValue(6) << endl;
cout << tree.getValue(3, 3) << endl;
cout << tree.getValue(12) << endl;
cout << tree.getValue(4, 5) << endl;
// 创建一个满二叉树
int arr[] = {3, 1, 2, 3, 4, 5, 6, 7};
BinaryTree fullTree(arr);
fullTree.show();
if (fullTree.isFullTree()) {
cout << "确实是满树\n";
} else{
Waring
}
// 判断真实的深度
// 下面这个二叉树实际只有2层
int arr2[] = {4,
2,
10, 0,
0,0, 6, 0,
0,0, 0, 0, 0, 0, 0, 0};
auto tree1 = BinaryTree(arr2);
tree1.show();
cout << tree1.depth()<<endl;
// 创建一个完全二叉树
int arr1[] = {3, 1, 2, 1, 4, 5, 0, 0};
BinaryTree completeTree(arr1) ;
completeTree.show() ;
if (completeTree.isCompleted()){
cout << "确实是完全树\n";
} else{Waring}
// 创建一个不是完全二叉树的
int arr3[] ={3, 1, 2, 3, 0, 1, 4, 5};
BinaryTree not_completeTree(arr3) ;
not_completeTree.show();
if (!not_completeTree.isCompleted()){
cout << "确实不是完全树\n";
} else{Waring}
// 获取左右子树,基于第一个实例
auto rT = tree.childTree('r');
rT.show();
auto lT = tree.childTree('l');
lT.show();
}
终端运行结果
```mermaid
graph TD
1[9]-->2[0]
1[9]-->3[5]
2[0]-->4[0]
2[0]-->5[0]
3[5]-->6[6]
3[5]-->7[0]
4[0]-->8[0]
4[0]-->9[0]
5[0]-->10[0]
5[0]-->11[0]
6[6]-->12[10]
6[6]-->13[0]
7[0]-->14[0]
7[0]-->15[0]
```
total : 4
9
9
6
6
10
10
```mermaid
graph TD
1[1]-->2[2]
1[1]-->3[3]
2[2]-->4[4]
2[2]-->5[5]
3[3]-->6[6]
3[3]-->7[7]
```
确实是满树
```mermaid
graph TD
1[2]-->2[10]
1[2]-->3[0]
2[10]-->4[0]
2[10]-->5[0]
3[0]-->6[0]
3[0]-->7[0]
4[0]-->8[0]
4[0]-->9[0]
5[0]-->10[0]
5[0]-->11[0]
6[0]-->12[0]
6[0]-->13[0]
7[0]-->14[0]
7[0]-->15[0]
```
2
```mermaid
graph TD
1[1]-->2[2]
1[1]-->3[1]
2[2]-->4[4]
2[2]-->5[5]
3[1]-->6[0]
3[1]-->7[0]
```
确实是完全树
```mermaid
graph TD
1[1]-->2[2]
1[1]-->3[3]
2[2]-->4[0]
2[2]-->5[1]
3[3]-->6[4]
3[3]-->7[5]
```
确实不是完全树
J == 3
J == 6 J == 7
J == 12 J == 13 J == 14 J == 15
```mermaid
graph TD
1[5]-->2[6]
1[5]-->3[0]
2[6]-->4[10]
2[6]-->5[0]
3[0]-->6[0]
3[0]-->7[0]
```
```mermaid
graph TD
1[0]-->2[0]
1[0]-->3[0]
2[0]-->4[0]
2[0]-->5[0]
3[0]-->6[0]
3[0]-->7[0]
```
可视化结果
源代码 ;
BinaryTree · PythonnotJava/自己实现的数据结构与算法合集 - 码云 - 开源中国 (gitee.com)