mirror of
https://github.com/krahets/hello-algo.git
synced 2026-07-22 19:36:06 +00:00
87 lines
5.1 KiB
Markdown
87 lines
5.1 KiB
Markdown
# 演習
|
|
|
|
## 確認問題
|
|
|
|
### 10 をヒープへ追加した後の調整
|
|
|
|
配列 `[9, 7, 8, 3, 5]` は最大ヒープを表しています。ここに 10 を追加します。
|
|
|
|
<!-- numbered-subquestions -->
|
|
|
|
1. まず 10 を配列の末尾に追加すると、その親ノードの値はいくつですか?
|
|
2. 新しいノードから下から上へヒープ化し、交換するたびに配列を書いてください。
|
|
3. 最終的なヒープ頂点の要素はいくつですか?交換は全部で何回行われますか?
|
|
|
|
??? success "解答"
|
|
|
|
1. 10 を追加した後のインデックスは 5 で、その親ノードのインデックスは
|
|
$\lfloor(5-1)/2\rfloor=2$ です。親ノードの値は 8 です。
|
|
|
|
2. 10 は 8 より大きいため、1 回目の交換後は `[9, 7, 10, 3, 5, 8]` です。
|
|
10 は親ノードの 9 よりも大きいため、2 回目の交換後は `[10, 7, 9, 3, 5, 8]` です。
|
|
これで 10 が根ノードに到達したため、ヒープ化は終了します。
|
|
|
|
3. 最終的なヒープ頂点は 10 で、交換回数は 2 回です。
|
|
|
|
### 最小ヒープの親子関係を確認する
|
|
|
|
配列 `[1, 4, 3, 7, 6, 2]` は完全二分木を表しています。最小ヒープでは、すべての親ノードがその子ノード以下でなければなりません。
|
|
インデックス $i$ に対して、左右の子ノードのインデックスはそれぞれ $2i+1$ と $2i+2$ です。
|
|
|
|
<!-- numbered-subquestions -->
|
|
|
|
1. インデックス 2 の子ノードのインデックスと値は、それぞれいくつですか?
|
|
2. インデックス 2 にあるノードの値は 3 です。子ノードとの間で最小ヒープの条件に反していますか?反している場合、どの 2 要素を交換しますか?
|
|
3. 問い 2 の判断に基づき、条件に反している場合は交換後の配列を書き、反していない場合は交換が不要な理由を説明してください。最後に、ほかの親子関係も確認してください。
|
|
|
|
??? success "解答"
|
|
|
|
1. インデックス 2 の左子ノードはインデックス 5、値は 2 です。右子ノードのインデックスは 6 ですが、配列の長さは 6 なので、右子ノードは存在しません。
|
|
|
|
2. 親ノードの値 3 は子ノードの値 2 より大きく、最小ヒープの条件に反しています。そのため、インデックス 2 とインデックス 5 の要素を交換します。
|
|
|
|
3. 交換後の配列は `[1, 4, 2, 7, 6, 3]` です。順に確認すると、
|
|
`1 ≤ 4`、`1 ≤ 2`、`4 ≤ 7`、`4 ≤ 6`、`2 ≤ 3` です。
|
|
すべての親ノードがその子ノード以下なので、現在は最小ヒープの条件を満たしています。
|
|
|
|
### 最小ヒープで最大の 3 つの数を保持する
|
|
|
|
データストリーム `[4, 1, 7, 3, 8]` から最大の 3 つの数を保持するために、大きさが 3 以下の最小ヒープを管理します。
|
|
|
|
まず、最初の 3 つの数を順に最小ヒープへ入れます。ヒープが満杯になった後は、新しい数を 1 つ読み込むたびに、
|
|
その数がヒープ頂点より大きければヒープ頂点を削除して新しい数を入れ、そうでなければヒープを変更しません。
|
|
|
|
各数を読み込んだ後、ヒープに保持されている数とヒープ頂点を書いてください。
|
|
保持されている数は集合として書けばよく、ヒープ配列内での並びを書く必要はありません。
|
|
|
|
??? success "解答"
|
|
|
|
各数を読み込んだ後の結果は、次のとおりです。
|
|
|
|
| 読み込んだ数 | 保持されている数 | ヒープ頂点 |
|
|
| --- | --- | --- |
|
|
| 4 | `{4}` | 4 |
|
|
| 1 | `{1, 4}` | 1 |
|
|
| 7 | `{1, 4, 7}` | 1 |
|
|
| 3 | `{3, 4, 7}` | 3 |
|
|
| 8 | `{4, 7, 8}` | 4 |
|
|
|
|
ヒープが満杯になった後、ヒープ頂点は、現在保持している数のうち最も小さい数です。新しい数がヒープ頂点より大きい場合に限り、
|
|
ヒープ頂点を新しい数に置き換えます。最終的に保持される `{4, 7, 8}` は、まさに最大の 3 つの数です。
|
|
|
|
## プログラミング演習
|
|
|
|
### 配列内の k 番目に大きい要素
|
|
|
|
整数配列 `nums` と整数 $k$ が与えられます。ここで $1 \le k \le n$ で、$n$ は配列の長さです。配列を大きい順に並べたとき、$k$ 番目にある要素を返してください。
|
|
|
|
重複する要素は別々に数えます。たとえば、`[5, 5, 2]` で 2 番目に大きい要素も 5 です。大きさが $k$ 以下の最小ヒープを使って求めてください。
|
|
|
|
??? tip "解法のヒント"
|
|
|
|
1. k 番目に大きい要素は、最大の k 個の数のうち最も小さい数です
|
|
2. 各数を最小ヒープへ入れ、大きさが k を超えたら最小値を取り出します
|
|
3. 走査を終えると、ヒープには最大の k 個の数が残り、ヒープ頂点が答えになります
|
|
|
|
[LeetCode](https://leetcode.com/problems/kth-largest-element-in-an-array/){ .rounded-button .exercise-button target="_blank" rel="noopener noreferrer" }
|