非递归二叉树的算法

  用递归来实现二叉树的算法代码非常简单,但同时也带来效率太低的弊端。为了提高效率我们会选择用非递归的方式来实现二叉树的算法。由于栈就是一种递归的思想,所以在实现非递归的算法时我们要借助栈来实现,主要的原理就是用栈来模拟递归的过程。下面在实现非递归算法的同时,也会将递归的算法给出做一个对比。

一、创建一颗二叉树(先序创建)
   以{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;
	}


  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值