二叉树的递归&非递归遍历及其他函数功能的实现
来源:互联网 发布:和女生网络聊天用语 编辑:程序博客网 时间:2024/05/18 01:31
二叉树是一种非常重要的数据结构,很多其它数据结构都是基于二叉树的基础演变而来的。对于二叉树,有前序,中序,后序以及层序四种遍历方法。因为树的定义本身就是递归定义,所以前三种遍历方式采用递归不仅容易理解而且代码很简洁。而对于树的遍历若采用非递归的方法,就要采用栈去模拟实现。层序的实现则要用到队列里的“先进先出”的原理来实现。
先来介绍一下要实现的函数:
template<class T>class BinaryTree{typedef BinaryTreeNode<T> Node; public:BinaryTree():_root(NULL){}BinaryTree(const T* a,size_t size,const T& invalid);BinaryTree(const BinaryTree<T>& b);BinaryTree& operator=(const BinaryTree<T>& b);~BinaryTree();void PrevOrder(); //前序遍历void InOrder(); //中序遍历void PostOrder(); //后序遍历void LevelOrder(); //队列实现层序遍历void PrevOrder_NonR(); //非递归前序遍历void InOrder_NonR(); //非递归中序遍历void PostOrder_NonR(); //非递归后序遍历size_t Size(); //元素个数size_t Depth(); //深度size_t LeafSize(); //叶子节点个数size_t GetKLevel(size_t k); //求第k层的节点个数Node* FindNode(const T& d=T()) //找某个数字的节点位置{return _FindNode(_root,d);}
1.前序
遍历过程是:根节点---左子树---右子树
(1)递归实现:
template<class T>void BinaryTree<T>::PrevOrder() //前序遍历{_PrevOrder(_root);}template<class T>void BinaryTree<T>::_PrevOrder(Node* root){//根节点--左子树--右子树if(NULL==root){return;}cout<<root->_data<<" ";_PrevOrder(root->_left);_PrevOrder((root->_right));}
(2)非递归实现:
根据前序遍历访问的顺序,优先访问根结点,然后再分别访问左孩子和右孩子。即对于任一结点,可看做是一个根结点,因此可以直接访问,访问完之后,若其左孩子不为空,按相同规则访问它的左子树;当访问完其左子树时,再访问它的右子树。过程如下:
对于任一当前结点cur:
1)访问结点cur,并将结点cur入栈;
2)判断结点cur的左孩子是否为空,若为空,则取栈顶结点并进行出栈操作,并将栈顶结点的右孩子置为当前的结点cur,再进入循环);若不为空,则将cur的左孩子置为当前的结点cur并压入栈中;
3)直到cur为NULL并且栈为空,则遍历结束。
template<class T>void BinaryTree<T>::PrevOrder_NonR() //非递归前序遍历{//根节点--左子树--右子树stack< Node* > s; //存放Node*节点Node* cur=_root;while(cur||!s.empty()){ while(cur){cout<<cur->_data<<" ";s.push(cur);cur=cur->_left;}//走完while循环后表示左子树已访问完Node* top=s.top();s.pop();cur=top->_right;}}
2.中序
遍历过程:左子树---根节点---右子树
(1)递归实现:
template<class T>void BinaryTree<T>::InOrder() //中序遍历{_InOrder(_root);}template<class T>void BinaryTree<T>::_InOrder(Node* root) {//左子树--根节点--右子树if(NULL==root){return ;}else{_InOrder(root->_left);cout<<root->_data<<" ";_InOrder(root->_right);}}
(2)非递归实现:
根据中序遍历的顺序,对于任一结点,优先访问其左孩子,而左孩子结点又可以看做一根结点,然后继续访问其左孩子结点,直到遇到左孩子结点为空的结点才进行访问,然后按相同的规则访问其右子树。过程如下:
对于任一结点cur
1)若其左孩子不为空,则将cur入栈并将cur的左孩子置为当前的cur,然后对当前结点cur再进行相同的处理;
2)若其左孩子为空,则取栈顶元素并进行出栈操作,访问该栈顶结点,然后将当前的cur置为栈顶结点的右孩子;
3)直到cur为NULL并且栈为空则遍历结束
测试用例:int a[10]={1,2,3,'#','#',4,'#','#',5,6};
template<class T>void BinaryTree<T>::InOrder_NonR() //非递归中序遍历{stack< Node* > s;Node* cur=_root;while(cur||!s.empty()){//中序遍历(出栈)是3,2,4,1,6,5while(cur){s.push(cur);cur=cur->_left; }cur=s.top();cout<<cur->_data<<" ";s.pop(); cur=cur->_right;//入栈顺序先是1 2 3,然后3没有左孩子出了while循环后先输出3并返回到上一级2处,//此时cur指针指向右子树,再次进入while循环,由于2的右节点不为空,所以压入4,//然后打印2,再打印2的右节点4,4的左右节点均为空时返回到1处并输出,此时栈为空//但当前节点(指向的是1的右节点)不为空,所以压入5,5的左节点不为空再压入6,然//后pop出6,5}}
3.后序
遍历过程:左子树---右子树---根节点
(1)递归实现:
template<class T>void BinaryTree<T>::PostOrder() //后序遍历{_PostOrder(_root);}template<class T>void BinaryTree<T>::_PostOrder(Node* root){//左子树--右子树--根节点if(NULL==root){return;}else{_PostOrder(root->_left);_PostOrder(root->_right);cout<<root->_data<<" ";}}
(2)非递归实现:
这种遍历方式的非递归其实还是不好想的,要保证根结点在左孩子和右孩子访问之后才能访问,因此对于任一结点cur,先将其入栈。如果cur不存在左孩子和右孩子,则可以直接访问它;或者cur存在左孩子或者右孩子,但是其左孩子和右孩子都已被访问过了,则同样可以直接访问该结点。若非上述两种情况,则将cur的右孩子和左孩子依次入栈,这样就保证了每次取栈顶元素的时候,左孩子在右孩子前面被访问,左孩子和右孩子都在根结点前面被访问。
以下图来分析一下它的过程:
首先将1入栈,cur指向栈顶元素1,不满足pop条件,然后依次push进5,2;此时栈顶元素为2(cur指向),仍然不满足pop条件,再次压入2的右孩子,左孩子,此时栈中为1 5 2 4 3,栈顶元素为3,满足其左右孩子为空的条件,进行pop 3,然后cur指向4,prev指向3,再次进行pop 4的操作,此时cur指向2,prev指向4,进行pop 2,
此时栈中只剩下1,cur指向它,不满足往下执行,push进5,cur指向5,还是不满足往下执行,push进6,此时满足pop条件进行pop 6,prev指向6,依然满足pop条件,再次pop 5,pop 1就结束了。
template<class T>void BinaryTree<T>::PostOrder_NonR() //非递归后序遍历{//后序遍历是先左再右再根节点 (出栈)3,4,2,6,5,1Node* cur=_root;Node* prev=NULL;stack<Node*> s;s.push(cur);while (!s.empty()){cur=s.top();if((cur->_left==NULL && cur->_right==NULL)||(prev!=NULL)&& (prev==cur->_left || prev==cur->_right)){cout<<cur->_data<<" ";s.pop();prev=cur;}else{if (cur->_right){s.push(cur->_right);}if(cur->_left){s.push(cur->_left);}}}}
4.层序
拿上图来说,层序就是1---2---5---3---4---6
对于层序遍历,需要借助队列的“先进先出”的特点来实现,下面是代码实现:
template<class T>void BinaryTree<T>::LevelOrder() //层序遍历{//利用队列的“先进先出”的特点if(NULL==_root){return ;}queue<Node*> q; //队列,存放Node*类型的节点Node* cur=_root;q.push(cur); //如果根节点不为空,则将其根节点(第一层)push进去while(!q.empty()){cur=q.front();cout<<cur->_data<<" ";q.pop();if(cur->_left){q.push(cur->_left);}if(cur->_right){q.push(cur->_right);}}cout<<endl;}
下面是整个实现过程:
Binarytree.h
#pragma once#include<iostream>#include<queue>#include<stack>#include<assert.h>using namespace std;template<class T>struct BinaryTreeNode{BinaryTreeNode(const T& d):_data(d),_left(NULL),_right(NULL){}T _data;BinaryTreeNode<T>* _left;BinaryTreeNode<T>* _right;};template<class T>class BinaryTree{typedef BinaryTreeNode<T> Node; public:BinaryTree():_root(NULL){}BinaryTree(const T* a,size_t size,const T& invalid);BinaryTree(const BinaryTree<T>& b);BinaryTree& operator=(const BinaryTree<T>& b);~BinaryTree();void PrevOrder(); //前序遍历void InOrder(); //中序遍历void PostOrder(); //后序遍历void LevelOrder(); //队列实现层序遍历void PrevOrder_NonR(); //非递归前序遍历void InOrder_NonR(); //非递归中序遍历void PostOrder_NonR(); //非递归后序遍历size_t Size(); //元素个数size_t Depth(); //深度size_t LeafSize(); //叶子节点个数size_t GetKLevel(size_t k); //求第k层的节点个数Node* FindNode(const T& d=T()) //找某个数字的节点位置{return _FindNode(_root,d);} public:Node* _CreateBinaryTree(const T* a,size_t size,const T& invalid,size_t& index); Node* _CopyBinaryTree(Node* root); void _Destroy(Node* root);void _PrevOrder(Node* root); void _InOrder(Node* root);void _PostOrder(Node* root);size_t _Size(Node* root);size_t _Depth(Node* root);size_t _LeafSize(Node* root);size_t _GetKLevel(size_t k,Node* root);Node* _FindNode(Node* root,const T& d){if (NULL==root){return NULL;}if (root->_data==d){return root;}Node* ret=_FindNode(root->_left,d); if(NULL==ret){ret=_FindNode(root->_right,d); //左子树没找到再去右子树找}return ret;}protected:Node* _root;};template<class T>BinaryTree<T>::BinaryTree(const T* a,size_t size,const T& invalid){size_t index=0;_root=_CreateBinaryTree(a,size,invalid,index);}template<class T>BinaryTreeNode<T>* BinaryTree<T>::_CreateBinaryTree(const T* a,size_t size,const T& invalid,size_t& index){assert(a); Node* root=NULL;if(index<size && a[index]!=invalid){root=new Node(a[index]); root->_left=_CreateBinaryTree(a,size,invalid,++index);//创建左子树root->_right=_CreateBinaryTree(a,size,invalid,++index);//创建右子树}return root;}template<class T>BinaryTree<T>::BinaryTree(const BinaryTree<T>& b){_root=_CopyBinaryTree(b._root);}template<class T>BinaryTreeNode<T>* BinaryTree<T>::_CopyBinaryTree(Node* root){Node* newroot=NULL;if(root){newroot=new Node(root->_data); newroot->_left=(_CopyBinaryTree(root->_left));newroot->_right=(_CopyBinaryTree(root->_right));}return newroot;}//传统写法//template<class T>//BinaryTree<T>& BinaryTree<T>::operator=(const BinaryTree<T>& b)//{//if(this!=&b)//{//Node* tmp=_CopyBinaryTree(b._root);//_Destroy(_root);//_root=tmp;//}//return *this;//}//现代写法template<class T>BinaryTree<T>& BinaryTree<T>::operator=(const BinaryTree<T>& b){if(this!=&b){BinaryTree<T> tmp(b);std::swap(_root,tmp._root);}return *this;}template<class T>BinaryTree<T>::~BinaryTree(){_Destroy(_root);_root=NULL;}template<class T>void BinaryTree<T>::_Destroy(Node* root){Node* cur=root;while(cur){_Destroy(cur->_left);_Destroy(cur->_right);delete cur;cur=NULL;}}template<class T>void BinaryTree<T>::PrevOrder() //前序遍历{_PrevOrder(_root);}template<class T>void BinaryTree<T>::_PrevOrder(Node* root){//根节点--左子树--右子树if(NULL==root){return;}cout<<root->_data<<" ";_PrevOrder(root->_left);_PrevOrder((root->_right));}template<class T>void BinaryTree<T>::InOrder() //中序遍历{_InOrder(_root);}template<class T>void BinaryTree<T>::_InOrder(Node* root) {//左子树--根节点--右子树if(NULL==root){return ;}else{_InOrder(root->_left);cout<<root->_data<<" ";_InOrder(root->_right);}}template<class T>void BinaryTree<T>::PostOrder() //后序遍历{_PostOrder(_root);}template<class T>void BinaryTree<T>::_PostOrder(Node* root){//左子树--右子树--根节点if(NULL==root){return;}else{_PostOrder(root->_left);_PostOrder(root->_right);cout<<root->_data<<" ";}}template<class T>void BinaryTree<T>::LevelOrder() //层序遍历{//利用队列的“先进先出”的特点if(NULL==_root){return ;}queue<Node*> q; //队列,存放Node*类型的节点Node* cur=_root;q.push(cur); //如果根节点不为空,则将其根节点(第一层)push进去while(!q.empty()){cur=q.front();cout<<cur->_data<<" ";q.pop();if(cur->_left){q.push(cur->_left);}if(cur->_right){q.push(cur->_right);}}cout<<endl;}template<class T>void BinaryTree<T>::PrevOrder_NonR() //非递归前序遍历{//根节点--左子树--右子树stack< Node* > s; //存放Node*节点Node* cur=_root;while(cur||!s.empty()){ while(cur){cout<<cur->_data<<" ";s.push(cur);cur=cur->_left;}//走完while循环后表示左子树已访问完Node* top=s.top();s.pop();cur=top->_right;}}template<class T>void BinaryTree<T>::InOrder_NonR() //非递归中序遍历{stack< Node* > s;Node* cur=_root;while(cur||!s.empty()){//中序遍历(出栈)是3,2,4,1,6,5while(cur){s.push(cur);cur=cur->_left; }cur=s.top();cout<<cur->_data<<" ";s.pop(); cur=cur->_right;//入栈顺序先是1 2 3,然后3没有左孩子出了while循环后先输出3并返回到上一级2处,//此时cur指针指向右子树,再次进入while循环,由于2的右节点不为空,所以压入4,//然后打印2,再打印2的右节点4,4的左右节点均为空时返回到1处并输出,此时栈为空//但当前节点(指向的是1的右节点)不为空,所以压入5,5的左节点不为空再压入6,然//后pop出6,5}}template<class T>void BinaryTree<T>::PostOrder_NonR() //非递归后序遍历{//后序遍历是先左再右再根节点 (出栈)3,4,2,6,5,1Node* cur=_root;Node* prev=NULL;stack<Node*> s;s.push(cur);while (!s.empty()){cur=s.top();if((cur->_left==NULL && cur->_right==NULL)||(prev!=NULL)&& (prev==cur->_left || prev==cur->_right)){cout<<cur->_data<<" ";s.pop();prev=cur;}else{if (cur->_right){s.push(cur->_right);}if(cur->_left){s.push(cur->_left);}}}}template<class T>size_t BinaryTree<T>::Size() //元素个数{return _Size(_root);}template<class T>size_t BinaryTree<T>::_Size(Node* root) {static size_t count=0;if(NULL==root){return 0;}_Size(root->_left);++count; //count的位置不同类似三种遍历方式_Size(root->_right);return count;//if (NULL==root)//{//return 0;//}//return _Size(root->_left)+_Size(root->_right)+1;}template<class T>size_t BinaryTree<T>::Depth() //深度{return _Depth(_root);}template<class T>size_t BinaryTree<T>::_Depth(Node* root) {if (NULL==root){return 0;}size_t LeftDepth=_Depth(root->_left);size_t RightDepth=_Depth(root->_right);return LeftDepth>RightDepth ? LeftDepth+1:RightDepth+1;//if(LeftDepth>RightDepth)//{//return LeftDepth+1; //把根节点算上//}//else//{//return RightDepth+1;//}}template<class T>size_t BinaryTree<T>::LeafSize() //叶子节点个数{return _LeafSize(_root);}template<class T>size_t BinaryTree<T>::_LeafSize(Node* root) //叶子节点个数{//叶子节点就是该节点没有左右孩子的节点if(NULL==root){return 0;}else if(root->_left==NULL && root->_right==NULL){return 1; //返回值形式,递归一次返1,}else{return _LeafSize(root->_left)+_LeafSize(root->_right);}}template<class T>size_t BinaryTree<T>::GetKLevel(size_t k) //第k层节点个数 {return _GetKLevel(k,_root);}template<class T>size_t BinaryTree<T>::_GetKLevel(size_t k,Node* root) {assert(k>0);if(NULL==root){return 0;}if(k==1) {return 1;}size_t leftsz=_GetKLevel(k-1,root->_left);size_t rightsz=_GetKLevel(k-1,root->_right);return leftsz+rightsz;}
test.cpp
#include"binarytree.h"void test(){int a[10]={1,2,3,'#','#',4,'#','#',5,6};BinaryTree<int> b1(a,10,'#');cout<<"前序:";b1.PrevOrder();cout<<endl;cout<<"中序:";b1.InOrder();cout<<endl;cout<<"后序:"; b1.PostOrder();cout<<endl;cout<<"层序:";b1.LevelOrder();cout<<endl;cout<<"非递归前序:";b1.PrevOrder_NonR();cout<<endl;cout<<"非递归中序:";b1.InOrder_NonR();cout<<endl;cout<<"非递归后序:";b1.PostOrder_NonR();cout<<endl;cout<<"b1.Size:"<<b1.Size()<<endl;cout<<"b1.Depth:"<<b1.Depth()<<endl;cout<<"b1.LeafSize:"<<b1.LeafSize()<<endl;cout<<"b1.GetKLevel:"<<b1.GetKLevel(2)<<endl;BinaryTree<int> b2(b1);cout<<"调拷贝构造,前序遍历:";b2.PrevOrder();cout<<endl;BinaryTree<int> b3;b3=b1;cout<<"调赋值操作,前序遍历:";b3.PrevOrder();cout<<endl;cout<<"调赋值操作,中序遍历:";b3.InOrder();cout<<endl; }void test1(){int a[10]={1,2,3,'#','#',4,'#','#',5,6};BinaryTree<int> b1(a,10,'#');BinaryTreeNode<int> *ret=b1.FindNode(6);assert(ret);cout<<"FindNode6:"<<ret->_data<<endl; ret=b1.FindNode(8);cout<<"FindeNode8:"<<ret<<endl; //找不到输出地址00000000}int main(){test();test1();system("pause");return 0;}
- 二叉树的递归&非递归遍历及其他函数功能的实现
- 二叉树非递归前序遍历表示及其他栈的情况
- 非递归实现二叉树的遍历
- 非递归实现二叉树的遍历
- 二叉树遍历的非递归实现
- 二叉树遍历的非递归实现
- 二叉树遍历的非递归实现
- 二叉树的非递归遍历实现
- 二叉树遍历的非递归实现
- 非递归实现二叉树的遍历
- 二叉树遍历的非递归实现
- 二叉树遍历的非递归实现
- 非递归遍历二叉树的实现
- 二叉树遍历的非递归实现
- 二叉树遍历的非递归实现
- Java创建二叉树及其遍历的递归和非递归实现
- 二叉树的遍历(递归实现+非递归实现)
- 二叉树的各种遍历的递归非递归实现。
- Java进阶(四十二)Java中多线程使用匿名内部类的方式进行创建3种方式
- js 中的 __proto__
- 空间归属问题
- Ajax 完整教程
- 2种KMP
- 二叉树的递归&非递归遍历及其他函数功能的实现
- Unique Binary Search Trees (leetcode) 动态规划
- Java实现基于Redis的分布式锁
- 3665顺序表应用8:最大子段和之动态规划法
- Proguard代码混淆
- 多核多线程基础
- 正则表达式(Regular Expression,regex,regexp)
- 字符串-字符串的包含-寻找兄弟字符串 课后题答案
- 数论,优化,预处理(GCD 等于 XOR,uva 12716)