二叉链存二叉树

预备知识

  • 指针的熟练掌握

  • 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. 跟运维学 Linux - 01

    跟运维学 Linux - 01 运维的诞生 运维工程师有很多叫法:系统运维.Linux 工程师.系统管理员... 网管可以说是运维工程师最早的雏形.在个人电脑未普及时,大家去网吧玩游戏. 玩家:&qu ...

  2. 我开源了团队内部基于SpringBoot Web快速开发的API脚手架v1.6.0更新

    什么是 rest-api-spring-boot-starter rest-api-spring-boot-starter 适用于SpringBoot Web API 快速构建让开发人员快速构建统一规 ...

  3. 一文理解GIT的代码冲突

    对于GIT,不知道有没有人和我一样,很长时间都是小心翼翼.紧张兮兮,生怕一不小心,自己辛苦写的代码没了. 特别是代码冲突,更是难到我无法理解,每次都要求助于百度,跟着人家的教程一步步解决,下一次还是这 ...

  4. 2023-08-22:请用go语言编写。给定一个长度为N的正数数组,还有一个正数K, 返回有多少子序列的最大公约数为K。 结果可能很大,对1000000007取模。 1 <= N <= 10^5, 1

    2023-08-22:请用go语言编写.给定一个长度为N的正数数组,还有一个正数K, 返回有多少子序列的最大公约数为K. 结果可能很大,对1000000007取模. 1 <= N <= 1 ...

  5. MIT6.s081/6.828 lectrue07:Page faults 以及 Lab5 心得

    本篇博客主要是复习 MIT6.s081/6.828 lectrue07:Page faults 以及记录 Lab5 :COW fork 的心得 值得一提的是,2020 年之前的版本第 5 个 lab ...

  6. 【译】在 Visual Studio 中处理图像变得更容易了

    任何 Web.桌面或移动开发人员都经常使用图像.你可以从 C#.HTML.XAML.CSS.C++.TypeScript 甚至代码注释中引用它们.有些图像是本地的,有些存在于线上或网络共享中,而其他图 ...

  7. 怎么选择API接口来获取自己想要的数据

    在今天的数字时代,数据变得越来越重要,API接口也成为了获取数据的一种重要方式.无论是开发自己的应用程序还是进行市场营销,数据的获取都是非常必要的.但是,如何选择API接口来获取自己想要的数据呢? 以 ...

  8. Web服务器部署上线的踩坑流程回顾与知新

    5月份时曾部署上线了C++的Web服务器,温故而知新,本篇文章梳理总结一下部署流程知识: 最初的解决方案:https://blog.csdn.net/BinBinCome/article/detail ...

  9. 微信小程序 setData accepts an Object rather than some undefined 解决办法

    问题 setData accepts an Object rather than some undefined setData接受一个对象而不是一些定义 让我猜猜, 你一定是在加载index页面(首页 ...

  10. 如何get一个终身免费续期的定制数字人?

    想拥有一个"数字分身" 吗?给你一个终身免费续期的特权. 定制周期长?训练.运营成本高?成片效果生硬?无法应用于实际场景? 随着AIGC技术的快速发展,虚拟数字人的生成效率不断提高 ...