ゼロスピードから二分木を始めるための「基本的なデータ構造」

「ソードチーは直径30,000マイルで、ソードライトは19大陸で寒いです。」

コンテンツ

1.この章の焦点

2.ツリー

2.1ツリーの概念

2.2樹木に関する基本的な知識

3.二分木

3.1二分木の概念

3.2特別な二分木

3.2.1完全な二分木

 3.2.2完全な二分木

 3.3二分木の特性

3.4二分木の特性に関連する多肢選択問題に関する演習

3.5二分木チェーン構造の実装

3.5.1二分木チェーン構造のトラバース

3.5.2多肢選択式トレーニング

4.再帰

4.1再帰の概念

4.2再帰の中心的な意味

4.3再帰を書く方法は?

5つの二分木一般的なOJの質問の練習

5.1二分木のプレオーダートラバーサル(Likou)

5.2二分木の最大深さ(フォースバックル)

5.3平衡二分木(フォースバックル)


1.この章の焦点

  1. 木の概念を紹介する
  2. 二分木の概念を紹介します
  3. 再帰の要点をマスターする
  4. ojの質問と二分木の詳細な説明

2.ツリー

2.1ツリーの概念

ツリーは、n個のノードの有限集合Tです。n = 0の場合、それは空のツリーと呼ばれます。n> 0の場合、セットは次の条件を満たす。

①直接の先行はないが、0以上の直接の後続があるroot(root)と呼ばれるノードが存在する必要があります。

②残りのn-1ノードはm(m> = 0)の互いに素な有限集合T1、T2、T3、... Tmに分割できます。ここで、Tiはルートのサブツリーと呼ばれるツリーです。各サブツリーのルートノードには、直前の先行ノードが1つだけありますが、直後の後続ノードは0個以上あります。

(ツリーは再帰的に定義されます)

次の図は、逆ツリーに似たツリーの論理構造を示しています。

 注:ノードのないツリーも空のツリーと呼ばれるツリーであり、ノードが1つしかないツリーもツリーです。

テスト1:

次のツリーですか、それとも非ツリーですか? 

回答:これはツリーではなく、この条件は満たされていません。各サブツリーのルートノードには、直接の先行ノードが1つだけあります。つまり、サイクルを形成できません。

たとえば、G、A、Dの2つの直接の先行要素があるため、構造はツリーではありません。

2.2樹木に関する基本的な知識

この木を例に取ってください

ノードの次数ノードに含まれるサブツリーの数は、ノードの次数と呼ばれます。上の図に示すように、Aは3です。

リーフノードまたはターミナルノード次数が0のノードはリーフノードと呼ばれます。上記のように、K、L、F、G、M、I、Jです。ノードはすべてリーフノードです。
親ノードまたは親ノードノードに子ノードが含まれている場合、このノードはその子ノードの親ノードと呼ばれます。上の図に示すように、AはBの親ノード、CはGの親ノードです。

子ノードまたは子ノードノードに含まれるサブツリーのルートノードは、ノードの子ノードと呼ばれます。上記のように、BはAの子ノード、GはCの子ノードです。

兄弟ノード同じ親ノードを持つノードは兄弟ノードと呼ばれます。上の図に示すように、BとCは兄弟ノードあり、EやGなどのいとこは兄弟ノードとは見なされません。

ツリーの次数ツリーでは、最大ノードの次数はツリーの次数と呼ばれます。上記のように、ツリーの次数は3です。

ノードの階層ルートから始まり、ルートが最初のレイヤー、ルートの子ノードが2番目のレイヤーというように続きます。
ツリーの高さまたは深さツリー内のノードの最大レベル;上記のように:ツリーの高さは4です。
ノードの祖先ルートからノードへのブランチ上のすべてのノード。上記のように:Aはすべてのノードの祖先です。

3.二分木

3.1二分木の概念

概念:二分木はノードの有限集合であり、空であるか、ルートノードと左右のサブツリーと呼ばれる2つの二分木で構成されます。

特徴:

1.各ノードには最大で2つのサブツリーがあります。つまり、二分木には次数が2より大きいノードはありません。
2.二分木のサブツリーは左右に分かれており、サブツリーの順序を逆にすることはできません。

以下は通常の二分木です

3.2特別な二分木

3.2.1完全な二分木

