mirror of
https://github.com/krahets/hello-algo.git
synced 2026-07-22 03:16:08 +00:00
100 lines
5.9 KiB
Markdown
100 lines
5.9 KiB
Markdown
# 演習
|
||
|
||
## 確認問題
|
||
|
||
### スタックとキューではどれが先に取り出されるか
|
||
|
||
空のスタック `S` と空のキュー `Q` を用意し、それぞれに同じ次の操作を行います。
|
||
|
||
手順 1:`A` を追加する。
|
||
手順 2:`B` を追加する。
|
||
手順 3:要素を 1 つ取り出して記録する。
|
||
手順 4:`C` を追加する。
|
||
手順 5:中身が空になるまで要素を取り出し、その順序を記録する。
|
||
|
||
`S` と `Q` から要素が取り出される順序をそれぞれ書き、「後入れ先出し」または「先入れ先出し」を使って違いを説明してください。
|
||
|
||
??? success "解答"
|
||
|
||
スタック `S` から取り出される順序は `B、C、A` です。`A、B` を追加した後、最後に追加した `B` を先に取り出します。次に `C` を追加し、
|
||
その後は `C、A` の順に取り出されます。これは「後入れ先出し」を表しています。
|
||
|
||
キュー `Q` から取り出される順序は `A、B、C` です。`A、B` を追加した後、最初に追加した `A` を先に取り出します。
|
||
次に `C` を追加し、その後は `B、C` の順に取り出されます。これは「先入れ先出し」を表しています。
|
||
|
||
### キューの末尾が配列の末尾を越えたら
|
||
|
||
長さ 5 の環状配列を使ってキューを実装します。配列のインデックスは `0~4` です。
|
||
現在、`front = 3`、`size = 2` で、キュー内の `A、B` はそれぞれインデックス 3、4 にあります。
|
||
|
||
<!-- numbered-subquestions -->
|
||
|
||
1. 「`C` をエンキューする」とき、`C` はどのインデックスに置きますか?エンキュー後の `size` はいくつですか?
|
||
2. 続けて 1 回デキューすると、どの要素が取り出されますか?新しい `front` と `size` はそれぞれいくつですか?
|
||
3. この時点で、キュー先頭から末尾までの論理的な順序はどうなっていますか?デキューするときに、配列内のほかの要素を移動する必要はありますか?その理由も説明してください。
|
||
|
||
??? success "解答"
|
||
|
||
1. 新しい要素の位置は
|
||
`(front + size) % 5 = (3 + 2) % 5 = 0` なので、
|
||
`C` はインデックス 0 に置きます。エンキュー後は `size = 3` です。
|
||
|
||
2. デキューでは、現在のキュー先頭 `A` が取り出されます。新しい先頭のインデックスは
|
||
`(3 + 1) % 5 = 4` なので、`front = 4`、`size = 2` です。
|
||
|
||
3. 有効な要素の論理的な順序は `B、C` で、`B` はインデックス 4、`C` はインデックス 0 にあります。
|
||
デキューするときは `front` を動かして `size` を変更するだけです。環状配列では剰余によってインデックスを先頭に戻せるため、
|
||
ほかの要素をまとめて前へ移動する必要はありません。
|
||
|
||
### 両端キューの両端での操作
|
||
|
||
ここでは、`push_first` はキュー先頭への追加、`push_last` はキュー末尾への追加、
|
||
`pop_first` はキュー先頭からの取り出し、`pop_last` はキュー末尾からの取り出しを表すものとします。
|
||
|
||
空の両端キュー `deq` に、次の操作を順に行います。
|
||
|
||
1. `push_last(A)`
|
||
2. `push_last(B)`
|
||
3. `push_first(C)`
|
||
4. `pop_last()`
|
||
5. `push_last(D)`
|
||
6. `pop_first()`
|
||
|
||
<!-- numbered-subquestions -->
|
||
|
||
1. 2 回の取り出しで、それぞれどの要素が得られますか?
|
||
2. すべての操作を終えた後、キュー先頭から末尾までにどの要素が残っていますか?
|
||
3. この 6 つの操作を確認してください。キュー末尾への追加とキュー先頭からの削除だけが許される通常のキューで、すべてを実行できますか?できない場合は実行できない操作を挙げ、両端キューなら実行できるか、その理由とともに説明してください。
|
||
|
||
??? success "解答"
|
||
|
||
最初の 3 つの操作を終えた時点で、両端キューは先頭から末尾へ `[C, A, B]` となっています。
|
||
|
||
<!-- numbered-subquestions -->
|
||
|
||
1. `pop_last()` は `B` を取り出します。`D` を追加するとキューは `[C, A, D]` となり、
|
||
続く `pop_first()` は `C` を取り出します。
|
||
|
||
2. 最後に `[A, D]` が残ります。
|
||
|
||
3. キュー末尾への追加とキュー先頭からの削除だけが許される通常のキューでは、すべての操作を実行できません。
|
||
手順 3 の `push_first(C)` はキュー先頭への追加を、手順 4 の `pop_last()` はキュー末尾からの削除を求めており、どちらも通常のキューで許された操作ではありません。
|
||
両端キューは両端で追加と削除ができるため、この 6 つの操作をすべて実行できます。
|
||
|
||
## プログラミング演習
|
||
|
||
### 括弧列の検査
|
||
|
||
`()`、`[]`、`{}` の 3 種類の括弧だけを含む文字列 `s` が与えられます。スタックを使って、それが正しい括弧列かどうかを判定してください。
|
||
|
||
正しい括弧列は、次の条件を両方とも満たします。各閉じ括弧は、まだ対応付けられていない開き括弧のうち最も直近のものと種類が一致し、
|
||
文字列を最後まで調べたときに、対応する閉じ括弧のない開き括弧が残っていません。判定結果をブール値で返してください。
|
||
|
||
??? tip "解法のヒント"
|
||
|
||
1. 「閉じ括弧から対応する開き括弧」への対応表を作れます
|
||
2. 閉じ括弧に出会ったら、まずスタックが空かどうかを確認し、次にスタックトップと対応しているかを確認します
|
||
3. 文字列を最後まで調べた後、スタックも空でなければなりません
|
||
|
||
[LeetCode](https://leetcode.com/problems/valid-parentheses/){ .rounded-button .exercise-button target="_blank" rel="noopener noreferrer" }
|