二叉树的前序中序后序遍历(当然是非递归的!)
二叉树的三种遍历方式一直都是为人津津乐道的面试题,考察一个人对递归的理解让他写个三种遍历方式是最简单的考察方式了,那么考察一个人对递归的理解更加深层次的方式就是让他用循环模拟递归(就是把递归代码转换成非递归),一般想要实现这样的东西是需要栈的,或许说使用栈的结构更加贴合函数栈的压入和弹出,更加好理解
递归的三种遍历方式分别为,前序遍历,中序遍历,后序遍历,在考虑完了递归的写法之后,非递归的写法更加难;
- 因为前序遍历是首先访问根节点的,所以见到节点就访问,然后再去访问左孩子和右孩子。原先使用递归的方式,栈帧的模拟可以使用栈来实现,因为是先访问了左节点,所以左节点应该在右节点之后入栈,b不难写出下面的代码
1 void PreOrder_NonR() 2 3 { 4 5 if (_root == NULL ) 6 7 { 8 9 return; 10 11 } 12 13 stack<BinaryTreeNode <T>*> s; 14 15 s.push(_root); 16 17 while (!s.empty()) 18 19 { 20 21 BinaryTreeNode<T >* top = s.top(); 22 23 cout << top->_data << " " ; 24 25 s.pop(); 26 27 if (top->_right) 28 29 { 30 31 s.push(top->_right); 32 33 } 34 35 if (top->_left) 36 37 { 38 39 s.push(top->_left); 40 41 } 42 43 } 44 45 cout << endl; 46 47 }
- 中序遍历可以采用递归的思考方式,因为中序遍历的方式是在访问完左子树之后再去访问根节点,所以在访问完根节点之后进入右子树,右子树又相当于一个新的根节点,所以应该采取访问完根节点之后将右孩子立刻压入栈,然后进入循环可以写出下面的代码
1 void InOrder_NonR() 2 3 { 4 5 stack<BinaryTreeNode <T>*> s; 6 7 BinaryTreeNode<T > *cur = _root; 8 9 while (cur || !s.empty()) 10 11 { 12 13 while (cur) 14 15 { 16 17 s.push(cur); 18 19 cur = cur->_left; 20 21 } 22 23 if (!s.empty()) 24 25 { 26 27 BinaryTreeNode<T > *top = s.top(); 28 29 cout << top->_data << " " ; 30 31 s.pop(); 32 33 cur = top->_right; 34 35 } 36 37 } 38 39 cout << endl; 40 41 }
- 后序遍历是先访问左子树和右子树,所以当左孩子出栈的时候,下一个栈内节点(即根节点)不能立刻就访问,需要考虑几种情况,如果此时此刻右孩子为空,那么可以访问,如果此时有孩子不为空,那么需要考虑右孩子是否被访问过了,如果访问过了,则可以访问根节点,否则需要先访问右子树然后再去访问根节点,可以利用一个变量来保存之前访问的是哪一个节点,可以写出如下代码
1 void PostOrder_NonR() 2 3 { 4 5 stack<BinaryTreeNode <T> *> s; 6 7 BinaryTreeNode<T >* PreVisited = NULL; 8 9 BinaryTreeNode<T >* cur = _root; 10 11 while (cur || !s.empty()) 12 13 { 14 15 while (cur) 16 17 { 18 19 s.push(cur); 20 21 cur = cur->_left; 22 23 } 24 25 BinaryTreeNode<T > *top = s.top(); 26 27 if (top->_right == NULL || PreVisited == top->_right) 28 29 { 30 31 cout << top->_data << " " ; 32 33 s.pop(); 34 35 PreVisited = top; 36 37 } 38 39 else 40 41 { 42 43 cur = top->_right; 44 45 } 46 47 } 48 49 cout << endl; 50 51 }
这是我从我的笔记导出来的,话说缩进怎么变成这德行了。。。