二叉链存二叉树

预备知识

  • 指针的熟练掌握

  • template模板的知识
  • 二叉树的基本知识

感谢:

代码参考:CSDN博主「云雨澄枫」的原创文章

链接

代码解析

结构体 BiNode

template<class T>
struct BiNode{
T data;
BiNode<T> *lchild,*rchild;
};

Node 结点

这个结构体就是用来存储二叉链的每一个节点的

  • data

表示这个节点所存的值

  • *lchild & *rchild

表示指向 左子树 和 右子树 的指针

这样的结构能很好地存下二叉树:

(具体的建树方法会放在后面解释)

类 BiTree

template <class T>
class BiTree{
public:
//构造 & 折构
BiTree(){root = Creat(root);}
~BiTree(){Release(root);}
//遍历
void PreOrder(){PreOrder(root);}
void InOrder(){InOrder(root);}
void PostOrder(){PostOrder(root);}
void LeverOrder();
//树的深度
int Depth(){Depth(root);}
//叶子结点数量
void CaculateLeafNum(){CaculateLeafNum(root);}
//交换左右子树
void swap(){swap(root);}
private:
BiNode<T> *root;//<-根节点在这里
BiNode<T> *Creat(BiNode<T> *bt);
void Release(BiNode<T> *bt);
void PreOrder(BiNode<T> *bt);
void InOrder(BiNode<T> *bt);
void PostOrder(BiNode<T> *bt);
int Depth(BiNode<T> *root);
void CaculateLeafNum(BiNode<T>* root);
void swap(BiNode<T> *root);
};

这便是树的主体

一个 BiTree 的类就代表一颗二叉树

  • public

在这里定义了一些之后会用到的函数以及构造函数和折构函数

这里知道有这些东西即可

  • private

这里则有一些 public 会用到的函数

以及这个树的根节点

构造函数 & 折构函数

  1. 构造函数

在类中 是这样写的:

BiTree(){root = Creat(root);}

构造这个树是通过 Creat 这个函数来实现的

Creat_Code:

template <class T>
BiNode<T> *BiTree<T>::Creat(BiNode<T> *bt)
{
T t;
cin >> t;
if(t == '#')
bt = NULL;
else {
bt = new BiNode<T>;
bt->data = t;
bt->lchild = Creat(bt->lchild);
bt->rchild = Creat(bt->rchild);
}
return bt;
}

在讲构造函数之前

还需要提一嘴其输入方式

这里是采用前序遍历的方式进行输入

同时还需要以 "#" 输入叶子结点的空子结点

例子:

cin:

ABD#G###CE##F##

  • 图中的序号即代表 cin 中的第几个字符

知道了输入的顺序之后

代码就变得好理解很多了

创建一个结点的时候

首先输入这个结点的值(就是 t)

若其值是 "#" 那就不继续往下面创造新结点了

直接让 bt 为 空指针

并把这个指针返回

若其值不为 "#"

那说明后面可能还有结点需要添加

就继续在左右子结点上调用 Creat函数

bt = new BiNode<T>;

这行代码可能还需要解释一下

new 是用来开辟新的内存空间的关键字

这里相当于开辟了一个新的结点结构体

而 bt 存下了这个结点结构体的指针

  1. 折构函数
~BiTree(){Release(root);}

折构函数和构造函数一样是用类名来作为函数名的

不过需要在前面加一个 "~"

这个函数调用了 Release 这个函数

template<class T>
void BiTree<T>::Release(BiNode<T> *bt){
if(bt != NULL){
Release(bt->lchild);
Release(bt->rchild);
delete bt;
}
}

delete 用于删除内存的关键词

这个函数因该很好理解

就是从上往下搜

从下往上删

就不过多解释了

遍历函数

这个分两部分将

第一部分 : 前/中/后序遍历

这些代码原理相同 就随便挑一个讲好了

template <class T>
void BiTree<T>::PreOrder(BiNode<T> *bt){
if (bt == NULL)
return;
else {
cout << bt->data;
PreOrder(bt->lchild);
PreOrder(bt->rchild);
}
}

