Skip to content
Open
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
158 changes: 158 additions & 0 deletions 150. Evaluate Reverse Polish Notation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
## step1 とりあえず解く
- 計算量
- 時間:O(N)
- 空間:O(N)
- 素直にArrayDequeをStackとして使用して求めた
- 見直すと `calculateFromOperator` というメソッド名は若干冗長に感じる
- 計算に演算子と非演算子を渡すことは自然であるため `calculate` でよかったかも
- `calculateFromOperator` の分岐はswitch式のが簡潔にかけたと思う

```java
class Solution {

public int evalRPN(String[] tokens) {
ArrayDeque<Integer> numStack = new ArrayDeque<>();
Set<String> operators = new HashSet<>(Arrays.asList("*", "-", "+", "/"));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Set.of() のほうがシンプルに書けると思います。
https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/util/Set.html

Set<String> operators = Set.of("*", "-", "+", "/");

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

ご確認ありがとうございます!
勉強になりますmm

for (String token : tokens) {
if (operators.contains(token)) {
int rightNum = numStack.pollLast();
int leftNum = numStack.pollLast();
numStack.add(this.calculateFromOperator(leftNum, rightNum, token));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

メソッド呼び出しの this. は省略してよいと思います。

} else {
numStack.add(Integer.parseInt(token));
}
}
return numStack.peekLast();
}

private int calculateFromOperator(int leftNum, int rightNum, String operator) {
if (operator.equals("+")) {
return leftNum + rightNum;
}
if (operator.equals("-")) {
return leftNum - rightNum;
}
if (operator.equals("*")) {
return leftNum * rightNum;
}
if (operator.equals("/")) {
return leftNum / rightNum;
}
throw new RuntimeException();
}
}

```

## step2 他の人の回答を見る
- 計算量
- 時間:O(N)
- 空間:O(N)
- やはりStackを使った回答が多かった
- 計算するメソッドは別で切り出したほうが、重複の削除や責任の分離という面でも良さそう

```java
class Solution {
public int evalRPN(String[] tokens) {
Stack<Integer> stack = new Stack<>();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

ArrayDeque のほうが高速なため、 ArrayDeque を優先的に使用したほうが良いと思います。

https://docs.oracle.com/javase/jp/8/docs/api/java/util/ArrayDeque.html

このクラスは通常、スタックとして使われるときはStackよりも高速で、キューとして使われるときはLinkedListよりも高速です。

for (String c : tokens) {
if (c.equals("+")) {
stack.push(stack.pop() + stack.pop());

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

個人的には step1 のように 4 種類の演算を同じようなロジックで書いたほうが、読み手にとってパターンマッチングで読めるので読みやすく、かつ書き間違えにくいと思います。

} else if (c.equals("-")) {
int a = stack.pop();
int b = stack.pop();
stack.push(b - a);
} else if (c.equals("*")) {
stack.push(stack.pop() * stack.pop());
} else if (c.equals("/")) {
int a = stack.pop();
int b = stack.pop();
stack.push(b / a);
} else {
stack.push(Integer.parseInt(c));
}
}
return stack.pop();
}
}
```

- 再帰的に解く方法もあった
- 計算量
- 時間:O(N)
- 空間:O(N)

```java
public class Solution {
public int evalRPN(String[] tokens) {
List<String> tokenList = new ArrayList<>(Arrays.asList(tokens));
return dfs(tokenList);
}

private int dfs(List<String> tokens) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

関数名には、内部の実装の詳細を表す言葉より、入力に対してどのような処理を行うのか、どのような値を返すかが分かるものを付けたほうが、読み手にとって分かりやすいと思います。
parse() はいかがでしょうか?これなら、入力データに対して構文解析を行い、その結果を返すことが読み手に伝わりやすいと思います。

String token = tokens.remove(tokens.size() - 1);

if (!"+-*/".contains(token)) {
return Integer.parseInt(token);
}

int right = dfs(tokens);
int left = dfs(tokens);

switch (token) {
case "+":
return left + right;
case "-":
return left - right;
case "*":
return left * right;
case "/":
return left / right;
}

return 0;
}
}
```

## step3 3回とく
- 異常系の処理と、stackから値を取り出す前のチェックを追加

```java
class Solution {
public int evalRPN(String[] tokens) {
if(tokens.length == 0) {
return 0;
}

ArrayDeque<Integer> stack = new ArrayDeque<>();
Set<String> operators = new HashSet<>(Arrays.asList("-", "+", "*", "/"));

for(String token : tokens) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

for 文の後にスペースを空けることをおすすめします。

参考までにスタイルガイドへのリンクを貼ります。

https://google.github.io/styleguide/javaguide.html#s4.6.2-horizontal-whitespace

Separating any keyword, such as if, for or catch, from an open parenthesis (() that follows it on that line

上記のスタイルガイドは唯一絶対のルールではなく、複数あるスタイルガイドの一つに過ぎないということを念頭に置くことをお勧めします。また、所属するチームにより何が良いとされているかは変わります。自分の中で良い書き方の基準を持ちつつ、チームの平均的な書き方で書くことをお勧めいたします。

if(operators.contains(token) && stack.size() >= 2) {
int rightNum = stack.pollLast();
int leftNum = stack.pollLast();
stack.add(this.calculate(leftNum, rightNum, token));
} else {
stack.add(Integer.parseInt(token));
}
}
return stack.peekLast();
}

private int calculate(int leftNum, int rightNum, String operator) {
switch(operator) {
case "+":
return leftNum + rightNum;
case "-":
return leftNum - rightNum;
case "*":
return leftNum * rightNum;
case "/":
return leftNum / rightNum;
}
throw new RuntimeException();
}
}

```