引言
说起二叉树,大部分为可能都或多或少知道一些他的性质,但如果让你用代码创建一个二叉树或遍历某个二叉树可能就变得困难了一些,下面就根据这两个问题进行展开,首先提问一个问题?
- 如果你想创建一颗二叉树,那这棵二叉树各个结点的值你该怎样保存,保存之后又该怎样取出?
如果上面一个问题搞清楚了,那么离二叉树的创建就不远了
创建二叉树的结点
一颗二叉树,肯定有一个个的结点,每个结点里面肯定都存有 数据 和 指针(左孩子指针和右孩子指针),下面先看结点的数据结构类型
//二叉树结点 typedef class BiNode { public: char data;//结点数据 BiNode* lchild, * rchild;//左右孩子指针 BiNode()//构造函数,令指针指向为空,这个不是必要的,不过这是一个好习惯 { this->lchild = NULL; this->rchild = NULL; } } BiTNode, * BiTree;
先说一下typedef,在class前加或者不加这个,区别很大, 如果不加,就意味着用这个类创建出来的对象就是一个对象,如果加上了typedef(上面的代码就加了),那么通过这个类创建的对象就不能称为对象了,加了之后,如果创建一个这样的对象,那就要通过这个类的对象(不真实)来创建一个真实的对象,前面这句话比较绕口,如果不理解,可以先去学一下typedef的用法,不然影响下面的阅读。
其次就是类内的成员和函数了,有一个数据成员,两个指针成员(这种指针是指向这种类定义出来的结点对象的),还有一个构造函数,这里的构造函数可以写,也可以不写。
创建二叉树的大体流程
- 首先我们想一下,我们既然要创建一颗二叉树,是不是应该把这颗二叉树保存在堆区?(可以想象成一颗大树扎根在堆区,从大树的根开始,大树可以扩展,继续长大的那种)
- 既然二叉树都保存在堆区,那么是不就应该有一个指针来指向这棵二叉树根,从而来控制这棵树,然后再对大树进行操作
BiTree bt;//创建一个指向树根的指针
- 有了这个接口,我们就该想怎样创建二叉树了(这时候只有指针,树还是一个空树),比如我们想给二叉树添加一个根结点,那么是不是应该在堆区开辟一块内存空间,让上面的那个指针指向这个内存空间,这样就建立起了联系,一是我们的二叉树在堆区放着,二是我们可以通过那个指针来操控这个二叉树。
- 既然我们创建好了二叉树的根结点,那么就可以输入想要根结点保存的数据了。如果我们再想给这个根结点创建一个左孩子,那么方法也是类的,首先在堆区找一块内存空间保存左孩子。再输入左孩子想要保存的值,再把根结点的指针域(左孩子指针)指向这个左孩子,那这样就算创建好了左孩子。(通过递归调用创建左孩子)
- 上面大概就是创建孩子的流程,但是还有一个问题? 上面创建孩子是通过递归调用自身的,那什么时候结束递归调用的(如果没有这个条件,那就会无限递归下去)。所以我们需要想一个办法来解决这个问题
- 解决办法:我们可以把我们想创建的树扩展一下,如下图
- 扩展的方式就是把把原有二叉树的所有结点中,如果没有左孩子或者右孩子,那就给他补齐,但是增加的结点数值为#
- 上面的创建结点的顺序是先开辟空间,然后输入一个数据存入这个结点中,但是没法控制递归调用的结束,所以我们可以改变一下这个顺序,可以首先输入数据,判断是不是 # 。如果是#,我们就不用递归调用了,这要就说明这结点就不用继续创建孩子了,下面看代码
//按照前序输入二叉树中的结点值(一个字符) 前序:根 左 右 //#表示空树。构造二叉树链表表示二叉树 void CreateBiTree(BiTree * T ) { char ch; cin >> ch;//输入结点的数据 if (ch == '#') { *T = NULL; } else { *T = new BiTNode; (*T)->data = ch;//根 CreateBiTree( ( &(*T) -> lchild ) );//左 CreateBiTree( ( &(*T) -> rchild ) );//右 } }
上面的代码是采取前序(根左右)创建的,如果想采取其他的方式创建,只要调换后三行代码的顺序即可
前序:根左右
中序:左根右
后序:左右根
三种遍历方法
相对于创建,其实遍历更简单一些, 首先判断结点是否为空,然后在进行遍历即可。
无论是前序,中序,还是后序,其实都是类似的,主要看三行代码的次序,下面直接看代码
//前序遍历 void PreOrderTraverse(BiTree T) { if (T != NULL) { cout << T->data << " "; PreOrderTraverse(T->lchild); PreOrderTraverse(T->rchild); } } //中序遍历 void InOrderTraverse(BiTree T) { if (T != NULL) { InOrderTraverse(T->lchild); cout << T->data << " "; InOrderTraverse(T->rchild); } } //后序遍历 void PostOrderTraverse(BiTree T) { if (T == NULL) { PostOrderTraverse(T->lchild); PostOrderTraverse(T->rchild); cout << T->data << " "; } }
完整的程序测试样例
#include<iostream> using namespace std; //二叉树结点 typedef class BiNode { public: char data;//结点数据 BiNode* lchild, * rchild;//左右孩子指针 BiNode()//构造函数,令指针指向为空,这个不是必要的,不过这是一个好习惯 { this->lchild = NULL; this->rchild = NULL; } } BiTNode, * BiTree; //按照前序输入二叉树中的结点值(一个字符) 前序:根 左 右 //#表示空树。构造二叉树链表表示二叉树 void CreateBiTree(BiTree * T ) { char ch; cin >> ch;//输入结点的数据 if (ch == '#') { *T = NULL; } else { *T = new BiTNode; (*T)->data = ch; CreateBiTree( ( &(*T) -> lchild ) ); CreateBiTree( &(*T) -> rchild); } } //前序遍历 void PreOrderTraverse(BiTree T) { if (T != NULL) { cout << T->data << " "; PreOrderTraverse(T->lchild); PreOrderTraverse(T->rchild); } } //中序遍历 void InOrderTraverse(BiTree T) { if (T != NULL) { InOrderTraverse(T->lchild); cout << T->data << " "; InOrderTraverse(T->rchild); } } //后序遍历 void PostOrderTraverse(BiTree T) { if (T == NULL) { PostOrderTraverse(T->lchild); PostOrderTraverse(T->rchild); cout << T->data << " "; } } int main() { BiTree bt;//创建一个树的根 CreateBiTree( &bt ); cout << "前序遍历为: "; PreOrderTraverse(bt); cout << endl; cout << "中序遍历为: "; InOrderTraverse(bt); cout << endl; cout << "后序遍历为: "; PostOrderTraverse(bt); cout << endl; system("pause"); return 0; }
测试 :
创作不易,如果文章对你有帮助,何不点个赞再走呢!!!