从 root结点 开始搜索

如果不是空结点 那就直接输出

然后往两边找

决定前中后的只在于

输出与往两侧搜索语句的顺序

第二部分 : 层序遍历

还是用这个图

Code:

template<class T>
void BiTree<T>::LeverOrder(){
int front = -1,rear = -1;
BiNode<T> *Q[100]; if(root == NULL) return;
Q[++rear] = root;
while(front != rear){
BiNode<T> *q = Q[++front];
cout << q->data;
if(q->lchild != NULL) Q[++rear] = q->lchild;
if(q->rchild != NULL) Q[++rear] = q->rchild;
}
}

这里是用一个数组和两个变量来实现了队列的功能

接下来我们来模拟一下

这样就可以实现层序遍历了

求树的深度

Code:

template<class T>
int BiTree<T>::Depth(BiNode<T> *root){
int hl,hr;
if(root == NULL)
return 0;
else{
hl = Depth(root->lchild);
hr = Depth(root->rchild);
return max(hl,hr) + 1;
}
}

深度要找的是最深的叶子结点的层数

因此直接左右找取最大值即可

求叶子结点数量

Code:

template<class T>
void BiTree<T>::CaculateLeafNum(BiNode<T> *root){
if(!root) return;
if(root->lchild == NULL && root->rchild == NULL) Leaf_Count++;
CaculateLeafNum(root->lchild);
CaculateLeafNum(root->rchild);
}

首先我们要知道叶子结点的特征:

没有儿子节点

(就是所有儿子节点都为 NULL)

所以左右搜找到无儿子 +1 即可

交换左右子树

template<class T>
void BiTree<T>::swap(BiNode<T> *root){
BiNode<T> *temp;
if(root == NULL)
return;
else{
temp = root->lchild;
root->lchild = root->rchild;
root->rchild = temp;
swap(root->lchild);
swap(root->rchild);
}
}

由于二叉链的本质就是指针的堆叠

因此直接交换指针的存值就可以了

Code

#include<bits/stdc++.h>
using namespace std; template<class T>
struct BiNode{
T data;
BiNode<T> *lchild,*rchild;
}; template <class T>
class BiTree{
public:
//构造 & 折构
BiTree(){root = Creat(root);}
~BiTree(){Release(root);}
//遍历
void PreOrder(){PreOrder(root);}
void InOrder(){InOrder(root);}
void PostOrder(){PostOrder(root);}
void LeverOrder();
//树的深度
int Depth(){Depth(root);}
//叶子结点数量
void CaculateLeafNum(){CaculateLeafNum(root);}
//交换左右子树
void swap(){swap(root);}
private:
BiNode<T> *root;
BiNode<T> *Creat(BiNode<T> *bt);
void Release(BiNode<T> *bt);
void PreOrder(BiNode<T> *bt);
void InOrder(BiNode<T> *bt);
void PostOrder(BiNode<T> *bt);
int Depth(BiNode<T> *root);
void CaculateLeafNum(BiNode<T>* root);
void swap(BiNode<T> *root);
};
int Leaf_Count = 0; template <class T>
BiNode<T> *BiTree<T>::Creat(BiNode<T> *bt)
{
T t;
cin >> t;
if(t == '#')
bt = NULL;
else {
bt = new BiNode<T>;
bt->data = t;
bt->lchild = Creat(bt->lchild);
bt->rchild = Creat(bt->rchild);
}
return bt;
}
template<class T>
void BiTree<T>::Release(BiNode<T> *bt){
if(bt != NULL){
Release(bt->lchild);
Release(bt->rchild);
delete bt;
}
} template <class T>
void BiTree<T>::PreOrder(BiNode<T> *bt){
if(bt == NULL)
return;
else {
cout << bt->data;
PreOrder(bt->lchild);
PreOrder(bt->rchild);
}
} template<class T>
void BiTree<T>::InOrder(BiNode<T> *bt){
if(bt == NULL)
return;
else{
InOrder(bt->lchild);
cout << bt->data;
InOrder(bt->rchild);
}
} template<class T>
void BiTree<T>::PostOrder(BiNode<T> *bt){
if(bt == NULL)
return;
else{
PostOrder(bt->lchild);
PostOrder(bt->rchild);
cout << bt->data;
}
} template<class T>
void BiTree<T>::LeverOrder(){
int front = -1,rear = -1;
BiNode<T> *Q[100]; if(root == NULL) return;
Q[++rear] = root;
while(front != rear){
BiNode<T> *q = Q[++front];
cout << q->data;
if(q->lchild != NULL) Q[++rear] = q->lchild;
if(q->rchild != NULL) Q[++rear] = q->rchild;
}
} template<class T>
int BiTree<T>::Depth(BiNode<T> *root){
int hl,hr;
if(root == NULL)
return 0;
else{
hl = Depth(root->lchild);
hr = Depth(root->rchild);
return max(hl,hr) + 1;
}
} template<class T>
void BiTree<T>::CaculateLeafNum(BiNode<T> *root){
if(!root) return;
if(root->lchild == NULL && root->rchild == NULL) Leaf_Count++;
CaculateLeafNum(root->lchild);
CaculateLeafNum(root->rchild);
} template<class T>
void BiTree<T>::swap(BiNode<T> *root){
BiNode<T> *temp;
if(root == NULL)
return;
else{
temp = root->lchild;
root->lchild = root->rchild;
root->rchild = temp;
swap(root->lchild);
swap(root->rchild);
}
} int main(){
BiTree<char>* bitree=new BiTree<char>;
cout<<"前序遍历";
bitree->PreOrder();
cout << endl;
cout<<"中序遍历";
bitree->InOrder();
cout << endl;
cout<<"后序遍历";
bitree->PostOrder();
cout << endl;
cout<<"层序遍历";
bitree->LeverOrder();
cout << endl;
cout<<"深度:"<<bitree->Depth()<<endl;
bitree->CaculateLeafNum();
cout<<"叶子结点个数:"<<Leaf_Count<<endl;
bitree->swap();
cout<<"左右子树交换后的层序:";
bitree->LeverOrder();
return 0;
}

