バイナリツリーのLeetCodeシリーズ

木は、 より一般的なコンピュータサイエンスのデータ構造であり、特別な図で、一緒に組織階層ノードデータ内に配置されます。

一般的に言えばツリーは、次のプロパティがあります。

  1. ツリーは通常、ルートノードのみを持っています。
  2. ルートノードに加えて、すべてのノードには、一意の親ノードを持っています。
  3. ノードがリーフノードと呼ばれている子がありません。
  4. 非呼ばれる内部ノードの少なくとも1つのサブルートノード。
  5. n有するノードからなるツリーn-1の辺と、
  6. 子が親ノード独立観測ノード、ツリーの子ノードは、(サブツリー)特性を有します

通常、階層ツリーを描いた上から下まで、コンピュータサイエンスのツリーのルートノードから開始します。


それはシンプルかつ重要であるため、この論文では、バイナリツリーを説明します

ツリーの各ノードは2人のだけ子の最大を持つことができます

典型的には次のように定義されたノードのバイナリツリーは、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、左の部分木が空になり、そして右の部分木23構図、つまり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で再現

おすすめ

転載: blog.csdn.net/weixin_34245749/article/details/93167936