Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions 232_implement_queue_using_stacks/memo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# 232. Implement Queue using Stacks

https://leetcode.com/problems/implement-queue-using-stacks/

## Comments

### step1

* 久々に取り組んだ
* 問題文から、2 つ stack を使うことが明らかだったので、その方針で考えてみた。
* 一旦移して順番を入れ替える (reverse) はまあ思いついて、ただ O(n) なんだよな、と思いつつ、O(1) がないか 3:00 くらい考えたが思いつかなかった。
* `st`, `st_aux` よりはいい名前があるだろうと思ったが、あまり思いつかなかったのでこのまま
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

auxはauxiliary(補助)という意味の略称なんですね。
これは単なる質問なのですが、auxという略称はよく見かけられるのでしょうか?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

aux はたまにみますが、私の考える典型例は下のような再帰のための補助関数ですね。

public:
  static Type Intersect(Type type1, Type type2, Zone* zone);
private:
  static int IntersectAux(Type type, Type other, UnionType* result, int size,
                          RangeType::Limits* limits, Zone* zone);

https://source.chromium.org/chromium/chromium/src/+/main:v8/src/compiler/turbofan-types.cc;l=814?q=aux%5Cb%20filepath:.*%5C.cc$

* 最初 `stack`, `stack_aux` としていたが、`std::stack` と衝突する
* 最初、`pop` の方で入れ替え処理を書こうとして、数行書いたあたりで、`pop` する処理と混ざって面倒なので、`push` で書いたほうがいいかな、と思った
* l11-14, 別になくても動くなと、後から読み直して気付いた
* 後から考えると、おそらく一般的なユースケースでは `pop` の方が頻度が低そうなので、そちらを O(n)、`push` を O(1) 二死したほうがよかったのかもしれない
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

こちらも単なる質問なのですが、こちらの「一般的なユースケースでは」というのは具体的にはどのようなユースケースを想定されておりましたか?
popの回数がpushの回数を上回らないというのは分かるのですが、私のイメージだとQueueを使うということはpush/popを同程度使うことが求められているのではないかという認識です。

* `peek`, `pop` の empty check はメソッドにしてもいい
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Google Style だと名前の付け方として、

Accessors and mutators (get and set functions) may be named like variables, in snake_case.

です。empty, peek はいいとして、push, pop は先頭大文字かなとも思いますが、許される範囲かもしれません。
https://google.github.io/styleguide/cppguide.html#Function_Names

