二叉树的三种递归与非递归遍历算法
树结点的声明
struct BinaryNode { char element; BinaryNode* left; BinaryNode* right; BinaryNode() { element = ' '; left = right = NULL; } };
创建一棵二叉树
1 BinaryNode * creatBinaryTree (string&s) 2 { 3 if (s.empty()) 4 return NULL; 5 size_t n = s.size(); 6 BinaryNode* p = new BinaryNode[n]; 7 for (int i = 0; i < n; ++i) 8 { 9 p[i].element = s[i]; 10 if ( (2*i+1) < n ) 11 p[i].left = &p[2*i+1]; 12 if ( (2*i+2) < n ) 13 p[i].right = &p[2*i+2]; 14 } 15 return p; 16 }
输出二叉树
1 enum mode{preR,preNr, inR, inNr, postR, postNr}; 2 3 void displayBinaryTree(BinaryNode*t, enum mode& m) 4 { 5 switch(m) 6 { 7 case preR: 8 preOrderTraverse_Recursive(t); 9 break; 10 case preNr: 11 preOrderTraverse_nonRecursive(t); 12 break; 13 case inR: 14 inOrderTraverse_Recursive(t); 15 break; 16 case inNr: 17 inOrderTraverse_nonRecursive(t); 18 break; 19 case postR: 20 postOrderTraverse_Recursive(t); 21 break; 22 case postNr: 23 postOrderTraverse_nonRecursive(t); 24 break; 25 default: 26 ; 27 } 28 }
一.先序遍历
1.递归算法
1 void preOrderTraverse_Recursive( BinaryNode *t) 2 { 3 if (t) 4 { 5 cout << t->element << endl; 6 preOrderTraverse_Recursive(t->left); 7 preOrderTraverse_Recursive(t->right); 8 } 9 }
2.非递归算法
思路::在先序遍历中,由于根节点首先被访问,而且由根结点可以同时得到左右孩子结点的信息,因此在访问过程中,可以在访问根节点的同时将根节点从栈中删除,并且将根节点的左右孩子按照右孩子,左孩子的顺序入栈。
1 void preTraverse_nonRecursive( BinaryNode *t) 2 { 3 stack<BinaryNode*>s; 4 BinaryNode* p = t; 5 s.push(p); 6 while (!s.empty()) 7 { 8 p = s.top(); 9 s.pop(); 10 if (p) 11 { 12 cout<<p->element<<endl; 13 s.push(p->right); 14 s.push(p->left); 15 } 16 } 17 }
二.中序遍历
1.递归算法
void inOrderTraverse_Recursive( BinaryNode *t)
{
if (t)
{
inOrderTraverse_Recursive(t->left);
cout << t->element << endl;
inOrderTraverse_Recursive(t->right);
}
}
2.非递归算法
思路1和思路2的主要区别是思路1会将空指针压入栈中,而思路2则不会,因此思路1需要将空指针弹出栈
1 //思路1 2 void inOrderTraverse_nonRecursive( BinaryNode *t) 3 { 4 stack<BinaryNode*>s; 5 BinaryNode* p = t ; 6 s.push(p); 7 while ( !s.empty() ) 8 {//p表示栈顶元素 9 p = s.top(); 10 while ( p ) 11 {//向左走到尽头,最后压入的一定是空指针 12 p = p->left; 13 s.push(p); 14 } 15 s.pop();//空指针出栈 16 if (!s.empty()) 17 { 18 p = s.top(); 19 s.pop(); 20 cout << p->element << endl; 21 s.push( p->right ); 22 } 23 } 24 }
1 void inOrderTraverse_nonRecursive( BinaryNode *t) 2 {//思路2 3 stack<BinaryNode*>s; 4 BinaryNode* p = t ; 5 while ( p || !s.empty() ) 6 {//p指向的是下一个需要被访问的结点 7 if ( p ) 8 { 9 s.push(p); 10 p = p->left; 11 } 12 else 13 {//p为null,栈非空 14 p = s.top(); 15 s.pop();//根指针退栈,遍历右子树 16 cout << p->element << endl; 17 p = p->right; 18 } 19 } 20 }
三.后序遍历
1.递归算法
void postOrderTraverse_Recursive( BinaryNode *t) { if (t) { postOrderTraverse_Recursive(t->left); postOrderTraverse_Recursive(t->right); cout << t->element << endl; } }
2.非递归算法
思路:按照根节点,右孩子结点,左孩子结点的顺序依次入栈。如果一个结点没有左右孩子结点,则该结点可被访问,同时该结点出栈。如果一个结点有左右孩子结点,但是其孩子结点已经被访问,则该结点也可以被访问。因此需要设置一个变量preNode存放上次被访问的结点。
1 void postOrderTraverse_nonRecursive( BinaryNode *t) 2 { 3 stack<BinaryNode*>s; 4 BinaryNode* p = t ; 5 BinaryNode* preNode = t; 6 if (p) 7 s.push(p); 8 while (!s.empty()) 9 { 10 p = s.top(); 11 if ( (p->left == NULL && p->right==NULL) 12 || (preNode == p->left || preNode == p->right) ) 13 { 14 cout << p->element << endl; 15 s.pop(); 16 preNode = p; 17 } 18 else 19 { 20 if (p->right) 21 s.push(p->right); 22 if (p->left) 23 s.push(p->left); 24 } 25 } 26 }
上图为反映了后序遍历时数据出栈入栈的情况。结点d因为左右孩子都为空,因此可以访问,访问完成后出栈,结点b的左右孩子因为都被访问过,因此也可以被访问,此时b的前一个访问结点是结点e,即它的右结点。