非递归非堆栈实现二叉树遍历


author:

  • luixiao1223
    title: 非递归和堆栈遍历二叉树

前导知识

我们都知道遍历二叉树的方法是用递归,也可以用stack来模拟递归。那么下面我们来看看如何实现二叉树的三中基本遍历方法。

中序

递归方法

Inorder_traverse(p)
   if p == NULL
       return
   Inorder_traverse(p.left)
   print(p.value)
   Inorder_traverse(p.left)

stack方法

由于stack方法明显要难于递归方法,所以这里我给出了c++的实现代码片段。

stack<node *> s;

  node* p = root;

  while(p!=NULL || !s.empty()){
      while(p!=NULL){
          s.push(p);
          p=p->left;
      }

      if (!s.empty()){
          p=s.top();
          cout<<p->value<<" ";
          s.pop();
          p=p->right;
      }
  }

  cout<<endl;

前序

递归方法

Inorder_traverse(p)
   if p == NULL
       return
   print(p.value)
   Inorder_traverse(p.left)
   Inorder_traverse(p.left)

stack方法

由于stack方法明显要难于递归方法,所以这里我给出了c++的实现代码片段。

stack<node *> s;
s.push(root);

while(!(s.empty())){
    node* current = s.top();
    s.pop();
    cout<<current->value<<" ";
    if (current->right){
        s.push(current->right);
    }
    if (current->left)
        s.push(current->left);
}

后序

递归方法

Inorder_traverse(p)
   if p == NULL
       return
   Inorder_traverse(p.left)
   Inorder_traverse(p.left)
   print(p.value)

stack方法

这个方法比较难于理解,不过我稍微再后面讲到无stack无递归实现遍历的算法里面讲解这个方法。

stack<node*> s;
node* cursor= root;

s.push(cursor);
cursor=cursor->left;

node* p=NULL;

while(!s.empty()){
    while(cursor!=NULL){
        s.push(cursor);
        cursor = cursor->left;
        p=NULL;
    }

    while(!s.empty()){
        cursor = s.top();
        if (p==cursor->left && cursor->right == NULL){
            cout<<cursor->value<<" "<<std::flush;
            s.pop();
            p=cursor;
        }else if(p==cursor->left){
            cursor = cursor->right;
            break;
        }else if(p==cursor->right){
            cout<<cursor->value<<" "<<std::flush;
            p=cursor;
            s.pop();
        }
    }
}
cout<<endl;

问题

我们看到二叉树的递归实现和stack方法实现。现在来看一个更难的实现。这个实现不适用递归也不实用stack而且额外分配的空间数量维持再常数(constant)级别。当然这个算法也比前面的所有算法复杂。

思路和实现

前面实现后续遍历的时候,我其实利用了一些思路来自于无stack和无递归的遍历实现。
其实无stack和无递归的实现,主要使用的是记录回溯的前导指针是目前指针的左孩子还是右孩子。

前序遍历

  1. 首先访问根节点
  2. 然后访一直访问左孩子,直到没有左孩子的节点。并设置prev指针为NULL,current指针为当前最后一个左孩子。
  3. 如果prev指针为当前节点的左孩子,判断current指针是否有右孩子。
    • 如果没有设置prev为当前指针,current=current->parent
    • 如果有访问右孩子,并设置current为右孩子。回到步骤2
  4. 如果prev指针为当前节点的右孩子。则直接设置prev=current,并current=current->parent。然后执行3.

这个过程中并没有使用堆栈,也不是递归,只有循环。

实现代码

node* cursor = root;
node* prev = NULL;

if (cursor != NULL){
    cout<<cursor->value<<" "<<std::flush;
}

while(cursor!=NULL){
    while(cursor->left && prev == cursor->parent){
        prev = cursor;
        cursor = cursor->left;
        if (cursor!=NULL){
            cout<<cursor->value<<" "<<flush;
        }
    }

    if (prev == cursor->left && cursor->right == NULL){
        prev = cursor;
        cursor = cursor->parent;
    }else if(prev == cursor->left){
        prev = cursor;
        cout<<cursor->right->value<<" "<<flush;
        cursor = cursor->right;
    }else{
        prev = cursor;
        cursor = cursor->parent;
    }
}
cout<<endl;

中序遍历

  1. 从根节往左孩子移动,移动到最左边的一个左孩子。设置prev=NULL,current为当前节点。
  2. 如果prev指针为当前节点的左孩子,访问当前current节点,判断current指针是否有右孩子。
    • 如果没有设置prev为当前指针,current=current->parent
    • 如果有,并设置current为右孩子。回到步骤1
  3. 如果prev指针为当前节点的右孩子。则直接设置prev=current,并current=current->parent。然后执行2.

这个过程中并没有使用堆栈,也不是递归,只有循环。

实现代码

node* cursor = root;
node* prev = NULL;

while(cursor!=NULL){
    while(cursor && prev == cursor->parent){
        prev = cursor;
        cursor = cursor->left;
    }

    if (cursor == NULL){
        cursor = prev;
        prev = NULL;
    }

    if (prev == cursor->left && cursor->right == NULL){
        cout<<cursor->value<<" "<<flush;
        prev = cursor;
        cursor = cursor->parent;
    }else if(prev == cursor->left){
        cout<<cursor->value<<" "<<flush;
        prev = cursor;
        cursor = cursor->right;
    }else{
        prev = cursor;
        cursor = cursor->parent;
    }
}
cout<<endl;

后序遍历

  1. 从根节往左孩子移动,移动到最左边的一个左孩子。设置prev=NULL,current为当前节点。
  2. 如果prev指针为当前节点的左孩子,判断current指针是否有右孩子。
    • 访问当前current节点,如果没有设置prev为当前指针,current=current->parent
    • 如果有,并设置current为右孩子。回到步骤1
  3. 如果prev指针为当前节点的右孩子。访问当前current节点,设置prev=current,并current=current->parent。然后执行2.

这个过程中并没有使用堆栈,也不是递归,只有循环。

node* cursor = root;
node* prev = NULL;

while(cursor!=NULL){
    while(cursor && prev == cursor->parent){
        prev = cursor;
        cursor = cursor->left;
    }

    if (cursor == NULL){
        cursor = prev;
        prev = NULL;
    }

    if (prev == cursor->left && cursor->right == NULL){
        cout<<cursor->value<<" "<<flush;
        prev = cursor;
        cursor = cursor->parent;
    }else if(prev == cursor->left){
        prev = cursor;
        cursor = cursor->right;
    }else{
        cout<<cursor->value<<" "<<flush;
        prev = cursor;
        cursor = cursor->parent;
    }
}
cout<<endl;
  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值