二叉树递归与非递归遍历

来源:互联网 发布:mac mini 内存升级 编辑:程序博客网 时间:2024/06/05 11:23
                                                  二叉树的遍历

常见的二叉树的遍历有三种方式:先序遍历(根结点,左子树,右子树)

                                                            中序遍历(左子树,根结点,右子树)

                                                           后续遍历(左子树,右子树,根结点)

这三种方式有分别可以用递归非递归来实现,相对二叉树的遍历递归比非递归简单。其实还有一种层序遍历,是利用队列访问这里不做解释。

一、递归遍历

1、先序遍历

           先序遍历就是从根结点开始,把根结点当成一个子问题,先访问父结点,然后访问左子树,等左子树全部访问完后,再访问右子树。 每访问到一个节点时,都把这个节点当成子问题进行上述顺序先访问父节点,然后直到左子树为空或者左子树已经访问过了时,再访问右子树。

  void _PrevOrder(Node* root)
 {
  if (root == NULL)
  {
   return;
  }
  cout << root->_data << " ";
  _PrevOrder(root->_left);
  _PrevOrder(root->_right);
 }

2、中序遍历

           中序遍历就是先访问左子树,等左子树全部访问完,在访问父结点,父结点访问完,再访问右子树。每访问到一个节点时,都把这个节点当成子问题进行上述顺序访问,直到访问到此节点的左子树为空时或者左子树已经访问过了,再访问其根结点,然后在访问右子树。

 void _InOrder(Node* root)
 {
  if (root == NULL)
  {
   return;
  }
  _InOrder(root->_left);
  cout << root->_data << " ";
  _InOrder(root->_right);
 }

3、后续遍历

       后续遍历就是先访问左子树,在访问右子树,左右子树都访问完了,再访问父结点。同样的每遇到一个节点都将其看成子问题,直到左右子树都为空,或者已经访问过,在访问父结点。

void _PrevOrder(Node* root)
 {
  if (root == NULL)
  {
   return;
  }
  cout << root->_data << " ";
  _PrevOrder(root->_left);
  _PrevOrder(root->_right);
 }

二、非递归遍历

非递归遍历是借助进行遍历,因为遍历二叉树访问完左子树后还必须访问访问右子树,所以我们必须把每个访问过得节点存起来,以方便再访问其节点的右子树。

1、先序遍历

先序遍历的主要步骤:(参考下图)

1.从根结点开始先压左路结点,并访问结点,直到把根结点和左路结点全部压入栈。
2.若左子树和为空,说明左路和根结点已经全部压栈并且已经访问过了,开始取栈顶元素来访问上一层父节点的右子树。把               右子树看成子问题继续进行1步骤。
3.依次进行上述1和2压栈访问和出栈操作,直到栈为空或者右子树为空这说明遍历完毕。

 void PrevOrderR()
 {
  Node* cur = _root;
  stack<Node*> s;
  while (!s.empty() || cur != NULL)  //若这里的cur为空,则说明左右子树都为空,只有栈和右子树同时为空时,二叉树才访问完
  {
   while (cur != NULL)
   {
    cout << cur->_data << " ";
    s.push(cur);
    cur = cur->_left;
   }
   //若左子树和为空时程序则走到这里,开始取栈顶元素来访问上一层父节点的右子树
   Node* top = s.top();
   cur = top->_right;
   s.pop();
  }
  cout << endl;
 }


 void PrevOrderR()
 {
  Node* cur = _root;
  stack<Node*> s;
  while (!s.empty() || cur != NULL)  //若这里的cur为空,则说明左右子树都为空,只有栈和右子树同时为空时,二叉树才访问完
  {
   while (cur != NULL)
   {
    cout << cur->_data << " ";
    s.push(cur);
    cur = cur->_left;
   }
   //若左子树和为空时程序则走到这里,开始取栈顶元素来访问上一层父节点的右子树
   Node* top = s.top();
   cur = top->_right;
   s.pop();
  }
  cout << endl;
 }

 

2、中序遍历

中序遍历和先序遍历的压栈出栈操作和判断都是是一样的,只是访问结点的顺序是不一样的。所以直接根据上图思想看中序遍历代码。

 void InOrderR()
 {
  Node* cur = _root;
  stack<Node*> s;

  while (!s.empty() || cur != NULL)  //若这里的cur为空,则说明左右子树都为空
  {
   while (cur != NULL)
   {
    s.push(cur);
    cur = cur->_left;
   }

   //若左子树为空时程序则走到这里,开始取栈顶元素来打印父节点,并访问父节点的右子树
   Node* top = s.top();
   cout << top->_data << " ";
   cur = top->_right;
   s.pop();
  }
  cout << endl;
 }

3、后续遍历

后续遍历的问题:

后续遍历就不一样了,稍微比中序和先序麻烦一点。

因为后续遍历必须是访问完左右子树之后才可以访问父亲结点,所以访问完左子树时,现在得去访问右子树,通过栈找到父亲结点(这时是第一次top父亲结点),然后找到父亲结点的右子树进行访问,当把右子树访问完成后,再通过栈找到父亲结点进行访问(这时是第二次top父亲结点A)。那么怎么才知道这时是第一次top和第二次top呢?如果不做处理的话这里就会一直认为是第一次top父亲节点,不出栈,就会造成死循环,所以怎样解决呢?

解决方法:

创建一个Prev结点,用来记录上一次出栈的结点,若上一次,出栈的结点为右子树,这说明可以访问根结点了,说明是已经第二次top父亲结点了。

 void PostOrderR()
 {
  Node* cur = _root;
  stack<Node*> s;
  Node* Prev = NULL;   //用Prev记录上一次访问的节点,这样就可以判断右子树(不为空时)是第一次访问,还是第二次访问
  while (!s.empty() || cur != NULL)        //第一次访问会把右子树当成子问题处理,当右子问题输出后,Prev就会                                                                 置成右子节点         
  {                                        //当在一次回到访问右子树的父节点时,发现上一次的Prev是右子节点,作                                                                说明已经访问过有节点,这可以输出根结点了    
   while (cur != NULL)    //从根结点开始把左子树全部压入栈
   {
    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;
 }



转载自(http://blog.csdn.NET/dream_1996/article/details/72809766?ref=myread)

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 郑州市住房公积金管理中心 淮安市住房公积金管理中心 住房基金查询 娄底住房公积金查询 住房公积金查询余额 贵港住房公积金查询 东莞市住房公积金管理中心 住房公积金网站 广州住房公积金查询 河南省住房公积金查询 福州住房公积金管理中心 常州住房公积金查询 遂宁市住房公积金管理中心 天津市住房公积金管理中心 湖南住房公积金查询个人账户 深圳住房公积金电话 住房公积金查询网 洛阳住房公积金 呼和浩特住房公积金查询 青海省住房公积金查询 郑州住房公积金管理中心 苏州住房公积金查询 成都住房公积金查询个人账户 住房公积金中心电话 昆山住房公积金管理中心 吉林市住房公积金查询 沈阳住房公积金 沈阳住房公积金管理中心 贵港市住房公积金管理中心 萍乡住房公积金查询 郑州市住房公积金 赣州市住房公积金管理中心 济南住房公积金查询 济宁市住房公积金个人查询 西安住房公积金查询网 西安市住房公积金中心 西安市住房公积金 西安市住房公积金管理中心 西安市个人住房公积金查询 个人住房公积金查询个人账户 西安住房公积金个人帐户查询