二叉树----第一篇(构造,遍历,求节点,求叶子)
来源:互联网 发布:健身房锻炼软件 编辑:程序博客网 时间:2024/06/05 18:30
一:二叉树结构
#include<iostream>#include<stack>#include<queue>using namespace std;struct BiData//节点结构{char data;BiData *lChild, *rChild;};class BiTree{public:BiData *root;//根节点int leafNum = 0;//叶子数,如果此处报错,请将0去掉,并将leafNum的初始化移到构造函数中,此程序在VisualStudio2015编译运行通过BiTree();//构造函数,构造一个二叉树BiData * Create();void PreOrder(BiData * _root);//前序遍历递归void PreOrderNonRec(BiData * _root);//前序遍历非递归void InOrder(BiData * _root);//中序遍历递归void InOrderNonRec(BiData * _root);// 中序遍历非递归void PostOrder(BiData * _root);//后序遍历递归void PostOrderNonRec(BiData * _root);//后序遍历非递归void LevelOrder(BiData * _root);//层次遍历int Count(BiData * _root);//计算节点int CountLeaf(BiData * _root);//计算叶子的个数(叶子就是度为0的节点)};int main(){BiTree myTree;//1.cout << "前序遍历递归与非递归\n";myTree.PreOrder(myTree.root); cout << endl;myTree.PreOrderNonRec(myTree.root); cout << endl<<endl;//2.cout << "中序遍历递归与非递归\n";myTree.InOrder(myTree.root); cout << endl;myTree.InOrderNonRec(myTree.root); cout << endl<<endl;//3.cout << "后序遍历递归与非递归\n";myTree.PostOrder(myTree.root); cout << endl;myTree.PostOrderNonRec(myTree.root); cout << endl << endl;;//4.cout << "层次遍历\n";myTree.LevelOrder(myTree.root); cout << endl << endl;;//5.cout << "该二叉树的节点有";cout << myTree.Count(myTree.root);cout << "个\n\n";//6.cout << "该二叉树的叶子有";cout << myTree.CountLeaf(myTree.root);cout << "个\n\n";return 0;}
上面的代码实现如下图:
上图myTree变量二叉树如下图
二:具体实现
1.二叉树的构造
对一个二叉树的构造,我使用递归的方法。二叉树的构造和遍历是比较考验对递归的理解的。如果你对递归的理解不到位,那么下面的二叉树三种非递归可能理解会吃力。
BiData * BiTree::Create(){char x;cin >> x;BiData *p;if (x == '0')//输入0表示下一个结点结束p = nullptr;else{p = new BiData;p->data = x;//分别构造左子树和右子树p->lChild = Create();p->rChild = Create();}return p;}BiTree::BiTree()//构造二叉树{root = Create();}
2.二叉树遍历-----前序遍历递归与非递归
递归版本的不用多讲了,就是按照“根节点---左孩子----右孩子”的顺序进行遍历。
主要是非递归, 对于任一结点P:
1)访问结点P,并将结点P入栈;
2)判断结点P的左孩子是否为空,若为空,则取栈顶结点并进行出栈操作,并将栈顶结点的右孩子置为当前的结点P,循环至1);若不为空,则将P的左孩子置为当前的结点P;
3)直到P为nullptr并且栈为空,则遍历结束。
模拟递归,借助容器stack来实现。优先访问根节点,然后接着左子树和右子树。其实对于每一个节点都可以看作根节点。对于上图的myTree,首先访问和输出根节点(也就是1),然后,左子树(也就是2---4---6----7),接着右子树(也就是3----5)。当访问和输出1后,接着访问和输出左孩子2,因为每一个节点都可以看作根节点,因此此时的2变为一个根节点,接着访问和输出2的左孩子(如果2有左孩子的话,如图来看,2是有左孩子的),依次访问到4,此时没有左孩子了,那就要看右孩子6了,访问6的左孩子,发现没有,接着6的右孩子7,此时7已经是结尾了,既没有左孩子也没有右孩子,至此2的左子树(就是4---6--7)已经遍历完毕了,要开始遍历2的右子树,发现2是没有右子树的,那就结束,自此,1的左子树也遍历完毕。好,接下来就是1的右子树了,依次推,3---5,结束。
void BiTree::PreOrder(BiData * _root)//前序遍历递归{if (_root == nullptr)return;else{cout << _root->data << " ";PreOrder(_root->lChild);PreOrder(_root->rChild);}}
void BiTree::PreOrderNonRec(BiData * _root)//前序遍历非递归{stack<BiData*> s;//模拟一个栈while (_root != nullptr || !s.empty()){while (_root != nullptr){cout << _root->data << " ";s.push(_root);_root = _root->lChild;}if (!s.empty()){_root = s.top()->rChild;s.pop();}}}
3.二叉树遍历----中序遍历递归与非递归
非递归,对于任一结点P,
1)若其左孩子不为空,则将P入栈并将P的左孩子置为当前的P,然后对当前结点P再进行相同的处理;
2)若其左孩子为空,则取栈顶元素并进行出栈操作,访问该栈顶结点,然后将当前的P置为栈顶结点的右孩子;
3)直到P为nullptr并且栈为空则遍历结束。
中序非递归版本其实就是需要遍历完一个节点的左子树,然后输出根节点,接着右子树。对于myTree,先遍历1的左子树,访问到4时,4没有左子树,输出4,再看其右孩子6.再开始遍历6的左子树,6没有左子树,那就遍历右子树7,至此,2的左子树遍历完成,开始2的右子树,发现为空,此时1的左子树遍历完成,输出1,再开始1的右子树。 其实只要记住先左遍历到底,再根,然后右,其中对于每一个节点,都是有左和右的。这点要记住,就像遍历到4时,4没有左,但是有右,对于4的右孩子6,我们还要遍历6的左孩子,是需要一直访问到底的,但是因为6没有左子树,故而直接转向右子树7了,对于7,我们依旧要遍历7的左子树一直遍历到底,因为其没有左子树,因此看右子树,7没有右子树,结束。
其实就这么简单。
void BiTree::InOrder(BiData * _root)//中序遍历递归{if (_root == nullptr)return;else{InOrder(_root->lChild);cout << _root->data << " ";InOrder(_root->rChild);}}
void BiTree::InOrderNonRec(BiData * _root)//中序遍历非递归{stack<BiData*> s;while (_root != nullptr || !s.empty()){while (_root != nullptr){s.push(_root);_root = _root->lChild;}if (!s.empty()){cout << s.top()->data << " ";_root = s.top()->rChild;s.pop();}}}
4.二叉树遍历-----后序遍历递归与非递归
非递归,对于每一个节点,如果我们想要输出它,只有它既没有左孩子也没有右孩子或者它有孩子但是它的孩子(左孩子和右孩子,如果它有的话)已经被输出了,那就可以输出它了。若非上述两种情况,则将P的右孩子和左孩子依次入栈,这样就保证了每次取栈顶元素的时候,左孩子和右孩子都在根结点前面被访问。也就是先依次遍历左子树和右子树,然后输出根节点。
依旧对于上图中的myTree,因为对于一个节点,如果我们想要输出它,只有它既没有左孩子也没有右孩子或者它有孩子但是它的孩子(左孩子和右孩子,如果它有的话)已经被输出了。那么对于1的左子树来说,一直入栈,直到7,发现没有左子树和右子树了,输出7,此时对于6来说,6没有左子树,而它的右子树也输出完成,因此输出6,再到4,4没有左子树,右子树输出完成,所以输出4,至此,2的左子树输出完成,而2没有右子树,所以输出2,至此,1的左子树完成,那就要开始1的右子树了,同理,先操作完一个节点的左子树和右子树才可以输出它自己,因此直到5,输出5,然后3,此时1的右子树完成,终于可以输出1了。
void BiTree::PostOrder(BiData * _root)//后序遍历递归{if (_root == nullptr)return;else{PostOrder(_root->lChild);PostOrder(_root->rChild);cout << _root->data << " ";}}
void BiTree::PostOrderNonRec(BiData * _root)//后序遍历非递归{if (_root == nullptr)return;stack<BiData*> s;//栈BiData * pre = nullptr;//上一次访问的节点BiData * cur;//现在访问的节点s.push(_root);while (!s.empty()){cur = s.top();if ((cur->lChild == nullptr&&cur->rChild == nullptr) ||(pre != nullptr) && (pre == cur->lChild || pre == cur->rChild)){cout << cur->data << " ";pre = cur;s.pop();}else{if (cur->rChild)s.push(cur->rChild);if (cur->lChild)s.push(cur->lChild);}}}
5.二叉树遍历-----层次遍历
层次遍历就是对于每一层(例如上面的myTree来说,1是第一层;2,3是第二层,4,5是第三层,依次下去),从左到右依次输出,这里需要用到队列,而不是栈,注意队列和栈的区别。对于根节点,先进队列,然后输出该节点,接着将根节点出队列,然后将节点的左右孩子(如果它有左右孩子的话)依次入队列,然后然后只要队列不为空,就取出第一个元素,输出该节点,并将该节点的左右孩子再依次入队列,依次下去。
void BiTree::LevelOrder(BiData * _root)//层次遍历{if (_root == nullptr)return;queue<BiData*> q;//队列q.push(_root);BiData * temp = _root;while (!q.empty()){temp = q.front();q.pop();cout << temp->data << " ";if (temp->lChild)q.push(temp->lChild);if (temp->rChild)q.push(temp->rChild);}}
6.计算节点数
int BiTree::Count(BiData * _root)//求节点数{if (_root == nullptr)return 0;elsereturn Count(_root->lChild) + Count(_root->rChild) + 1;}
7.计算叶子数
这里在类里定义了一个leafNum用来记录叶子数
int BiTree::CountLeaf(BiData * _root)//求叶子数{if (_root == NULL);else{if (!_root->lChild && !_root->rChild)leafNum++;else{CountLeaf(_root->lChild);CountLeaf(_root->rChild);}}return leafNum;}
三:总结
二叉树的实现主要是对递归的理解,仔细观察上面的实现,发现它们的实现都用到了递归。可是呢,大多数同学对递归的理解也只是模模糊糊的。对于递归的理解,网上的博客也是有很多,个人觉得,画图是最好的学习与认知方法,画出递归的具体运行情形,这样再遇到递归时能够在脑海构造出递归。
这里要做出一些补充,关于二叉树的三种非递归遍历,如果大家仔细的话应该能发现这三种实现方式只有前序,中序的实现大致相似,后序相比前两个较为复杂。对于这方面,我也在百度上找到了统一的写法,链接是 更简单的非递归遍历二叉树的方法
- 二叉树----第一篇(构造,遍历,求节点,求叶子)
- 二叉树建立、各种遍历、求叶子节点
- c语言实现二叉树先序,中序,后序(递归),层次遍历,求叶子节点个数及树的深度,下一篇写非递归的遍历
- 求二叉树非叶子节点数
- 完全二叉树求叶子节点个数
- 求二叉树的叶子节点数目
- 创建二叉树求叶子节点个数
- 数据结构之二叉树求叶子节点
- 求二叉树的节点个数、叶子节点个数、第k层的节点个数
- 递归遍历 二叉树 求高度 和 节点数 和 叶子节点数
- 求满二叉树(哈夫曼树)叶子节点和非叶子节点的数目
- 第十周项目3-利用二叉树遍历思想解决问题(3)求二叉树b的叶子节点个数
- 二叉树 遍历|统计叶子节点|求深度|交换左右子树|查找是否存在某个特定叶子节点练习题
- 二叉树,递归、非递归遍历,求深度,输出叶子节点
- 基本数据结构——二叉树的建立,遍历,求叶子节点,深度计算
- 二叉树的字符串创建和遍历,求深度,叶子节点数
- 二叉树求高度、叶子节点个数、总节点个数
- 树与图的应用:二叉树的构造、遍历和求叶子数和高度
- challenge(一)前言
- IDA6.8 泄露版,大家懂的
- redis安装后提示权限问题ERR operation not permitted
- JVM内存及内存溢出异常(个人总结)
- Eclipse下搭建Hadoop2.7.0开发环境
- 二叉树----第一篇(构造,遍历,求节点,求叶子)
- Linux虚拟内存与交换空间机制
- js中==和===的区别
- BestCoder Round #65 HDU 5592 ZYB's Premutation (数据结构查询第K大)
- 项目二——二叉树排序树中查找的路径
- 文件操作函数(2)-ftell
- AngularJS进阶(二十)HTML5实现获取地理位置信息并定位功能
- 【Android】网络状态获取工具
- POJ-2664