[C++]二叉链-二叉树存储的更多相关文章

  1. 二叉搜索树 & 二叉树 & 遍历方法

    二叉搜索树 & 二叉树 & 遍历方法 二叉搜索树 BST / binary search tree https://en.wikipedia.org/wiki/Binary_searc ...

  2. HDU3791二叉搜索树(二叉树)

    Problem Description 判断两序列是否为同一二叉搜索树序列   Input 开始一个数n,(1<=n<=20) 表示有n个需要判断,n= 0 的时候输入结束.接下去一行是一 ...

  3. 二叉苹果树 - 二叉树树型DP

    传送门 中文题面: 题目描述 有一棵苹果树,如果树枝有分叉,一定是分 2 叉(就是说没有只有 1 个儿子的结点,这棵树共有N 个结点(叶子点或者树枝分叉点),编号为1-N,树根编号一定是1. 我们用一 ...

  4. 数据结构图文解析之:二叉堆详解及C++模板实现

    0. 数据结构图文解析系列 数据结构系列文章 数据结构图文解析之:数组.单链表.双链表介绍及C++模板实现 数据结构图文解析之:栈的简介及C++模板实现 数据结构图文解析之:队列详解与C++模板实现 ...

  5. 数据结构-二叉搜索树的js实现

    一.树的相关概念 1.基本概念 子树 一个子树由一个节点和它的后代构成. 节点的度 节点所拥有的子树的个数. 树的度 树中各节点度的最大值 节点的深度 节点的深度等于祖先节点的数量 树的高度 树的高度 ...

  6. 二叉堆 及 大根堆的python实现

    Python 二叉堆(binary heap) 二叉堆是一种特殊的堆,二叉堆是完全二叉树或者是近似完全二叉树.二叉堆满足堆特性:父节点的键值总是保持固定的序关系于任何一个子节点的键值,且每个节点的左子 ...

  7. javascript实现二叉搜索树

    在使用javascript实现基本的数据结构中,练习了好几周,对基本的数据结构如 栈.队列.链表.集合.哈希表.树.图等内容进行了总结并且写了笔记和代码. 在 github中可以看到  点击查看,可以 ...

  8. 二叉树的二叉链表存储结构及C++实现

    前言:存储二叉树的关键是如何表示结点之间的逻辑关系,也就是双亲和孩子之间的关系.在具体应用中,可能要求从任一结点能直接访问到它的孩子. 一.二叉链表 二叉树一般多采用二叉链表(binary linke ...

  9. [数据结构]——二叉树(Binary Tree)、二叉搜索树(Binary Search Tree)及其衍生算法

    二叉树(Binary Tree)是最简单的树形数据结构,然而却十分精妙.其衍生出各种算法,以致于占据了数据结构的半壁江山.STL中大名顶顶的关联容器--集合(set).映射(map)便是使用二叉树实现 ...

  10. C#实现二叉树--二叉链表结构

    二叉树的简单介绍 关于二叉树的介绍请看这里 : 二叉树的简单介绍 http://www.cnblogs.com/JiYF/p/7048785.html 二叉链表存储结构: 二叉树的链式存储结构是指,用 ...

随机推荐

  1. docker 公有仓库与私有仓库常见操作

    本文为博主原创,转载请注明出处: 自建一个Docker仓库,可以使用Docker官方提供的开源项目Docker Registry.以下是一些基本步骤: 安装Docker Registry: 在服务器上 ...

  2. C语言基础--数组详细说明

    目录 一.什么是数组 二.一维数组 1.一维数组创建 2.一维数组的使用 2.1 索引值 2.2 遍历数组 2.3 如何使用sizeof()计算出数组的长度 三.二维数组 1.二维数组的创建 2.二维 ...

  3. zabbix 可计算监控项使用 last() 与 avg() 的区别

    使用zabbix的可计算监控项时遇到的问题 在agent异常退出的情况下,使用last()的监控项依然在产出数据! 分析解决 last()函数会跳过空值,取最后一个有效值计算.遍查文档,只有用avg( ...

  4. Windows 下搭建 Appium + Android+python 自动化测试环境

    前言 本来并不打算写这么一篇文章,但是实践下来发现网上的各种教程里大致有两个问题.一是文章有些跟不上时代,目前android开发和测试的技术更新都比较快,内容有些过期.二是细节部分不是太完整,拼拼凑凑 ...

  5. 最全linux基础知识

    linux基础知识 [root@localhost ~]# 各位置表示什么意识 root:表示用户名 (现在的用户是root切换为test便是张三) localhost:表示主机名 (当前主机名切换为 ...

  6. redux的三个概念与三大核心

    1.什么是redux?一个组件里可能会有很多的状态,比如控制某个内容显示的flag,从后端获取的展示数据,那么这些状态可以在自己的单个页面进行管理,也可以选择别的管理方式,redux就是是一种状态管理 ...

  7. 2023NepCTF-RE部分题解

    2023NepCTF-RE部分题解 九龙拉棺 过反调试 很容易发现 void __stdcall sub_401700() 里面有tea的痕迹 接出来发现只是前半部分 #include <std ...

  8. 5、Spring之bean的作用域和生命周期

    5.1.bean的作用域 5.1.1.单例(默认且常用) 5.1.1.1.配置bean 注意:当bean不配置scope属性时,默认是singleton(单例) <?xml version=&q ...

  9. 震惊!CSS 也能实现碰撞检测?

    本文,我们将一起学习,使用纯 CSS,实现如下所示的动画效果: 上面的动画效果,非常有意思,核心有两点: 小球随机做 X.Y 方向的直线运动,并且能够实现碰撞到边界的时候,实现反弹效果 小球在碰撞边界 ...

  10. 浅析三维模型OBJ格式轻量化处理常见问题与处理措施

    浅析三维模型OBJ格式轻量化处理常见问题与处理措施 在三维模型OBJ格式轻量化处理过程中,可能会遇到一些问题.以下是一些常见问题以及相应的解决方法: 1.文件大小过大: OBJ格式的三维模型文件通常包 ...