强烈建议小白去B站搜索:懒猫老师
二叉树可以通过顺序存储结构、链式存储结构、二维数组、邻接表的结构来存储,本文为较为简单直观,容易理解的链式存储结构的实现 。二叉链表的结构如图所示。
首先定义一个节点结构体BiNode,data为数据域(可以改为模板类,能够兼容所有数据类型,此处使用char类型只做简单实现),lchild和rchild分别指向左右孩子。
struct BiNode
{
char data;
BiNode* lchild;
BiNode* rchild;
};
然后定义二叉树类BiTree,包含有参、无参构造函数,析构函数,以及四种遍历函数。定义的root根节点为私有成员变量,所以此处public的下的函数是不能直接对root进行访问的,public的函数再次调用private中的函数去对二叉树进行各种操作。这么写的好处是让类完全封装起来,只留下外部调用接口,安全性更高(我也不太懂,自己感觉的)。
class BiTree
{
private:
BiNode* root;//指向根节点的头指针
public:
BiTree(string &s) { root = Creat(root,s); };//构造函数,建立一颗二叉树
BiTree() { root = Creat(root); };//构造函数,建立一颗二叉树
~BiTree(){ Release(root);};//析构函数
void PreOrder() { PreOrder(root); };//前序遍历二叉树
void InOrder() { InOrder(root); }; //中序遍历二叉树
void PostOrder() { PostOrder(root); };//后序遍历二叉树
void LeverOrder(); //层序遍历二叉树
private:
BiNode* Creat(BiNode* bt);
BiNode* Creat(BiNode* bt, string& s);//构造函数调用
void Release(BiNode* bt);//析构函数调用
void PreOrder(BiNode* bt);//前序遍历函数调用
void InOrder(BiNode* bt);//中序遍历函数调用
void PostOrder(BiNode* bt);//后序遍历函数调用
};
无参构造函数直接返回空树,有参构造函数通过Public中的BiTree()调用的时候只需要有一个string类型变量s做参数即可,s为字符串。s为所需要二叉树的扩展二叉树的前序遍历序列。(为了建立一颗二叉树,将二叉树中的每个节点的空指针引出一个虚节点,其值为一个特定值,如“#”,以标识其为空,把这样处理后的二叉树称为原二叉树的扩展二叉树。)
s传入到构造函数中,用迭代器取出字符串中的首个字符存入到ch中并删掉s的首个字符(意思就是挨个取),首先判断传入字符是否为“#”,若为“#”则说明该节点为空,直接返回。节点不为空的时候创建新节点并为data域赋值,然后递归调用Creat()依次构造左右子树。
//无参构造函数调用
BiNode* BiTree::Creat(BiNode* bt)
{
bt = NULL;
return bt;
}
//有参构造函数调用
BiNode* BiTree::Creat(BiNode* bt,string &s)
{
string::iterator it = s.begin();
char ch = *it;
s.erase(0, 1);
if (ch == '#')
bt = NULL;
else
{
bt = new BiNode;
bt->data = ch;
bt->lchild = Creat(bt->lchild,s);
bt->rchild = Creat(bt->rchild,s);
}
cout << ch;
return bt;
}
前中后序遍历通过递归实现(也可以通过非递归实现,递归比较简单好理解) ,思想基本都一样。以前序遍历为例,传入参数为根节点,首先判断根节点是否为空,若为空则说明为空树(第二次递归调用时说明该节点为空,可以理解为空子树);若不为空则首先读取数据域,然后再递归依次读取左右子树。中序、后续同理,只是读取顺序有变化。
void BiTree::PreOrder(BiNode* bt)
{
if (bt == NULL)
return;
else
{
cout << bt->data;
PreOrder(bt->lchild);
PreOrder(bt->rchild);
}
}
void BiTree::InOrder(BiNode* bt)
{
if (bt == NULL)
return;
else
{
InOrder(bt->lchild);
cout << bt->data;
InOrder(bt->rchild);
}
}
void BiTree::PostOrder(BiNode* bt)
{
if (bt == NULL)
return;
else
{
PostOrder(bt->lchild);
PostOrder(bt->rchild);
cout << bt->data;
}
}
层序遍历通过队列来实现,首先建立一个存取数据类型为BiNode的队列 ,用于暂存节点。遍历开始也是首先判断是否为空树,若不为空树则节点入队列。当队列不为空时,首先将队列的头节点——即最先入队的节点出队,输出该节点信息,然后判断该节点是否有左右子树,若有左右子树则依次入队。由于队是先进先出,所以能够实现按层从左到右读取。如果还是理解不了再次强烈建议B站观看懒猫老师视频!!!
void BiTree::LeverOrder()
{
queue<BiNode*> q;
if (this->root == NULL)
return;
q.push(this->root);
while (!q.empty())
{
BiNode* q1 = q.front();
q.pop();
cout << q1->data;
if (q1->lchild != NULL)
q.push(q1->lchild);
if (q1->rchild != NULL)
q.push(q1->rchild);
}
}