一般的に言えばツリーは、次のプロパティがあります。
- ツリーは通常、ルートノードのみを持っています。
- ルートノードに加えて、すべてのノードには、一意の親ノードを持っています。
- ノードがリーフノードと呼ばれている子がありません。
- 非呼ばれる内部ノードの少なくとも1つのサブルートノード。
n
有するノードからなるツリーn-1
の辺と、- 子が親ノード独立観測ノード、ツリーの子ノードは、(サブツリー)特性を有します
通常、階層ツリーを描いた上から下まで、コンピュータサイエンスのツリーのルートノードから開始します。
それはシンプルかつ重要であるため、この論文では、バイナリツリーを説明します
典型的には次のように定義されたノードのバイナリツリーは、left
左の部分木のルートノードを表し、right
右の部分木のルートノード、val
すなわちを、対応する値です。
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
复制代码
主な違いである一次、およびその後の配列:一般的に3つの方法横断バイナリツリーに対する異なるタイミングアクセスノード以下のように、特に擬似コード:
def visit(node):
<< preorder actions >>
left_val = visit(node.left)
<< inorder actions >>
right_val = visit(node.right)
<< postorder actions >>
复制代码
バイナリ検索ツリーは、各ノードのために、特別なバイナリツリーでnode
、次のプロパティがあります。
node
すべての値は、左の子よりも小さいですnode
node
すべての値は、右の子よりも大きいですnode
LeetCodeリンク
別のツリーのサブツリー
二つの非空のバイナリツリーsおよびtが与えられると、ツリーTがSのサブツリーと全く同じ構造及びノード値を持っているかどうかをチェック。Sのサブツリーはツリーがs内のノードと、このノードの子孫のすべてで構成されています。ツリーsはまた、自身のサブツリーとして考えることができます。
サンプル
ツリーの与えられました:
3
/ \
4 5
/ \
1 2
复制代码
与えられた木T:
4
/ \
1 2
复制代码
戻り真のTがSのサブツリーと同じ構造及びノード値を有するので、。
ツリーの与えられました:
3
/ \
4 5
/ \
1 2
/
0
复制代码
与えられた木T:
4
/ \
1 2
复制代码
戻り偽。
問題の解決策
この問題の影響はあなたに2つのツリーを与えることです、ツリーは、ツリーの第2のサブツリーの最初の裁判官はありませんが、この方法は非常に簡単です。まず、第二の値に等しい第1のツリーのルートツリー値のすべてのノードを識別し、次にサブツリーのセットから(ルートノードを含むことができる)のノードが完全に同等の第2ツリーノードツリーを同定します。したがって、この2つのノードを、タイトルに変換与えられ、それは、同じ2つのツリーか否かが判断されます。バイナリ・ツリー再帰構造ので、我々は簡単に一致2つのツリーかどうかを決定するために再帰関数を書くことができる:二つの非空のノードの所与の場合において、根の値をサブツリー、右サブツリー合同を左合同します我々は、等しい、二つのノードのうちの少なくとも一方が空の場合、2つの合同ノードの条件が空でなければなりません。
bool isSameTree(TreeNode *p, TreeNode *q) {
if (p && q) {
bool leftSame = isSameTree(p->left, q->left);
bool rightSame = isSameTree(p->right, q->right);
return leftSame && rightSame && p->val == q->val;
}
return p == NULL && q == NULL;
}
复制代码
主な機能は、主に検索機能を使ってレベルにします。
bool isSubtree(TreeNode* s, TreeNode* t) {
TreeNode *p;
queue<TreeNode*> q;
q.push(s);
vector<TreeNode*> targets;
while (!q.empty()) {
p = q.front();
q.pop();
if (p->val == t->val)
targets.push_back(p);
if (p->left)
q.push(p->left);
if (p->right)
q.push(p->right);
}
for (int i = 0; i < targets.size(); ++i) {
if (isSameTree(t, targets[i])) {
targets.clear();
return true;
}
}
return false;
}
复制代码
二分木の直径
バイナリツリーを考えると、あなたは木の直径の長さを計算する必要があります。二分木の直径は、長さ最長ツリー内の任意の2つのノード間の経路。このパスは、またはルートを通過してもしなくてもよいです。
サンプル
バイナリツリーを考えます
1
/ \
2 3
/ \
4 5
复制代码
経路の長さであるリターン3、[4,2,1,3]又は[5,2,1,3]。
2つのノード間の経路の長さは、それらの間のエッジの数で表されます。
問題の解決策
簡単に言えば、この質問は、ツリー遠い距離にある2つの頂点を見つけることであり、この距離の長さを出力します。
最初に私は、結果を加算後に発見された、別々に計算されたサブツリーの深さについては、単純ではありません。この問題を考えましたか?だから私の最初の提出は、次のように書かれています:
int diameterOfBinaryTree(TreeNode *root) {
if (root == NULL)
return 0;
int ld = depth(root->left), rd = depth(root->right);
return ld + rd;
}
int depth(TreeNode *r) {
if (r == NULL)
return 0;
return max(depth(r->left), depth(r->right)) + 1;
}
int max(int a, int b) {
return a > b ? a : b;
}
复制代码
上記の関数は、depth
ツリーのルートを見つけるために使用されたr
深さ(リーフノードとなるよう深さ1
)、その後、最長パスは、ツリーの深さと子供についてですが、これは間違っています!このため、それが暗示
:そのルートの一部を通る最長パスの木。例を見てください:
1
/
2
/ \
4 5
/ \
6 7
复制代码
上記ルートノードを示すように1
最大3の経路が、木が最長パスであるべきである[6、4、2、5、7]、4の間であるべきです。次のようにだから私は、実際には、特定のアルゴリズムが同じで、私はすべてのツリーノードを横断する唯一のこの時間は上記のコードを変更:上記のコードは、最長路長のルートの世話をする場合は、ここでのコードがすべてです最長経路長は、すべてのツリーノードを計算し、その後、最大値を選択します。
int diameterOfBinaryTree(TreeNode *root) {
if (root == NULL)
return 0;
int ld = diameterOfBinaryTree(root->left), rd = diameterOfBinaryTree(root->right);
return max(max(ld, rd), maxPath(root));
}
int maxPath(TreeNode *r) {
if (r == NULL)
return 0;
int ld = depth(r->left), rd = depth(r->right);
return ld + rd;
}
int depth(TreeNode *r) {
if (r == NULL)
return 0;
return max(depth(r->left), depth(r->right)) + 1;
}
int max(int a, int b) {
return a > b ? a : b;
}
复制代码
ユニークな二分探索木
与えられたn
、によって尋ねた1...n
どのように多くの、バイナリツリーの異なるノードを探して?
サンプル
与えられたn = 3
、ある5
二分探索木の異なる形式は:
1 3 3 2 1
\ / / / \ \
3 2 1 1 3 2
/ / \ \
2 1 2 3
复制代码
問題の解決策
私は、法律で主にこの質問を見つけます。だから、n
ノードの数が、あるf(n)
ことで1...n
二分探索木の異なる形式のノードの数です。
N = 1
n = 1
答えは、はい、唯一の一形態ですf(1) = 1
。
1
复制代码
N = 2
n = 2
状況がよく理解されていますが、ときにその法律を表示されませんf(2) = 2
。
1 2
\ /
2 1
复制代码
N = 3
図中の試料が得られたIは、ルートノードに応じて、小から大サイズの値に、少し配置され、非常に明確ではありません。
1 1 2 3 3
\ \ / \ / /
3 2 1 3 2 1
/ \ / \
2 3 1 2
复制代码
1
、ツリー形式の数と、によって
n = 2
決定された形状の数。
2
、ツリー形式の数と、によって
n = 1
決定された形状の数。
3
ツリー形式の数は、によって決定された場合
n = 2
、決定形状の数。
なぜ彼らは、ルートノードので、言うか1
、左の部分木が空になり、そして右の部分木2
と3
構図、つまりn = 2
場合、しかし根がある2
(すなわちノード)され、スケールの左と右のサブツリー1
、それがあるn = 1
状況。ルートの変更は、その大きさと左右のサブツリーの形状を変更した場合にも気づきます。見バイナリツリー性質を求める、すべてのノードの値は、ルートノードの左サブツリーよりも小さい、すべてのノードの値は、ルートノードの右サブツリーより大きく、ノードの集合における値の範囲のため、{1, 2, ..., n}
ルートノードは、決定左右されている場合、内スケールのサブツリーも同定しました。ルートノードが想定されi
、左サブツリーの次にサイズi - 1
、右サブツリースケールn-i
。上記のように、n = 3, i = 1
ときに左及び大きさ0と2の右サブツリー、それぞれ、と。
我々はそれを結論付けることができます:
f(1) = 1
f(2) = 2
f(3) = f(2) + f(1) + f(2)
复制代码
最高の状態で見てn = 4
状況。
N = 4
このような状況のために多くのスペースを占めているので、それはので、私は、より抽象的に表現されますg(n)
表しn
形式のバイナリツリーの異なるノード集合を、があります。
1 2 3 4
\ / \ / \ /
g(3) g(1) g(2) g(2) g(1) g(3)
复制代码
ここで非常に繊細な場所を見ることができる(ルートノードは2または3)、二分木の左及び右サブツリーの組み合わせの形態であり、左フォームサブツリー及び右サブツリーは独立しており、したがって、ルート・ノードの固定値二つの数の総数が製品形態を形成すべきです。
f(1) = 1
f(2) = f(1) * f(0) + f(0) * f(1)
f(3) = f(2) * f(0) + f(1) * f(1) + f(0) * f(2)
f(4) = f(3) * f(0) + f(2) * f(1) + f(1) * f(2) + f(0) * f(3)
复制代码
私たちは、計算するためには、上記の式から見ることができf(4)
、計算繰り返す必要が2
倍にf(1)、f(2)和f(3)
、そしてf(3)
コールを繰り返す必要がf(1)和f(2)
とき、n
これは非常に時間のかかる計算が比較的大きい場合には、計算の計算結果をキャッシュします。
プログラミングを容易にするために、定義がf(0) = 1
、あります。
class Solution {
public:
/**
* @paramn n: An integer
* @return: An integer
*/
int cache[1000];
Solution() {
for (int i = 0; i < 1000; ++i)
cache[i] = -1;
}
int numTrees(int n) {
// write your code here
if (cache[n] != -1)
return cache[n];
if (n == 0)
return cache[n] = 1;
if (n == 1 || n == 2)
return cache[n] = n;
int r = 0;
for (int i = 1; i <= n; ++i)
r += numTrees(n - i) * numTrees(i - 1);
return cache[n] = r;
}
};复制代码
ます。https://juejin.im/post/5d07cf61518825684b589289で再現