題目要求:{1,2,3…N}表示一棵二叉樹中序遍歷結果,
1.求有多少種可能的二叉樹結構?
2.返回所有可能的二叉樹結構的頭結點?
題目解析:
1.一個關鍵點是:中序遍歷一棵二叉樹結果有序無重複,那麼這棵二叉樹必然是搜索二叉樹。根據搜索二叉樹的性質,結點1一定沒有左子樹,所以結點1爲頭結點可能的二叉樹結構數目取決於其右子樹的結點數目。假設num(x)表示x個節點可能的二叉樹結構數目,那麼以1爲頭結點的二叉樹結構數目就是num(N-1);現在以任意節點i爲頭結點的可能二叉樹結構的個數取決於其左子樹和右子樹的可能數的乘積,即num(i-1)*num(N-i),Okay,說到這裏第一個問題已經可以解決了,遞歸加動態規劃降低時間複雜度解決此問題。
下面看一下代碼,No code say what:
// 問題1
int numTree(int n) {
vector<int> num;
num[0] = 1;
// 時間複雜度O(N^2)
for (int i = 1; i < n + 1; ++i) {
for (int j = 1; j < i + 1; ++j) {
num[i] += num[j - 1] * num[i -j];
}
}
return num[n];
}
2.第二個問題與第一個問題類似,但要比第一個問題複雜一些。現在要返回所有可能的二叉樹結構的頭結點,那麼就要把每一種可能的二叉樹結構構造出來。現在假設用結點i是頭結點,把左子樹所有可能的頭結點存在listLeft中,把所有可能的右子樹可能的頭結點存儲在rightLeft中,那麼所有的左子樹頭結點和右子樹頭結點組合就能產生出一種可能的二叉樹結構,把所有的可能結果存儲在listRes中,返回即可。
首先定義二叉樹結點結構,和一個拷貝結點方法,該拷貝方法可以拷貝整棵樹。
struct Node {
int value;
Node *left;
Node *right;
};
Node * cloneNode(Node * node) {
if (!node) {
return NULL;
}
Node * N = new Node();
N->value = node->value;
N->left = cloneNode(node->left);
N->right = cloneNode(node->right);
return N;
}
然後看一下問題2的生成算法:
// 問題2
bool generateTrees(int n, list<Node*> &res) {
if (0 > n) {
return false;
}
generate(1, n, res);
return true;
}
/* param<start> : 以start爲開始頭結點
* param<end> : 以end爲結束頭結點
*/
void generate(int start, int end, list<Node*> &res) {
if (start > end) {
res.push_back(NULL);
return ;
}
for (int i = start, i <= end; ++i) {
Node node;
list<Node*> listLeft;
list<Node*> listRight;
generate(start, i - 1, listLeft);
generate(i + 1, end, listRight);
node.value = i;
list<Node>::iterator iterLeft;
list<Node>::iterator iterRight;
for (iterLeft = listLeft.begin(); iterLeft != listLeft.end(); iterLeft++) {
node.left = *iterLeft;
for (iterRight = listRight.begin(); iterRight != listRight.end(); iterRight++) {
node.right = *iterRight;
res.push_back(cloneNode(&node));
}
}
}
return ;
}
Okay,到這裏這個問題已經完美解決了…
路漫漫其修遠兮,吾將上下而…