非递归二叉树的算法
2016-09-28 11:26
288 查看
用递归来实现二叉树的算法代码非常简单,但同时也带来效率太低的弊端。为了提高效率我们会选择用非递归的方式来实现二叉树的算法。由于栈就是一种递归的思想,所以在实现非递归的算法时我们要借助栈来实现,主要的原理就是用栈来模拟递归的过程。下面在实现非递归算法的同时,也会将递归的算法给出做一个对比。
一、创建一颗二叉树(先序创建)
以{1,2,3,'#','#',4,'#','#',5,6}为例:
先创建左路,每创建一个结点就入栈,cur指向当前结点:
左路创建完毕之后用一个变量top保存栈顶元素3,然后将栈顶的元素3抛出:
二、先序遍历
如上图:用一个cur遍历左路,并且将遍历的每一个结点入栈,当左路为空的时候将栈顶元素出栈并保存,并且让cur指向这个出栈元素的右子树重复上述操作,直到栈&&cur同时为空,遍历结束。
非递归二叉树的完整代码:
//后序遍历的另一种算法
一、创建一颗二叉树(先序创建)
以{1,2,3,'#','#',4,'#','#',5,6}为例:
先创建左路,每创建一个结点就入栈,cur指向当前结点:
左路创建完毕之后用一个变量top保存栈顶元素3,然后将栈顶的元素3抛出:
然后再创建top的右子树且cur=top->_right,然后再重复上述操作,直到创建完毕。
非递归: TreeNonR(const T* a, size_t size, const T& invalid) { Node* cur = NULL; stack<Node*> s; size_t index = 0; while (index < size) { while (index < size&&a[index] != invalid) { if (index == 0) //根节点特殊处理 { _root = new Node(a[index++]); cur = _root; } else { cur->_left = new Node(a[index++]); cur = cur->_left; } s.push(cur); } index++; Node* top = s.top(); s.pop(); if (index < size&&a[index] != invalid) //如果右子树的根节点不为空再创建 { cur = top; cur->_right = new Node(a[index++]); cur = cur->_right; s.push(cur); } } } 递归: BinaryTree(const T *array,size_t size,const T& invalid) { size_t index = 0; _root=_CreatTree(array,size,index,invalid); } Node* _CreatTree(const T *array, size_t size, size_t& index, const T& invalid) { assert(array); Node *root=NULL; if (index < size&&array[index] != invalid) { root = new Node(array[index]); //创建根节点 root->_left = _CreatTree(array,size,++index,invalid); //递归创建左子树 root->_right= _CreatTree(array,size,++index,invalid); //递归创建右子树 } return root; }
二、先序遍历
如上图:用一个cur遍历左路,并且将遍历的每一个结点入栈,当左路为空的时候将栈顶元素出栈并保存,并且让cur指向这个出栈元素的右子树重复上述操作,直到栈&&cur同时为空,遍历结束。
非递归: void PrevOderNonR() { Node* cur = _root; stack<Node*> s; while (cur || !s.empty()) { while (cur) //访问左路结点 { cout << cur->_data << " "; s.push(cur); cur = cur->_left; } Node* top = s.top(); s.pop(); cur = top->_right; //将左路的最后一个结点的右子树的根节点设置为cur,重复上述过程 } cout <<endl; } 递归: void PrevOder() //前序遍历 { _PrevPrint(_root); cout << endl; } void _PrevPrint(Node *root) { Node *cur =root; if (cur) { cout << cur->_data << " "; _PrevPrint(cur->_left); _PrevPrint(cur->_right); } }
3、中序遍历 中序遍历与先序遍历是一样的原理。 非递归: void InOderNonR() { Node* cur = _root; stack<Node*> s; while (cur || !s.empty()) { while (cur) //访问左路 { s.push(cur); cur = cur->_left; } Node* top = s.top(); cout << top->_data <<" "; s.pop(); cur = top->_right; //将左路的最后一个结点的右子树的根节点设置为cur,重复上述过程 } cout << endl; } 递归: void InOder() //中序遍历 { _InPrint(_root); cout << endl; } void _InPrint(Node *root) { Node *cur = root; if (cur) { _InPrint(cur->_left); cout << cur->_data << " "; _InPrint(cur->_right); } }
4、后序遍历: 试想一下,非递归遍历一棵二叉树无非三种状态,遍历左路,遍历右子树的根节点,出栈。只要我们用一个标记flag来标记这三个状态。 flag=1,遍历左路,左路为空时将flag设置为2。 flag=2,遍历栈顶元素的右子树的根节点,若这个结点为空则flag=3,否则将这个遍历完这个结点后再将flag设置为1。 flag=3,表明栈顶元素的左右子树已经遍历完毕,将栈顶元素出栈,flag设置为2。 非递归: //非递归后序遍历二叉树,设置3个状态来分别标记:遍历左路,将右子树的根节点入队,出栈 void PostOderNonR() { if (_root == NULL) return; Node* cur =_root; int flag = 1; stack<Node*> s; s.push(_root); while (!s.empty()) { cur = s.top(); if (flag == 1) //遍历左路 { if (cur->_left == NULL) //左路为空,切换状态到2 { flag = 2; } else { cur = cur->_left; s.push(cur); } } else if (flag == 2) //将当前结点的右子树的根节点入队 { if (cur->_right== NULL) //如果右子树的根节点为空,表示该结点访问完毕,切换到出栈 flag = 3; else { cur = cur->_right; s.push(cur); //将当前结点的右子树的根节点入队 flag = 1; //切换状态到遍历左路 } } else { Node* top = s.top(); cout << top->_data<<" "; s.pop(); while (!s.empty()&&s.top()->_right==top) //将上一个已经访问完右子树的结点出栈 { top = s.top(); s.pop(); cout << top->_data << " "; } flag = 2; //切换状态到2 } } cout << endl; }
递归: void PostOder() //后序遍历 { _BackPrint(_root); cout << endl; } void _BackPrint(Node *root) { Node *cur = root; if (cur) { _PostPrint(cur->_left); _PostPrint(cur->_right); cout << cur->_data << " "; } }
5、求树的结点个数 非递归: size_t Size() { size_t count = 0; Node* cur = _root; stack<Node*> s; while (cur || !s.empty()) { while (cur) { count++; //遍历一个结点就让count++ s.push(cur); cur = cur->_left; } Node * top = s.top(); s.pop(); cur = top->_right; } return count; } 递归: size_t Size() //求结点个数 { return _Size(_root); } size_t _Size(Node *root) { if (root == NULL) return 0; return 1 + _Size(root->_left) + _Size(root->_right); }
6、求叶子结点的个数 非递归: size_t Leaft() { size_t count = 0; Node* cur = _root; stack<Node*> s; while (cur || !s.empty()) { while (cur) { //如果左右子树都为空,则为叶子结点 if (cur->_left == NULL&&cur->_right == NULL) count++; s.push(cur); cur = cur->_left; } Node * top = s.top(); s.pop(); cur = top->_right; } return count; } 递归: size_t Leaf() //求叶子结点的个数 { return _leaf(_root); } size_t _leaf(Node* root) { Node* cur = root; if (NULL==cur) return 0; if (cur->_left == NULL&&cur->_right == NULL) //如果左右子树都为空,则返回1 return 1; return _leaf(cur->_left ) + _leaf(cur->_right); }
7、查找一个结点 非递归: Node* Find(const T& x) { Node* cur = _root; stack<Node*> s; while(cur||!s.empty()) { while (cur) { if (cur->_data == x) //如果找到则直接返回 return cur; s.push(cur); cur = cur->_left; } Node* top = s.top(); s.pop(); cur = top->_right; } return NULL; } 递归: Node* Find(const T& x) { return _Find(_root,x); } Node* _Find(Node* root,const T& x) { if (root == NULL) return NULL; if (root->_data == x) return root; Node* cur = _Find(root->_left,x); if (cur == NULL) cur = _Find(root->_right,x); return cur; }
8、求树的深度 非递归:借助队列,一层一层的访问,每访问完一层,deep加一,直到队列为空,则求得深度。 size_t Depth() { if (_root == NULL) return 0; queue<Node*> q; size_t deep = 0; size_t NodeNum = 1; //统计有多少数据入过队 size_t LeveLast = 1; //标记正在访问的这层的最后一个数据的序号 size_t VisitNum=0; //统计已经出队的数据的个数 q.push(_root); while (!q.empty()) { Node* cur =q.front(); q.pop(); VisitNum++; if (NULL != cur->_left) { q.push(cur->_left); NodeNum++; } if (NULL != cur->_right) { q.push(cur->_right); NodeNum++; } //如果以出队的个数等于这一层的最后一个数据的序号 if (LeveLast == VisitNum) { deep++; //访问完一层就让深度加一 LeveLast = NodeNum; //更新到下一层的最后一个数据的位置 } } return deep; } 递归: size_t Depth() //求深度 { return _Depth(_root); } size_t _Depth(Node *root) { Node *cur = root; if (NULL == cur) return 0; size_t left = _Depth(cur->_left); size_t right = _Depth(cur->_right); return left > right ? left + 1 : right + 1; }
9、求第i层的结点的个数 非递归:使用队列,将每一层的元素的个数都统计出来。 size_t GetLeveSize(size_t i) { //空树返回0 if (NULL == _root) return 0; //第0或第1层返回层号 if (i<=1) return i; queue<Node*> q; q.push(_root); size_t leve =1; //标记层号 size_t NodeNum = 1; //统计有多少数据入过队 size_t LeveLast = 1; //标记正在访问的这层的最后一个数据的序号 size_t VisitNum = 0; //统计已经有多少数据已经出队 while (!q.empty()) { Node* cur = q.front(); q.pop(); VisitNum++; if (NULL != cur->_left) { q.push(cur->_left); NodeNum++; } if (NULL != cur->_right) { q.push(cur->_right); NodeNum++; } if (VisitNum == LeveLast) //如果以出队的个数等于这一层的最后一个数据的序号 { leve++; if (leve == i) break; LeveLast = NodeNum; //更新到下一层的最后一个数据的位置 } } //用已经入队过的数据个数减去已经出队的个数,得到要求的这一层的个数 return NodeNum - VisitNum; } 递归: size_t GetLeveNode(size_t k) { return _GetLeveNode(_root,k); } size_t _GetLeveNode(Node *root,size_t k) { if (root == NULL) return 0; if (k <= 1) return k; return _GetLeveNode(root->_left,k-1)+_GetLeveNode(root->_right,k-1); }
非递归二叉树的完整代码:
#pragma once #include<queue> #include<stack> using namespace std; template<typename T> struct TreeNode { T _data; TreeNode<T> *_left; TreeNode<T> *_right; TreeNode(const T& data=T()) :_data(data) , _left(NULL) , _right(NULL) {} }; template<typename T> class TreeNonR { typedef TreeNode<T> Node; public: TreeNonR() :_root(NULL) {} /*TreeNonR(const T* a, size_t size, const T& invalid) { size_t index = 0; stack<Node*> s; Node* cur = _root; int flag = 1; while (index < size) { if (flag == 1) { if (a[index] == '#') flag = 2; else { cur= new Node(a[index]); if (!s.empty()) { s.top()->_left = cur; } else { _root = cur; } s.push(cur); } } else if (flag==2) { if (a[index] == '#') flag = 3; else { cur = new Node(a[index]); s.top()->_right = cur; s.push(cur); flag = 1; } } else { Node* top = s.top(); s.pop(); while (!s.empty() && s.top()->_right == top) { top = s.top(); s.pop(); } flag = 2; index--; } index++; } }*/ TreeNonR(const T* a, size_t size, const T& invalid) { Node* cur = NULL; stack<Node*> s; size_t index = 0; while (index < size) { while (index < size&&a[index] != invalid) { if (index == 0) //根节点特殊处理 { _root = new Node(a[index++]); cur = _root; } else { cur->_left = new Node(a[index++]); cur = cur->_left; } s.push(cur); } index++; Node* top = s.top(); s.pop(); if (index < size&&a[index] != invalid) //如果右子树的根节点不为空再创建 { cur = top; cur->_right = new Node(a[index++]); cur = cur->_right; s.push(cur); } } } TreeNonR(const TreeNonR<T>& t) { Node* tmp = NULL; Node* cur = t._root; stack<Node*> s; stack<Node*> s2; while (cur || !s.empty()) { while (cur) { if (cur == t._root) //根节点特殊处理 { tmp = new Node(cur->_data); _root = tmp; } else { tmp->_left = new Node(cur->_data); tmp = tmp->_left; } s.push(cur); s2.push(tmp); cur = cur->_left; } Node* top = s.top(); Node* top2 = s2.top(); s.pop(); s2.pop(); cur = top->_right; if (cur) { tmp = top2; tmp->_right= new Node(cur->_data); tmp = tmp->_right; cur = cur->_left; } } } TreeNonR<T>& operator=(TreeNonR<T>& t) { if (this != &t) { TreeNonR<T> tmp(t); swap(_root,tmp._root); } return *this; } ~TreeNonR() { Node* cur = _root; stack<Node*> s; stack<Node*> del; //遍历一遍树,将每个结点存入del,最后释放del while (cur || !s.empty()) { while (cur) { del.push(cur); s.push(cur); cur = cur->_left; } Node* top = s.top(); s.pop(); cur = top->_right; } while (!del.empty()) //释放del栈中的结点 { Node* top = del.top(); del.pop(); delete top; } } void PrevOderNonR() { Node* cur = _root; stack<Node*> s; while (cur || !s.empty()) { while (cur) //访问左路结点 { cout << cur->_data << " "; s.push(cur); cur = cur->_left; } Node* top = s.top(); s.pop(); cur = top->_right; //将左路的最后一个结点的右子树的根节点设置为cur,重复上述过程 } cout <<endl; } void InOderNonR() { Node* cur = _root; stack<Node*> s; while (cur || !s.empty()) { while (cur) //访问左路 { s.push(cur); cur = cur->_left; } Node* top = s.top(); cout << top->_data <<" "; s.pop(); cur = top->_right; //将左路的最后一个结点的右子树的根节点设置为cur,重复上述过程 } cout << endl; } //非递归后序遍历二叉树,设置3个状态来分别标记:遍历左路,将右子树的根节点入队,出栈 void PostOderNonR() { if (_root == NULL) return; Node* cur =_root; int flag = 1; stack<Node*> s; s.push(_root); while (!s.empty()) { cur = s.top(); if (flag == 1) //遍历左路 { if (cur->_left == NULL) //左路为空,切换状态到2 { flag = 2; } else { cur = cur->_left; s.push(cur); } } else if (flag == 2) //将当前结点的右子树的根节点入队 { if (cur->_right== NULL) //如果右子树的根节点为空,表示该结点访问完毕,切换到出栈 flag = 3; else { cur = cur->_right; s.push(cur); //将当前结点的右子树的根节点入队 flag = 1; //切换状态到遍历左路 } } else { Node* top = s.top(); cout << top->_data<<" "; s.pop(); while (!s.empty()&&s.top()->_right==top) //将上一个已经访问完右子树的结点出栈 { top = s.top(); s.pop(); cout << top->_data << " "; } flag = 2; //切换状态到2 } } cout << endl; } size_t Size() { size_t count = 0; Node* cur = _root; stack<Node*> s; while (cur || !s.empty()) { while (cur) { count++; //遍历一个结点就让count++ s.push(cur); cur = cur->_left; } Node * top = s.top(); s.pop(); cur = top->_right; } return count; } size_t Depth() { if (_root == NULL) return 0; queue<Node*> q; size_t deep = 0; size_t NodeNum = 1; //统计有多少数据入过队 size_t LeveLast = 1; //标记正在访问的这层的最后一个数据的序号 size_t VisitNum=0; //统计已经出队的数据的个数 q.push(_root); while (!q.empty()) { Node* cur =q.front(); q.pop(); VisitNum++; if (NULL != cur->_left) { q.push(cur->_left); NodeNum++; } if (NULL != cur->_right) { q.push(cur->_right); NodeNum++; } //如果以出队的个数等于这一层的最后一个数据的序号 if (LeveLast == VisitNum) { deep++; //访问完一层就让深度加一 LeveLast = NodeNum; //更新到下一层的最后一个数据的位置 } } return deep; } size_t GetLeveSize(size_t i) { //空树返回0 if (NULL == _root) return 0; //第0或第1层返回层号 if (i<=1) return i; queue<Node*> q; q.push(_root); size_t leve =1; //标记层号 size_t NodeNum = 1; //统计有多少数据入过队 size_t LeveLast = 1; //标记正在访问的这层的最后一个数据的序号 size_t VisitNum = 0; //统计已经有多少数据已经出队 while (!q.empty()) { Node* cur = q.front(); q.pop(); VisitNum++; if (NULL != cur->_left) { q.push(cur->_left); NodeNum++; } if (NULL != cur->_right) { q.push(cur->_right); NodeNum++; } if (VisitNum == LeveLast) //如果以出队的个数等于这一层的最后一个数据的序号 { leve++; if (leve == i) break; LeveLast = NodeNum; //更新到下一层的最后一个数据的位置 } } //用已经入队过的数据个数减去已经出队的个数,得到要求的这一层的个数 return NodeNum - VisitNum; } size_t Leaft() { size_t count = 0; Node* cur = _root; stack<Node*> s; while (cur || !s.empty()) { while (cur) { //如果左右子树都为空,则为叶子结点 if (cur->_left == NULL&&cur->_right == NULL) count++; s.push(cur); cur = cur->_left; } Node * top = s.top(); s.pop(); cur = top->_right; } return count; } Node* Find(const T& x) { Node* cur = _root; stack<Node*> s; while(cur||!s.empty()) { while (cur) { if (cur->_data == x) //如果找到则直接返回 return cur; s.push(cur); cur = cur->_left; } Node* top = s.top(); s.pop(); cur = top->_right; } return NULL; } protected: Node *_root; };
//后序遍历的另一种算法
void PostOderNonR() { if (_root == NULL) return; Node* cur = _root; Node* prev = NULL; //记录前一个访问的结点 stack<Node*> s; while (cur||!s.empty()) { while (cur) { s.push(cur); cur = cur->_left; } Node* top = s.top(); if (top->_right==NULL||top->_right==prev) { cout << top->_data <<" "; prev = top; s.pop(); } else { cur =top->_right; } } cout << endl; }
相关文章推荐
- 每日一个算法------二叉树实现、递归和非递归算法(c++版)
- 算法:二叉树的先(根)序遍历、中(根)序遍历、后(根)序遍历(递归及压栈出栈实现的非递归方式)的java代码实现
- 【数据结构与算法】二叉树的遍历(递归遍历、非递归遍历、层序遍历)
- 【算法题】二叉树非递归遍历
- 不使用递归和堆栈,遍历二叉树所有叶子节点的算法
- 二叉树创建及遍历算法(递归及非递归)
- 二叉树创建及遍历算法(递归及非递归)
- 每天学习一算法系列(35)(递归和非递归俩种方法实现二叉树的前序遍历)
- 二叉树三种遍历算法的递归和非递归实现(C++)
- 常用数序结构与算法之后续非递归遍历二叉树
- 二叉树采用二叉链表结构表示。设计并实现如下算法:后序递归建树,先序非递归遍历该树。
- 实现平衡二叉排序树的各种算法(包括二叉树的递归遍历、非递归遍历)
- 二叉树创建及遍历算法(递归及非递归)
- 二叉树创建及遍历算法(递归及非递归)
- 二叉树的遍历算法(先序中序后序遍历的递归算法与非递归算法、层级遍历的递归与非递归算法)
- 算法:二叉树的先序遍历、中序遍历、后序遍历(递归及非递归方式)的java代码实现
- 算法导论 10.4-2 O(n)时间 递归遍历二叉树
- 二叉树创建及遍历算法(递归及非递归)
- 二叉树非递归遍历的算法-有限状态自动机
- 程序员面试100题(算法)之把二叉查找树转变成排序的双向链表(含二叉树前序创建、递归)