Given inorder and postorder traversal of a tree, construct the binary tree.
Note:
You may assume that duplicates do not exist in the tree.
利用二叉树的中序和后序的序列还原一颗二叉树。
Note的意思是如果有节点的值是重复的话,那么就无法确保这颗二叉树是唯一的了。
一开始我基本上是毫无头绪。然后思考:
1 画图: 把二叉树和其中序和后序的序列都画出来(这个很重要,我以前有时候总是凭空想象,结果脑子一片空白)
画图也注意把特殊情况都画下来!
2 把问题写下来:怎么构造一个节点?用什么算法?能用递归吗?从什么地方开始构造?根节点?反正能想到的问题都写下来。一定要写下来!
--问题一定要考虑上特殊情况的问题,也一定要写下来。
--其中边界作为主要特殊情况,优先考虑。
3 观察:到底有什么特点是可以利用来解题的呢? A)后序最后一个一定是根节点 B)中序第一个一定是最左边的根节点。C)根节点左右子树是可以分开的
观察总结:那么什么特点是有用的呢?A)C)是有用的;B)好像是无用的。
利用A)C)可以构造递归,每递归一次可以构造一个节点。
4 分治:什么问题需要什么函数解决,一定要把函数分到最小。
做起来又发现很多“小问题”:
1)每个节点必须用new动态分配内存,因为不能返回局部函数指针地址
2)利用C)特性分开左右子树很麻烦,因为要维持4个边界,这样的情况要极其小心,因为很容易下标越界等问题出来的。最好写个更小的程序,先验证过下标处理正确之后再调用该函数。(这个非常麻烦!每次需要打起12分精神)
3)递归参数如何处理?一开始我是想剪裁vector,每次把新的vector装的中序和后序数列递归传递给下一个递归函数,但是这样就会造成大量的参数传递,是个非常糟糕的做法。程序能运行也很糟糕。千万不能用vector作参数递归调用。应该是使用其下标,每次用下标标明需要vector中的哪一部分进行运算!
4)特殊情况处理,4个边界处理,空树,树为一个根节点。
感觉好辛苦,一个编程任务,如临大敌似的!不知道是不是功力不到。
突然想到这是不就像是金庸武功第一层境界:举重若重。
不知道别人是不是到了第二层境界了:举重若轻
第三层境界:举轻若重!
最后:摘叶飞花!
12-4update:再次看看觉得自己已经快要超越第一层境界了。超越了就不置顶了。
struct TreeNode { int val; TreeNode *left; TreeNode *right; TreeNode(int x) : val(x), left(NULL), right(NULL) {} }; class Solution { public: TreeNode *buildTree(vector<int> &inorder, vector<int> &postorder) { return split(inorder, 0, inorder.size(), postorder, 0, postorder.size()); } TreeNode *split(vector<int> &in, int inbeg,int inend, vector<int>& po, int pobeg, int poend) { if(in.empty()) return nullptr; TreeNode *node = new TreeNode(po[poend-1]); if(in.size() <= 1) return node; auto initer = find(in.begin()+inbeg, in.begin()+inend, po[poend-1]); int lnum = initer - in.begin() - inbeg; int rnum = inend - inbeg - lnum - 1; int linbeg = inbeg; int linend = inbeg + lnum; int rinbeg = inbeg + lnum + 1; int rinend = inend; int lpobeg = pobeg; int lpoend = pobeg + lnum; int rpobeg = pobeg + lnum; int rpoend = poend - 1; if(linbeg<linend) node->left = split(in, linbeg, linend, po, lpobeg, lpoend); if(rinbeg<rinend) node->right = split(in, rinbeg, rinend, po, rpobeg, rpoend); return node; } };
测试程序:
#include<iostream> #include<vector> using namespace std; void printTree(TreeNode *root) { if(!root) return; cout<<root->val<<" "; printTree(root->left); printTree(root->right); } int main() { TreeNode *root; TreeNode *re; int a[] = {8,4,2,5,1,6,3,7}; int b[] = {8,4,5,2,6,7,3,1}; vector<int> li(a, a+8); vector<int> ri(b, b+8); Solution solu; root = solu.buildTree(li,ri); printTree(root); cout<<endl; li.clear(); ri.clear(); re = solu.buildTree(li,ri); printTree(re); cout<<endl; system("pause"); return 0; }
最后这些方法论都已经融入思维当中,能本能地逻辑思考了。
这种题目已经是非常轻松的了,那么就算是举重若轻吧。O(∩_∩)O~
//2014-2-16 update TreeNode *buildTree(vector<int> &inorder, vector<int> &postorder) { return build(inorder, 0, inorder.size()-1, postorder, 0, postorder.size()-1); } TreeNode *build(vector<int> &inorder, int in1, int in2, vector<int> &postorder, int po1, int po2) { if (in1 > in2) return nullptr; TreeNode *root = new TreeNode(postorder[po2]); int offset = 0;//注意是offset,一定要多走几个例子,了解好所有情况! for (int i = in1; inorder[i+offset] != postorder[po2]; offset++); root->left = build(inorder, in1, in1+offset-1, postorder, po1, po1+offset-1); root->right = build(inorder, in1+offset+1, in2, postorder, po1+offset, po2-1); return root;//别忘记了return 答案! }
//2014-2-16 update 中序构造 TreeNode *buildTree(vector<int> &inorder, vector<int> &postorder) { return build(inorder, 0, inorder.size()-1, postorder, 0, postorder.size()-1); } TreeNode *build(vector<int> &inorder, int in1, int in2, vector<int> &postorder, int po1, int po2) { if (in1 > in2) return nullptr; int offset = 0;//注意是offset,一定要多走几个例子,了解好所有情况! for (int i = in1; inorder[i+offset] != postorder[po2]; offset++); TreeNode *lt = build(inorder, in1, in1+offset-1, postorder, po1, po1+offset-1); TreeNode *root = new TreeNode(postorder[po2]); root->left = lt; root->right = build(inorder, in1+offset+1, in2, postorder, po1+offset, po2-1); return root; }