题目:输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
解:算法的关键是找出根节点的左子树和右子树。如果算法能求出根节点和左右子树,剩下的递归调用这个算法就可以了。前序遍历的第一个值就是根节点,而中序遍历中左子树就是根节点前面的部分,右子树就是根节点右边的部分,问题得解。
下面是代码及测试部分。
#include <iostream> using namespace std; struct BinaryTreeNode { int m_nValue; BinaryTreeNode* m_pLeft; BinaryTreeNode* m_pRight; }; BinaryTreeNode* ConstructCore(int* startPreorder, int* endPreorder, int* startInorder, int* endInorder) { int rootValue = startPreorder[0]; BinaryTreeNode* root = new BinaryTreeNode(); root->m_nValue = rootValue; root->m_pLeft = root->m_pRight = NULL; if(startPreorder == endPreorder) { if(startInorder == endInorder && *startPreorder == *startInorder) return root; else cout << "Invalid input." << endl; } int* rootInorder = startInorder; while(rootInorder <= endInorder && *rootInorder != rootValue) ++rootInorder; if(rootInorder == endInorder && *rootInorder != rootValue) cout << "Invalid input." << endl; int leftLength = rootInorder - startInorder; int* leftPreorderEnd = startPreorder + leftLength; if(leftLength > 0) root->m_pLeft = ConstructCore(startPreorder+1, leftPreorderEnd, startInorder, rootInorder-1); if(leftLength < endPreorder - startPreorder) root->m_pRight = ConstructCore(leftPreorderEnd+1, endPreorder, rootInorder+1, endInorder); return root; } BinaryTreeNode* Construct(int* preorder, int* inorder, int length) { if(preorder == NULL || inorder == NULL || length <= 0) return NULL; return ConstructCore(preorder, preorder+length-1, inorder, inorder+length-1); } void PreorderTraversal(BinaryTreeNode* T) { if(T) { cout << T->m_nValue << " "; PreorderTraversal(T->m_pLeft); PreorderTraversal(T->m_pRight); } else return; } void InorderTraversal(BinaryTreeNode* T) { if(T) { InorderTraversal(T->m_pLeft); cout << T->m_nValue << " "; InorderTraversal(T->m_pRight); } else return; } int main() { // your code goes here int preOrder[] = {1,2,4,7,3,5,6,8}; int inOrder[] = {4,7,2,1,5,3,8,6}; BinaryTreeNode* tree = Construct(preOrder, inOrder, 8); PreorderTraversal(tree); cout << endl; InorderTraversal(tree); return 0; }
延伸:如果知道中序遍历和后序遍历呢?因为根节点为后续遍历的最后一个数,所以依然可以从中序遍历中划分出根节点的左右子树。如果知道前序遍历和后续遍历呢,能不能重建二叉树?我认为(仅仅是个人认为)是不能的,因为这时找不到左右子树的边界。
下面是知道中序遍历和后续遍历重建二叉树的代码:
BinaryTreeNode* ConstructCore(int* startInorder, int* endInorder, int* startPostorder, int* endPostorder) { int rootValue = *endPostorder; BinaryTreeNode* root = new BinaryTreeNode(); root->m_nValue = rootValue; root->m_pLeft = root->m_pRight = NULL; if(startInorder == endInorder) { if(startPostorder == endPostorder && *startInorder == *startPostorder) return root; else cout << "Invalid input." << endl; } int* rootInorder = startInorder; while(rootInorder <= endInorder && *rootInorder != rootValue) ++rootInorder; if(rootInorder == endInorder && *rootInorder != rootValue) cout << "Invalid input." << endl; int leftLength = rootInorder - startInorder; int* leftPostorderEnd = startPostorder + leftLength - 1; if(leftLength > 0) root->m_pLeft = ConstructCore(startInorder, rootInorder-1, startPostorder, leftPostorderEnd); if(leftLength < endInorder - startInorder) root->m_pRight = ConstructCore(rootInorder+1, endInorder, leftPostorderEnd+1, endPostorder-1); return root; } BinaryTreeNode* Construct(int* inorder, int* postorder, int length) { if(postorder == NULL || inorder == NULL || length <= 0) return NULL; return ConstructCore(inorder, inorder+length-1, postorder, postorder+length-1); }