* ただそれ以前に例外を投げていいのかちょっと迷った (https://github.com/ryosuketc/leetcode_grind75/pull/10#discussion_r2295004606)
* ただ今回だと int を返さなければならないが、"特殊な int" というのが思いつかなかった。どのような int でも queue に入る可能性があるので、特定の int をエラーとして扱うのは無理がある
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

C++ は速度のために配列外アクセスは未定義動作にしてよいという風潮がありますが、まあ、空で pop したら例外を投げるほうが行儀はいいでしょうね。

* ので結局今回は例外を選択
* 最初、Python のクセか、`st`, `st_aux` をコンストラクタ内で定義して怒られた。
* C++ だと、宣言と初期化が分離している (コンストラクタでは初期化を行う)

### step2

* https://leetcode.com/problems/implement-queue-using-stacks/editorial/
* amortized O(1) の書き方があるらしい。
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Chris Okasaki の Purely Functional Data Structures に載ってますね。BankersQueue です。

* なるほど、普段は `st` に LIFO のまま push しておく
* `pop` が呼ばれたとき、**かつ `st_aux` が空の場合のみ**に一気に `st_aux` に移す。`st_aux` に要素が残っていれば (`st_aux` は FIFO で積んでいるので) 単純に top を返して pop すればよい。
* `front` で状態管理しようとして失敗した (`MyQueue1`)
* Gemini と相談したりあれこれしてたらこの実装が良さそう (`MyQueue2`)
* `front` による状態管理はしない
* `st`, `st_aux` は役割的に `stack_in`, `stack_out` がよさそう。省略形 `st` も避けておく
* `stack_in`, `stack_out` 間の処理を `peek` に移動し、`pop` から `peek` を呼ぶようにした
* このクラスの `empty()` を利用。
* `exception`
* https://en.cppreference.com/w/cpp/error/exception.html
* C++ だと上のような standard exceptions がある。まだぜんぶは覚えてないけど
* C++ 例外も Python と同様に階層構造があるようだ。上位のものを指定すると下位をまとめて catch できる (はず)

### step3
* `step2.MyQueue2` と同じ
54 changes: 54 additions & 0 deletions 232_implement_queue_using_stacks/step1.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
class MyQueue {
private:
// stack holds items in quque fashion.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

細かくて恐縮ですが、こちらqueueのスペルミスですかね

std::stack<int> st;
std::stack<int> st_aux;
public:
MyQueue() {
}

void push(int x) {
if (st.empty()) {
st.push(x);
return;
}
Comment on lines +11 to +14
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ここは無くてもいいのかなと思いました。
(追記)memoのほうでもご自分でも気づかれてましたね、失礼しました。

while (!st.empty()) {
st_aux.push(st.top());
st.pop();
}
st_aux.push(x);
while (!st_aux.empty()) {
st.push(st_aux.top());
st_aux.pop();
}
}

int pop() {
if (st.empty()) {
throw std::out_of_range("Queue is empty");
}
int top = st.top();
st.pop();
return top;
}

int peek() {
if (st.empty()) {
throw std::out_of_range("Queue is empty");
}
return st.top();
}

bool empty() {
return st.empty();
}
};

/**
* Your MyQueue object will be instantiated and called as such:
* MyQueue* obj = new MyQueue();
* obj->push(x);
* int param_2 = obj->pop();
* int param_3 = obj->peek();
* bool param_4 = obj->empty();
*/
95 changes: 95 additions & 0 deletions 232_implement_queue_using_stacks/step2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@

// 動かないコード。
// front で状態管理しようとして失敗した。デバッグしようとして面倒になったのでやめた。
#include <stack>

class MyQueue1 {
private:
std::stack<int> st;
std::stack<int> st_aux;
int front;
public:
MyQueue() {
}

void push(int x) {
if (st.empty() && st_aux.empty()) {
front = x;
}
st.push(x);
}

int pop() {
if (st.empty() && st_aux.empty()) {
throw std::out_of_range("Queue is empty");
}
if (st_aux.empty()) {
while (!st.empty()) {
st_aux.push(st.top());
st.pop();
}
}
int popped_item = st_aux.top();
st_aux.pop();
// Update the front.
if (!st_aux.empty()) {
front = st_aux.top();
}
return popped_item;
}

int peek() {
return front;
}

bool empty() {
return st.empty() && st_aux.empty();
}
};

/**
* Your MyQueue object will be instantiated and called as such:
* MyQueue* obj = new MyQueue();
* obj->push(x);
* int param_2 = obj->pop();
* int param_3 = obj->peek();
* bool param_4 = obj->empty();
*/

#include <stack>

class MyQueue2 {
std::stack<int> stack_in;
std::stack<int> stack_out;
public:
MyQueue() {
}

void push(int x) {
stack_in.push(x);
}

int pop() {
int front = peek();
stack_out.pop();
return front;
}

int peek() {
if (empty()) {
throw std::out_of_range("Queue is empty");
}
if (stack_out.empty()) {
while (!stack_in.empty()) {
stack_out.push(stack_in.top());
stack_in.pop();
}
}
return stack_out.top();
}

bool empty() {
return stack_in.empty() && stack_out.empty();
}
};

38 changes: 38 additions & 0 deletions 232_implement_queue_using_stacks/step3.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@

#include <stack>

class MyQueue2 {
std::stack<int> stack_in;
std::stack<int> stack_out;
Comment on lines +5 to +6
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

分かりやすい名前で良いと思いました!
(自分はpush用のスタック、pop用のスタックでstack_push, stack_popとかにするかなと考えていました)

public:
MyQueue() {
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
}
MyQueue() = default

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@austyhooong こちらのほうがいいと思った意図を書いてあげるとより好ましいと思います。


void push(int x) {
stack_in.push(x);
}

int pop() {
int front = peek();
stack_out.pop();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

peekのほうで例外が飛んでくる可能性があるのでそれをキャッチできるとよいのかなと思いました

return front;
}

int peek() {
if (empty()) {
throw std::out_of_range("Queue is empty");
}
if (stack_out.empty()) {
while (!stack_in.empty()) {
stack_out.push(stack_in.top());
stack_in.pop();
}
}
return stack_out.top();
}

bool empty() {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
bool empty() {
bool empty() const noexcept {

return stack_in.empty() && stack_out.empty();
}
};