非递归实现二叉树先序、中序、后序遍历(栈实现)

 

本篇文章主要详解利用栈的方式二叉树先序、中序、后序遍历的非递归写法

首先我们需要实现一颗二叉树。

以下是通过先序序列建树的代码

例如:先序序列{1,2,4,10,'#','#',6,11,'#','#',7'#','#','#',3,8,'#','#',9}代表以下的树

 
  1. typedef struct Node

  2. {

  3. int _data;

  4. Node* _right;

  5. Node* _left;

  6. Node()

  7. {}

  8. Node(int x)

  9. {

  10. _data = x;

  11. _left = NULL;

  12. _right = NULL;

  13. }

  14. }*pNode;

  15.  
 
//参数类型arr 数组指针,index 下标,size 数组元素个数,invalid代表先序遍历时遇到的NULL

pNode createTree_PrevOrder( const int* arr, size_t& index,const size_t size,int invalid){Node* root = NULL;if (index < size && arr[index] != invalid){root = new Node(arr[index]);root->_left = createTree_PrevOrder(arr, ++index, size, invalid);root->_right = createTree_PrevOrder(arr, ++index, size, invalid);}return root;}

先序非递归:

先序遍历时,每当我们压入一个结点,我们压入结点前对其进行访问。

 
  1. void prevOrder_NR(pNode root)

  2. {

  3. if (NULL == root)

  4. cout << "empty!" << endl;

  5. stack<pNode> s;

  6. pNode cur = root;

  7. while (cur || !s.empty())

  8. {

  9. while (cur) //判断:cur为空,遍历栈顶结点的右子树结点,cur不为空;访问cur,并遍历cur的左子树结点

  10. {

  11. cout << cur->_data << "->";//遍历一个结点则进行访问

  12. s.push(cur);

  13. cur = cur->_left;

  14. }

  15.  
  16. cur = s.top(); //此时cur为空,因此需要对cur进行处理

  17. s.pop(); //将栈顶结点的右子树结点压入栈前,将栈顶结点弹出,避免死循环

  18. cur = cur->_right;//在下一个while时将右子树结点压入栈

  19. }

  20.  
  21. cout << "over"<<endl;

  22. }

中序非递归:

中序时我们需要在遍历完左子树后访问根节点,再去遍历右子树,因此我们仅需依照先序遍历修改部分代码。

 
  1. void inOrder_NR(pNode root)

  2. {

  3. if (NULL == root)

  4. cout << "empty" << endl;

  5. stack<pNode> s;

  6.  
  7. pNode cur = root;

  8.  
  9. while (cur || !s.empty())

  10. {

  11. while (cur)//压入左子树结点

  12. {

  13. s.push(cur);

  14. cur = cur->_left;

  15. }

  16.  
  17. cur = s.top();//取栈顶结点

  18. cout << cur->_data << "->" ;//访问左结点,此时栈顶节点有两种情况:1.左子树结点为空;2.左子树结点已被访问弹出

  19. s.pop(); //弹出访问过的结点

  20. cur = cur->_right; //将栈顶结点的右子树结点入栈

  21. }

  22. cout << "over" << endl;

  23. }

后序遍历:

后序遍历时由于访问完左右子树后才能访问根结点,因此需要将根结点在栈内保留到左右子树被访问后,但同时会出现一个问题,当右子树弹出后遇到根结点又会将右子树结点压入栈中,造成死循环,因此我们需要在定义一个变量last代表最后一个访问的结点,当last与栈顶结点的右子树结点相同时,则不再将右子树结点压入栈中。

 
 
  1. void pastOrder_NR(pNode root)

  2. {

  3. if (NULL == root)

  4. cout << "empty" << endl;

  5.  
  6. pNode cur = root;

  7. pNode last = NULL;

  8. stack<pNode> s;

  9.  
  10. while (cur || !s.empty())

  11. {

  12. while (cur)//压入左子树结点

  13. {

  14. s.push(cur);

  15. cur = cur->_left;

  16. }

  17.  
  18. cur = s.top();

  19.  
  20. if (cur->_right && last != cur->_right)//考虑栈顶结点的右子树结点。存在且没被访问过,将右子树结点压入栈中

  21. {

  22. cur = cur->_right;

  23. }

  24. else if ((NULL == cur->_right) || (last == cur->_right))

  25. //右子树结点为空或者已经被访问过,则访问栈顶结点并弹出

  26. {

  27. cout << cur->_data << "->";

  28. last = cur;//更新last值

  29. s.pop();

  30. //cur置空作用在于当原栈顶结点被访问并弹出后,下一层while是将当前栈顶结点的左子树入栈,当前栈顶结点的左子树已经被遍历过,

  31. //因此会造成死循环,所以将cur置空,直接考虑当前栈顶结点的右子树

  32. //一旦某个结点入栈,首先会遍历这个结点的左子树,然后考虑右子树的情况

  33. cur = NULL;

  34. }

  35. }cout << "over" << endl;

  36. }

附带递归写法用于测试正确性

 
  1. void prevOrder(pNode root)

  2. {

  3. if (root == NULL)

  4. return;

  5. cout << root->_data << "->";

  6. prevOrder(root->_left);

  7. prevOrder(root->_right);

  8. }

  9. void pastOrder(pNode root)

  10. {

  11. if (root == NULL) return;

  12. pastOrder(root->_left);

  13. pastOrder(root->_right);

  14. cout << root->_data << "->";

  15. }

  16.  
  17. void inOrder(pNode root)

  18. {

  19. if (root == NULL) return;

  20. inOrder(root->_left);

  21. cout << root->_data << "->";

  22. inOrder(root->_right);

  23.  
  24. }

 
 
  1. void test()

  2. {

  3. int arr[] = { 1, 2, 4, 10, '#', '#', 6, 11, '#', '#', 7,'#', '#', '#', 3, 8, '#', '#', 9 };

  4. size_t index = 0;

  5. pNode root=createTree_PrevOrder(arr, index, sizeof(arr)/sizeof(arr[0]), '#');

  6.  
  7. cout << "prevOrder_NR:" ;

  8. prevOrder_NR(root);

  9. cout << "prevOrder :";

  10. prevOrder(root);

  11. cout << endl;

  12. cout << "inOrder_NR:";

  13. inOrder_NR(root);

  14. cout << "inOrder :";

  15. inOrder(root);

  16. cout << endl;

  17. cout << "pastOrder_NR:";

  18. pastOrder_NR(root);

  19. cout << "pastOrder :";

  20. pastOrder(root);

  21. cout << endl;

  22. }

猜你喜欢

转载自blog.csdn.net/weixin_46847295/article/details/107774094