由前序遍历和中序遍历重建二叉树
编程之美3.9:给出前序遍历和中序遍历,重新创建二叉树,后序遍历输出。代码如下:
1 #include <iostream> 2 #include <cassert> 3 4 using namespace std; 5 6 struct Node 7 { 8 Node* m_lChild; 9 Node* m_rChild; 10 char data; 11 }; 12 13 void AfterTra(Node* pRoot) 14 { 15 if (!pRoot) 16 { 17 return; 18 } 19 AfterTra(pRoot->m_lChild); 20 AfterTra(pRoot->m_rChild); 21 cout<<pRoot->data<<" "; 22 } 23 24 //删除树的操作 25 void DestroyTree(Node*& pRoot) 26 { 27 if (!pRoot) 28 { 29 return; 30 } 31 DestroyTree(pRoot->m_lChild); 32 DestroyTree(pRoot->m_rChild); 33 delete pRoot; 34 } 35 36 //计算树中的节点个数 37 int TreeSize(Node* pRoot) 38 { 39 if (!pRoot) 40 { 41 return 0; 42 } 43 return 1+TreeSize(pRoot->m_lChild)+TreeSize(pRoot->m_rChild); 44 } 45 //无重复字母的情况 46 void ReBuild(char* pPreOrder,char* pInOrder,int nTreeLen,Node** pRoot) 47 { 48 if (!pPreOrder || !pInOrder) 49 { 50 return; 51 } 52 Node* pTmp=new Node; 53 pTmp->m_lChild=NULL; 54 pTmp->m_rChild=NULL; 55 pTmp->data=*pPreOrder; 56 if (*pRoot==NULL) 57 { 58 *pRoot=pTmp; 59 } 60 if (nTreeLen==1) 61 { 62 return; 63 } 64 char* pOrgInOrder=pInOrder; 65 char* pLeftEnd=pInOrder; 66 int nTempLen=0; 67 while(*pLeftEnd!=*pPreOrder) 68 { 69 if (!pPreOrder || !pLeftEnd) 70 { 71 return; 72 } 73 nTempLen++; 74 if (nTempLen>nTreeLen) 75 { 76 cout<<"Invaluid Input!!"<<endl; 77 break; 78 } 79 pLeftEnd++; 80 } 81 int nLeftLen=0; 82 nLeftLen=(int)(pLeftEnd-pOrgInOrder); 83 int nRightLen=0; 84 nRightLen=nTreeLen-nLeftLen-1; 85 if (nLeftLen>0) 86 { 87 ReBuild(pPreOrder+1,pInOrder,nLeftLen,&((*pRoot)->m_lChild)); 88 } 89 if (nRightLen>0) 90 { 91 ReBuild(pPreOrder+nLeftLen+1,pInOrder+nLeftLen+1,nRightLen,&((*pRoot)->m_rChild)); 92 } 93 } 94 95 int main() 96 { 97 const int TREELEN=6; 98 char szPreOrder[]="abdcef"; 99 char szInOrder[]="dbaecf"; 100 Node* pRoot=NULL; 101 ReBuild(szPreOrder,szInOrder,TREELEN,&pRoot); 102 AfterTra(pRoot); 103 DestroyTree(pRoot); 104 cout<<endl; 105 }
对于书中的几个扩展问题:
1. 如果有重复字母情况会怎样?如果有重复字母,得到的树不一定唯一。怎样输出所有可能的树?还没想到方法,求路人指教。
2. 怎样判断给出的前序和中序遍历是否合理?如果没有字母不重复,题目代码中已经给出答案:
if (nTempLen>nTreeLen)
{
cout<<"Invaluid Input!!"<<endl;
break;
}
只有给出序列不合理的情况下这个if语句才会为真。当有字母重复时,这个方法也可以用于判断。
3. 如果给出的是后序和中序遍历,方法同上,只需要小小的修改。
4. 如果给出的是中序遍历和层次遍历,这个算法的中心思想是递归下降. 层次遍历的第一个结点(记为R)即为树根. 由中序遍历的性质可知, 在中序序列中, R左边的为左子树的结点, R右边的为右子树的结点. 我们将R左边的序列记为P1, R右边的序列记为P2. 用上面的性质, 对层次序列剩下的结点, 按顺序(这个最重要)判断是左子树还是右子树的结点, 分别归类组成两个序列: 左子树的层次遍历序列C1和右子树的层次遍历序列C2. 问题就变为按P1和C1建立左子树, 按P2和C2建立右子树. 递归形成。相比较于前序和中序,多个一步是使用循环将层次遍历中的节点分为左子树层次遍历和右子树层次遍历两部分。
5. 如果给出的是前序遍历和后序遍历,这样不能确定二叉树。
6. 可见一棵树的中序遍历比较重要,因为中序遍历将左右子树都存放在根的左右两侧了。
7. http://topic.csdn.net/t/20040107/09/2640738.html有个层次和中序遍历生成二叉树非递归算法,有时间看看。