mirror of
https://github.com/krahets/hello-algo.git
synced 2026-07-07 13:14:19 +00:00
Re-translate the Japanese version (#1871)
* Retranslate Japanese docs with GPT-5.4 * Retranslate Japanese code with GPT-5.4
This commit is contained in:
@@ -1,34 +1,34 @@
|
||||
# 二分木の配列表現
|
||||
|
||||
連結リスト表現では、二分木の格納単位はノード`TreeNode`であり、ノードはポインタによって接続されます。連結リスト表現での二分木の基本操作については前の節で紹介しました。
|
||||
連結リスト表現では、二分木の記憶単位はノード `TreeNode` であり、ノード同士はポインタによって接続されます。前節では、連結リスト表現における二分木の各種基本操作を紹介しました。
|
||||
|
||||
では、配列を使って二分木を表現することはできるでしょうか?答えはイエスです。
|
||||
では、配列で二分木を表現できるでしょうか?答えはもちろん可能です。
|
||||
|
||||
## 完全二分木の表現
|
||||
## 充足二分木を表現する
|
||||
|
||||
まず簡単なケースから分析してみましょう。完全二分木が与えられたとき、レベル順探索の順序に従ってすべてのノードを配列に格納し、各ノードは一意の配列インデックスに対応します。
|
||||
まずは簡単な例を考えます。与えられた 1 本の充足二分木について、すべてのノードをレベル順走査の順に配列へ格納すると、各ノードは一意な配列インデックスに対応します。
|
||||
|
||||
レベル順探索の特性に基づいて、親ノードのインデックスとその子ノードの間の「マッピング公式」を導き出すことができます:**ノードのインデックスが$i$の場合、その左の子のインデックスは$2i + 1$、右の子のインデックスは$2i + 2$です**。下図は、さまざまなノードのインデックス間のマッピング関係を示しています。
|
||||
レベル順走査の性質に基づくと、親ノードのインデックスと子ノードのインデックスの間にある「対応式」を導けます。**あるノードのインデックスが $i$ なら、その左子ノードのインデックスは $2i + 1$ 、右子ノードのインデックスは $2i + 2$ です**。以下の図は、各ノードインデックス間の対応関係を示しています。
|
||||
|
||||

|
||||

|
||||
|
||||
**マッピング公式は、連結リストのノード参照(ポインタ)と同様の役割を果たします**。配列内の任意のノードが与えられたとき、マッピング公式を使用してその左(右)の子ノードにアクセスできます。
|
||||
**対応式は、連結リストにおけるノード参照(ポインタ)と同じ役割を果たします**。与えられた配列内の任意のノードについて、この対応式を使えばその左(右)子ノードにアクセスできます。
|
||||
|
||||
## 任意の二分木の表現
|
||||
## 任意の二分木を表現する
|
||||
|
||||
完全二分木は特別なケースです。二分木の中間レベルには多くの`None`値が存在することがよくあります。レベル順探索のシーケンスにはこれらの`None`値が含まれないため、このシーケンスだけに依存して`None`値の数と分布を推測することはできません。**つまり、複数の二分木構造が同じレベル順探索シーケンスと一致する可能性があります**。
|
||||
充足二分木は特殊なケースであり、一般の二分木では中間層に多数の `None` が存在することがよくあります。レベル順走査の列にはこれらの `None` が含まれないため、その列だけから `None` の数や分布位置を推定することはできません。**つまり、このレベル順走査列に一致する二分木構造は複数存在し得ます**。
|
||||
|
||||
下図に示すように、完全でない二分木が与えられた場合、上記の配列表現方法は失敗します。
|
||||
次の図のように、非充足二分木が与えられると、上記の配列表現はすでに成り立ちません。
|
||||
|
||||

|
||||

|
||||
|
||||
この問題を解決するために、**レベル順探索シーケンスですべての`None`値を明示的に書き出すことを検討できます**。下図に示すように、この処理後、レベル順探索シーケンスは二分木を一意に表現できます。サンプルコードは以下の通りです:
|
||||
この問題を解決するために、**レベル順走査列にすべての `None` を明示的に書き込む**ことを考えられます。次の図のように、このように処理すればレベル順走査列で二分木を一意に表現できます。コード例は以下のとおりです:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python title=""
|
||||
# 二分木の配列表現
|
||||
# Noneを使用して空のスロットを表現
|
||||
# 空き位置を表すために None を使う
|
||||
tree = [1, 2, 3, 4, None, 6, 7, 8, 9, None, None, 12, None, None, 15]
|
||||
```
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
|
||||
```cpp title=""
|
||||
/* 二分木の配列表現 */
|
||||
// 最大整数値INT_MAXを使用して空のスロットをマーク
|
||||
// int の最大値 INT_MAX を使って空き位置を示す
|
||||
vector<int> tree = {1, 2, 3, 4, INT_MAX, 6, 7, 8, 9, INT_MAX, INT_MAX, 12, INT_MAX, INT_MAX, 15};
|
||||
```
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
|
||||
```java title=""
|
||||
/* 二分木の配列表現 */
|
||||
// Integerラッパークラスを使用してnullで空のスロットをマーク
|
||||
// int のラッパークラス Integer を使えば、null で空き位置を示せる
|
||||
Integer[] tree = { 1, 2, 3, 4, null, 6, 7, 8, 9, null, null, 12, null, null, 15 };
|
||||
```
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
|
||||
```csharp title=""
|
||||
/* 二分木の配列表現 */
|
||||
// nullable int (int?)を使用してnullで空のスロットをマーク
|
||||
// nullable な int? 型を使えば、null で空き位置を示せる
|
||||
int?[] tree = [1, 2, 3, 4, null, 6, 7, 8, 9, null, null, 12, null, null, 15];
|
||||
```
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
|
||||
```go title=""
|
||||
/* 二分木の配列表現 */
|
||||
// any型スライスを使用してnilで空のスロットをマーク
|
||||
// any 型のスライスを使えば、nil で空き位置を示せる
|
||||
tree := []any{1, 2, 3, 4, nil, 6, 7, 8, 9, nil, nil, 12, nil, nil, 15}
|
||||
```
|
||||
|
||||
@@ -68,7 +68,7 @@
|
||||
|
||||
```swift title=""
|
||||
/* 二分木の配列表現 */
|
||||
// optional Int (Int?)を使用してnilで空のスロットをマーク
|
||||
// nullable な Int? 型を使えば、nil で空き位置を示せる
|
||||
let tree: [Int?] = [1, 2, 3, 4, nil, 6, 7, 8, 9, nil, nil, 12, nil, nil, 15]
|
||||
```
|
||||
|
||||
@@ -76,7 +76,7 @@
|
||||
|
||||
```javascript title=""
|
||||
/* 二分木の配列表現 */
|
||||
// nullを使用して空のスロットを表現
|
||||
// null を使って空き位置を表す
|
||||
let tree = [1, 2, 3, 4, null, 6, 7, 8, 9, null, null, 12, null, null, 15];
|
||||
```
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
|
||||
```typescript title=""
|
||||
/* 二分木の配列表現 */
|
||||
// nullを使用して空のスロットを表現
|
||||
// null を使って空き位置を表す
|
||||
let tree: (number | null)[] = [1, 2, 3, 4, null, 6, 7, 8, 9, null, null, 12, null, null, 15];
|
||||
```
|
||||
|
||||
@@ -92,7 +92,7 @@
|
||||
|
||||
```dart title=""
|
||||
/* 二分木の配列表現 */
|
||||
// nullable int (int?)を使用してnullで空のスロットをマーク
|
||||
// nullable な int? 型を使えば、null で空き位置を示せる
|
||||
List<int?> tree = [1, 2, 3, 4, null, 6, 7, 8, 9, null, null, 12, null, null, 15];
|
||||
```
|
||||
|
||||
@@ -100,7 +100,7 @@
|
||||
|
||||
```rust title=""
|
||||
/* 二分木の配列表現 */
|
||||
// Noneを使用して空のスロットをマーク
|
||||
// None を使って空き位置を示す
|
||||
let tree = [Some(1), Some(2), Some(3), Some(4), None, Some(6), Some(7), Some(8), Some(9), None, None, Some(12), None, None, Some(15)];
|
||||
```
|
||||
|
||||
@@ -108,7 +108,7 @@
|
||||
|
||||
```c title=""
|
||||
/* 二分木の配列表現 */
|
||||
// 最大int値を使用して空のスロットをマーク、したがってノード値はINT_MAXであってはならない
|
||||
// int の最大値で空き位置を示すため、ノード値は INT_MAX であってはならない
|
||||
int tree[] = {1, 2, 3, 4, INT_MAX, 6, 7, 8, 9, INT_MAX, INT_MAX, 12, INT_MAX, INT_MAX, 15};
|
||||
```
|
||||
|
||||
@@ -116,43 +116,45 @@
|
||||
|
||||
```kotlin title=""
|
||||
/* 二分木の配列表現 */
|
||||
// nullを使用して空のスロットを表現
|
||||
val tree = mutableListOf( 1, 2, 3, 4, null, 6, 7, 8, 9, null, null, 12, null, null, 15 )
|
||||
// null を使って空き位置を表す
|
||||
val tree = arrayOf( 1, 2, 3, 4, null, 6, 7, 8, 9, null, null, 12, null, null, 15 )
|
||||
```
|
||||
|
||||
=== "Ruby"
|
||||
|
||||
```ruby title=""
|
||||
|
||||
### 二分木の配列表現 ###
|
||||
# nil を使って空き位置を表す
|
||||
tree = [1, 2, 3, 4, nil, 6, 7, 8, 9, nil, nil, 12, nil, nil, 15]
|
||||
```
|
||||
|
||||

|
||||

|
||||
|
||||
注目すべきは、**完備二分木は配列表現に非常に適している**ということです。完備二分木の定義を思い出すと、`None`は最下位レベルでのみ、かつ右側に向かって現れます。**つまり、すべての`None`値は確実にレベル順探索シーケンスの最後に現れます**。
|
||||
補足すると、**完全二分木は配列による表現に非常に適しています**。完全二分木の定義を振り返ると、`None` は最下層の右側にしか現れないため、**すべての `None` は必ずレベル順走査列の末尾に現れます**。
|
||||
|
||||
これは、配列を使用して完備二分木を表現する際、すべての`None`値の格納を省略できることを意味し、非常に便利です。下図に例を示します。
|
||||
つまり、完全二分木を配列で表す場合は、すべての `None` の格納を省略できるため、非常に便利です。次の図に例を示します。
|
||||
|
||||

|
||||

|
||||
|
||||
以下のコードは、配列表現に基づく二分木を実装し、次の操作を含みます:
|
||||
以下のコードでは、配列ベースで表現した二分木を実装しており、次の操作を含みます。
|
||||
|
||||
- ノードが与えられたとき、その値、左(右)の子ノード、および親ノードを取得する。
|
||||
- 前順、中順、後順、およびレベル順探索シーケンスを取得する。
|
||||
- あるノードが与えられたとき、その値、左(右)子ノード、親ノードを取得する。
|
||||
- 前順走査、中順走査、後順走査、レベル順走査の列を取得する。
|
||||
|
||||
```src
|
||||
[file]{array_binary_tree}-[class]{array_binary_tree}-[func]{}
|
||||
```
|
||||
|
||||
## 利点と制限
|
||||
## 利点と制約
|
||||
|
||||
二分木の配列表現には以下の利点があります:
|
||||
二分木の配列表現には主に次の利点があります。
|
||||
|
||||
- 配列は連続したメモリ空間に格納されるため、キャッシュフレンドリーで、より高速なアクセスと探索が可能です。
|
||||
- ポインタを格納する必要がないため、スペースを節約できます。
|
||||
- ノードへのランダムアクセスが可能です。
|
||||
- 配列は連続したメモリ空間に格納されるため、キャッシュ効率が高く、アクセスと走査が速い。
|
||||
- ポインタを格納する必要がなく、比較的省スペースである。
|
||||
- ノードへのランダムアクセスが可能である。
|
||||
|
||||
しかし、配列表現にはいくつかの制限もあります:
|
||||
ただし、配列表現にはいくつかの制約もあります。
|
||||
|
||||
- 配列格納には連続したメモリ空間が必要なため、大量のデータを持つ木の格納には適していません。
|
||||
- ノードの追加や削除には配列の挿入や削除操作が必要で、効率が低くなります。
|
||||
- 二分木に多くの`None`値がある場合、配列に含まれるノードデータの割合が低くなり、空間利用率が低下します。
|
||||
- 配列による格納には連続したメモリ空間が必要なため、データ量が大きすぎる木の格納には向かない。
|
||||
- ノードの追加と削除は配列の挿入・削除操作で実現する必要があり、効率は低い。
|
||||
- 二分木に大量の `None` が存在すると、配列に占める実ノードデータの比率が低くなり、空間利用率も低下する。
|
||||
|
||||
Reference in New Issue
Block a user