コンセプト:完全な二分木は二分木です。各層のノード数が最大値に達すると、二分木は完全な二分
木になります。つまり、二分木にKレベルがあり、ノードの総数が(2 ^ k)-1の場合、それは完全な二分木です。

 3.2.2完全な二分木

 3.3二分木の特性

二分木のプロパティ
1.ルートノードのレベル数が1として指定されている場合、空でない二分木のi番目のレベルには最大で2 ^(i-1)個のノードがあります。
2.ルートノードのレベルが1として指定されている場合、深さhの二分木のノードの最大数は2^h-1です。
3.任意の二分木で、次数0のリーフノードの数がn0で、次数2の分岐ノードの数がn2の場合、n0 = n2+1です。
4.ルートノードのレベルが1として指定されている場合、 n個のノードを持つ
完全な二分木の深さh = Log2(N + 1)。

3.4二分木の特性に関連する多肢選択問題に関する演習

1.二分木には合計399個のノードがあり、そのうちの次数2のノードは199個あり、二分木のリーフノードの数は()
Aです。そのような二分木はありません
B 200
C 198
D 199

2. 2nノードの完全な二分木では、リーフノードの数は()
A n
B n + 1
C n-1
D n/2です。

解析:

1:Bを選択します。上記のプロパティ3に従って、n0 = n2 + 1 = 200です。つまり、リーフノードの数は200です(リーフノードは次数0のノードです)。

2:Aを選択します。二分木には0度、1度、2度のノードしかないため、n2 + n1 + n0 = 2 * n

また、n0 = n2 + 1であるため、2 * n0 + n1-1 = 2 * n

ツリーは完全な二分木であるため、n1は0または1のみになります。

n1が0の場合、n0は小数であり、この場合は省略されます。

したがって、n1は1に等しいので、n0はnに等しくなります。

3.5二分木チェーン構造の実装

3.5.1二分木チェーン構造のトラバース

いわゆるトラバーサル(トラバーサル)とは、特定の検索ルートをたどり、ツリー内の各ノードに1回だけ順番にアクセスすることを指します。アクセス
ノードによって実行される操作は、特定のアプリケーションの問題によって異なります。トラバーサルは、バイナリツリーでの最も重要な操作の1つであり、バイナリツリーで
の他の操作の基礎となります。

トラバーサル

1. NLR:プレオーダートラバーサル(プレオーダートラバーサルとも呼ばれます)-ルートノードにアクセスする操作は、その左右のサブツリーをトラバースする前に発生します。
2. LNR:インオーダートラバーサル-ルートノードにアクセスする操作は、その左右のサブツリーをトラバースする途中で発生します。
3. LRN:ポストオーダートラバーサル-ルートノードにアクセスする操作は、その左右のサブツリーをトラバースした後に発生します。
訪問したノードはサブツリーのルートである必要があるため、N(ノード)、L(左のサブツリー)、およびR(右のサブツリー)はルート、ルートの左側のサブツリー、およびルートの右側のサブツリーとして解釈できます。NLR、LNR、およびLRNは、それぞれ、ルート前トラバーサル、ルート中トラバーサル、およびルート後トラバーサルとも呼ばれます。

4.レベル順トラバーサル:レベル順トラバーサル:プレオーダートラバーサル、インオーダートラバーサル、およびポストオーダートラバーサルに加えて、バイナリツリーでレベルオーダートラバーサルを実行することもできます。バイナリツリーのルートノードのレイヤー数が1であり、レベル順トラバーサルがバイナリツリーのルートノードから開始し、最初に最初のレイヤーのルートノードにアクセスし、次に上のノードにアクセスするとします。 2番目のレイヤーを左から右に、次に3番目のレイヤーをレイヤーのノードというように、上から下、左から右にレイヤーごとにツリーのノードにアクセスするプロセスは、レイヤー順序トラバーサルです。
 

レイヤー順序トラバーサルグラフ:

 

演習:次のプレオーダー/インオーダー/ポストオーダー/レベルオーダートラバーサルを記述してください

 プレオーダートラバーサル:

A-> B-> D-> NULL-> NULL-> E-> H-> NULL-> NULL-> I-> NULL-> NULL-> C-> F-> NULL-> NULL-> G

-> NULL-> NULL

順序のないトラバーサル:

