树的前序、中序以及后序非递归遍历

树的前序、中序以及后序非递归遍历
树的前序、中序以及后序遍历的递归算法很简单,大部分人都能信手拈来,但是非递归算法却不是那么简单,我们来各个击破:
树的结构如下:

1 typedef  struct Node {
2      int data;
3      struct Node *left, *right;
4 } BTree;
5 
1)前序非递归遍历:
      前序遍历的非递归算法相比较而言最简单,只需要访问栈顶元素,然后再将栈顶元素出栈,然后再将栈顶元素的右、左孩子入栈即可,不过这里注意的是必须得右孩子先入栈。
      程序如下:

 1  void PreOrderTraversal(BTree *root) {
 2      if (root == NULL)  return;
 3     stack<BTree *> s;
 4     BTree *tmp;
 5     s.push(root);
 6      while (!s.empty()) {
 7         tmp = s.top();
 8         s.pop();
 9         printf("%d\n", tmp->data);
10          if (tmp->right != NULL) {
11             s.push(tmp->right);
12         }
13          if (tmp->left != NULL) {
14             s.push(tmp->left);
15         }
16     }
17 }
18 

      这种写法也非常直观,但是当遇到中序非递归遍历的时候这种写法就不能解决问题了,原因是前序遍历是先访问根节点,访问完后就将根节点出栈了,后面栈的操作不再遇到根节点,而中序遍历的时候必须先访问左子树才能访问根节点,这样在访问左子树之前根节点必须先入栈,但是当某个元素出栈的时候你不能知道它的左孩子是否已经被访问过了,因此我们需要换一种思路:
      其实我们可以模拟中序遍历的过程:
            只要当前节点有左孩子,则必须先去访问左子树,而当前节点就得入栈;
            如果当前节点为空怎么办?当然就访问它的父节点了,也就是栈顶元素;
            访问完栈顶元素之后就需要将当前节点置为栈顶元素的右孩子,然后栈顶元素出栈;
            再继续上述过程直到栈空;
      代码如下:

 1  void InOrderTraversal(BTree *root) {
 2      if (root == NULL)  return;
 3     stack<BTree *> s;
 4     s.push(root);
 5     BTree *cur = root->left;  // 指向当前要检查的节点
 6       while (cur != NULL || !s.empty()) {
 7          while (cur != NULL) {  // 一直向左走
 8              s.push(cur);
 9             cur = cur->left;
10         }
11         cur = s.top();
12         s.pop();
13         printf("%d\n", cur->data);
14         cur = cur->right;
15     }
16 }
17 

后序遍历与中序遍历很相似,但是比中序遍历复杂的地方是如何判断该节点的左右子树都已经访问过了,按照中序遍历的写法左子树还是先被访问,没有问题,但是访问完左子树后不能直接访问当前节点,要判断当前节点的右子树是否已经被访问,如果没有访问则应该继续去访问右子树,最后再访问当前节点:
      算法如下:
            用cur记录当前要检查的节点;
            用previsited记录前一个被访问(visited)的节点;
            这样只要cur有左孩子,则cur入栈,直到cur没有左孩子;
            然后判断栈顶元素的右孩子是否是上一个被访问的节点或者没有右孩子;(因为后序遍历的特性,在访问序列中,当前节点的前驱必然是其右孩子(如果有的话))
            如果有右孩子而又没有被访问过则cur置为当前节点的右孩子,继续上述过程;
            否则访问当前节点,并置previsited为当前节点;
            重复以上过程直到栈空;
      代码如下:

 1  void PostOrderTraversal(BTree *root) {
 2      if (root == NULL)  return;
 3     stack<BTree *> s;
 4     BTree *cur = root;  // 指向当前要检查的节点
 5      BTree *previsited = NULL;  // 指向前一个被访问的节点
 6       while (cur != NULL || !s.empty()) {
 7          while (cur != NULL) {  // 一直向左走直到为空
 8              s.push(cur);
 9             cur = cur->left;
10         }
11         cur = s.top();
12          // 当前节点的右孩子如果为空或者已经被访问,则访问当前节点
13           if (cur->right == NULL || cur->right == previsited) {
14             printf("%d\n", cur->data);
15             s.pop();
16             previsited = cur;
17             cur = NULL;
18         }
19          else {  // 否则访问右孩子
20              cur = cur->right;
21         }
22     }
23 }
24 

理解树的非递归遍历非常重要,因为它能帮助更好的理解树的操作。

你可能感兴趣的:(树的前序、中序以及后序非递归遍历)