NULL-> D-> NULL-> B-> NULL-> H-> NULL-> E-> NULL-> I-> NULL-> A-> NULL-> F-> NULL-> C-> NULL

-> G-> NULL

その後のトラバーサル:

NULL-> NULL-> D-> NULL-> NULL-> H-> NULL-> NULL-> I-> E-> B-NULL-> NULL-> F-> NULL-> NULL

-> G-> C-> A

層序解説历:A-> B-> C-> D-> E-> F-> G-> NULL-> NULL-> H-> I-> NULL-> NULL-> NULL-> NULL

3.5.2多肢選択式トレーニング

1.レイヤー(同じレイヤーで左から右へ)によって出力される完全な二分木のシーケンスはABCDEFGHです。この完全な二分木の事前注文シーケンスは次のとおりです。(

A ABDHECFG
B ABCDEFGH
C HDBEAFCG
D HDEBFGCA

2.バイナリツリーのプレオーダートラバーサルとインオーダートラバーサルは次のとおりです。プレオーダートラバーサル: EFHIGJK ;インオーダートラバーサル:HFIEJKG。次に、バイナリツリーのルートノードは

AE
BFCGDHです。

3.レッスン1:badceのバイナリツリーのインオーダートラバーサルシーケンスとポストオーダートラバーサルシーケンス:bdecaを想定すると、バイナリツリーのプレオーダートラバーサルシーケンスは____になります。
A adbce
B decab
C debac
D abcde

1.A

分析:二分木の構造は、レベル順序トラバーサルを介して描画でき、二分木の構造に従って事前順序シーケンスを取得できます。
2.A

分析:プレオーダートラバーサルによると、Eがルートです。
3.D

分析:ポストオーダートラバーサルによると、バイナリツリーのルートはaであり、後続のトラバーサルでは、bが左側のサブツリー、dceが右側のサブツリーであることがわかります。二分木の構造を描くことができ、次にプレオーダートラバーサルシーケンスを取得することができます。


4.再帰

4.1再帰の概念

再帰の概念:再帰、つまり、関数は実行中のプロセス中に自分自身を呼び出します。

再帰を形成するために必要な条件:
1。サブ問題は元の問題と同じであり、より単純である必要があります
。2.それ自体を無期限に呼び出すことはできず、出口が必要であり、非再帰に単純化できます。処理。

4.2再帰の中心的な意味

問題をいくつかの同一のサブ問題に分割し、大きな問題をサブ問題に引き渡し、サブ問題をサブサブ問題に引き渡して実行します。タスクは再帰的な終了です。

フィボナッチ数を例にとってみましょう。

フィボナッチ数列のn番目の数の値を要求します

n-1とn-2の値を見つけるために単純化する

次に、n-1とn-2の値は、n-2とn-3およびn-3とn-4の値に簡略化されます。

.........。

これは、nをいくつかの同一のサブ問題に減らすためのものです。サブ問題は次のとおりです。これらはすべて、最初の2つの数値の値を必要とします。

n == 1まで再帰的に、次に1を返すか、n == 0まで再帰して、1を返します。これは再帰の終了であり、タスクを完了するために最終的に配置されるステートメントでもあります。

4.3再帰を書く方法は?

      3つのステップ:

  1. 問題をいくつかの同一のサブ問題に減らします。
  2. 再帰的な出口を書く
  3. 関数自体を呼び出し、

以下は、フィボナッチ数列を実装するための再帰的な方法です。

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int fabonacci(int n)
{
	if (n < 3)//递归出口
	{
		return 1;
	}
	return fabonacci(n - 1) + fabonacci(n - 2);//根据函数功能实现函数
}
int main()
{
	//1 1 2 3 5 8 13
	int n = 7;
	printf("%d\n", fabonacci(n));
	return 0;
}

5つの二分木一般的なOJの質問の練習

5.1二分木のプレオーダートラバーサル(Likou)

二分木のルートノードを 提供し、そのノード値のプレオーダートラバーサルをroot 返し ます 

入力: root = [1、null、2,3]
出力: [1,2,3]

実装するインターフェース:

int* preorderTraversal(struct TreeNode* root, int* returnSize)
{

}

アイデア:ヒープ領域にスペースを適用してノード値を格納し、再帰的なアイデアを使用して、ノード1を最初にヒープスペースに配置し、次に左側のサブツリーをスペースに配置してから、右側のサブツリーに配置します。

再帰的な書き込み:

最初に再帰出口を記述します。

if(root==NULL)
{
    return;
}

ルートノードに配置

ret[*returnSize]=root->val;
*returnSize=(*returnSize)+1;

最後に、残りの左サブツリーと右サブツリーをトラバースする必要があります。残りのトラバーサル問題を解決する関数が必要です。この問題を解決する関数は、作成した関数です。つまり、関数自体を呼び出すことができます。
もう1つの質問は、ヒープ領域にどのくらいのスペースを開く必要があるかです。ここでは、再帰を使用して、バイナリツリー内のノードの数を見つけます。

3つのステップで再帰を記述します

1.問題をいくつかの小さな問題に変えます。各ツリーは、次の条件を満たす必要があります。ツリー内のノード数==1+左側のサブツリー内のノード数+右側のサブツリー内のノード数。

2.再帰的出口を書き込む:ツリーが空の場合は、0を返します。

3.関数自体を呼び出します。

 int size(struct TreeNode* root)
 {
     return root==NULL?0:1+size(root->left)+size(root->right);
 }
 void traversal(struct TreeNode* root,int* returnSize,int* ret)
 {
    if(root==NULL)
    {
        return;
    }
    ret[*returnSize]=root->val;
    *returnSize=(*returnSize)+1;
    traversal(root->left,returnSize,ret);
    traversal(root->right,returnSize,ret);
 }
 int size(struct TreeNode* root)
 {
     return root==NULL?0:1+size(root->left)+size(root->right);
 }
int* preorderTraversal(struct TreeNode* root, int* returnSize)
{
    int s=size(root);
    *returnSize=0;
    int* ret=malloc(sizeof(int)*s);
    traversal(root,returnSize,ret);
    return ret;
}

5.2二分木の最大深さ(フォースバックル)

二分木が与えられたら、その最大の深さを見つけます。

二分木の深さは、ルートノードから最も遠いリーフノードまでの最長パス上のノードの数です。

説明:リーフ・ノードは、子ノードを持たないノードです。

例:
二分木[3,9,20、null、null、15,7]が与えられた場合、

  3
   / \
  920
    / \
   15 7
最大深度3を返します。

アイデア:直接再帰

3つのステップ:

1.問題をいくつかの同一の小さな問題に変えます。各ツリーは次の条件を満たす必要があります。最大深度==1+(左側のサブツリーの高さ>右側のサブツリーの高さ?左側のサブツリーの高さ:右側の高さサブツリー)。

2.再帰的終了。ノードのアドレスが空の場合、0を返します。

3.関数自体を呼び出します。

int maxDepth(struct TreeNode* root)
{
    if(root==NULL)
    {
        return 0;
    }
    return 1+fmax(maxDepth(root->left),maxDepth(root->right));
}

 5.3平衡二分木(フォースバックル)

二分木が与えられたら、それが高度にバランスの取れた二分木であるかどうかを判断します。

この問題では、高さのバランスが取れた二分木は次のように定義されます。

二分木の各ノード の左右のサブツリー間の高さの差の絶対値は1を超えません。

 

入力: root = [3,9,20、null、null、15,7]
出力: true

アイデア:直接再帰

3つのステップ:

1.問題をいくつかの同一の小さな問題に変え、各ツリーが満たす必要があります。ルートノードは平衡ツリーの条件を満たす必要があります&&左サブツリーは平衡ツリーです&&右サブツリーは平衡ツリーです。

2.再帰的終了。ノードのアドレスが空の場合、trueを返します。

3.関数自体を呼び出します。

int height(struct TreeNode* root)
{
    if(root==NULL)
    {
        return 0;
    }
    return 1+fmax(height(root->left),height(root->right));
}
bool isBalanced(struct TreeNode* root)
{
    if(root==NULL)
    {
        return true;
    }
    return abs(height(root->left)-height(root->right))<=1 && 
    isBalanced(root->left) && 
    isBalanced(root->right);
}

ご覧いただきありがとうございます。いいね、コメント、収集を歓迎します。ご不明な点がございましたら、作者までお気軽にご連絡ください。

おすすめ

転載: blog.csdn.net/m0_62171658/article/details/123433827