-
Notifications
You must be signed in to change notification settings - Fork 0
Evaluate Reverse Polish Notation #86
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Yuto729
wants to merge
1
commit into
main
Choose a base branch
from
evaluate-reverse-polish-notation
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,170 @@ | ||
| # 150. Evaluate Reverse Polish Notation | ||
|
|
||
| You are given an array of strings tokens that represents an arithmetic expression in a Reverse Polish Notation. | ||
|
|
||
| Evaluate the expression. Return an integer that represents the value of the expression. | ||
|
|
||
| Note that: | ||
|
|
||
| - The valid operators are '+', '-', '\*', and '/'. | ||
| - Each operand may be an integer or another expression. | ||
| - The division between two integers always truncates toward zero. | ||
| - There will not be any division by zero. | ||
| - The input represents a valid arithmetic expression in a reverse polish notation. | ||
| - The answer and all the intermediate calculations can be represented in a 32-bit integer. | ||
|
|
||
| Example 1: | ||
| Input: tokens = ["2","1","+","3","*"] | ||
| Output: 9 | ||
| Explanation: ((2 + 1) \* 3) = 9 | ||
|
|
||
| Example 2: | ||
| Input: tokens = ["4","13","5","/","+"] | ||
| Output: 6 | ||
| Explanation: (4 + (13 / 5)) = 6 | ||
|
|
||
| Example 3: | ||
| Input: tokens = ["10","6","9","3","+","-11","*","/","*","17","+","5","+"] | ||
| Output: 22 | ||
| Explanation: ((10 _ (6 / ((9 + 3) _ -11))) + 17) + 5 | ||
| = ((10 _ (6 / (12 _ -11))) + 17) + 5 | ||
| = ((10 _ (6 / -132)) + 17) + 5 | ||
| = ((10 _ 0) + 17) + 5 | ||
| = (0 + 17) + 5 | ||
| = 17 + 5 | ||
| = 22 | ||
|
|
||
| ## Step1 | ||
|
|
||
| - 数字は負もありえる | ||
| - 0除算はない前提でよいか? | ||
| - 丸める際は、0の方向に丸める | ||
|
|
||
| オペレーターに遭遇するまで、オペランドをスタックに積む。オペレーターに遭遇したら、スタックから2つ値をpopして左のオペランドと右のオペランドとする。演算結果はスタックに書き戻す。 | ||
|
|
||
| Time: O(N) | ||
| 割り算の時に、Python組み込みの`//`を使うとうまくいかない -> なぜか? | ||
|
|
||
| ```py | ||
| class Solution: | ||
| def evalRPN(self, tokens: List[str]) -> int: | ||
| operands = [] | ||
| operators = ["+", "-", "*", "/"] | ||
|
|
||
| def calculate(left: int, right: int, operand: str): | ||
| if operand == "+": | ||
| return left + right | ||
| elif operand == "-": | ||
| return left - right | ||
| elif operand == "*": | ||
| return left * right | ||
| elif operand == "/": | ||
| return int(left / right) | ||
| else: | ||
| raise ValueError("invalid operand") | ||
|
|
||
| for token in tokens: | ||
| if token in operators: | ||
| right = operands.pop() | ||
| left = operands.pop() | ||
| operands.append(calculate(left, right, token)) | ||
| continue | ||
|
|
||
| operands.append(int(token)) | ||
|
|
||
| return operands.pop() | ||
| ``` | ||
|
|
||
| basic case | ||
|
|
||
| - tokens = ["4","13","5","/","+"] | ||
| // s = [] | ||
| in for loop | ||
| // s = [4] | ||
| // s = [4, 13] | ||
| // s = [4, 13, 5] | ||
| // token = / -> left = 13, right = 5 -> s = [4, 2] (13 // 5) | ||
| // token = + -> 6 (4 + 2) | ||
|
|
||
| edge case | ||
|
|
||
| - tokens = ["1"] | ||
| - 数字が全部負 | ||
|
|
||
| AI review | ||
|
|
||
| - `int(a / b)`の問題点 | ||
|
|
||
| ### follow up | ||
|
|
||
| Q. 入力に不正な式が入ってくる場合、どう改善する? | ||
|
|
||
| A. | ||
|
|
||
| - calculate関数は、割り算で0がrightに入ってこないかのチェックがまず必要。 | ||
| - トークンの処理で、有効なオペレータである場合、有効な数字である場合、無効なtokenの場合で分ける必要がある | ||
| - さらにpopをする際にガードをつけてたりない場合はエラーを出す。 | ||
| - 最後にループを抜けた後で、operandsに2つ以上値が入っている場合も無効になる | ||
| - 有効な数字であることをどうやって調べるかだが, tryブロック内でint関数で変換する。例えば、int("3.5")といった場合は例外が発生するので`ValueError`を返す。int("-11")などは通らないといけない | ||
|
|
||
| - `operator`モジュールを使うと以下のように分岐そのものを消すことができる | ||
| つまり、演算記号に対して関数の対応表を先に作っておく。関数はlambda関数に統一もできる | ||
|
|
||
| ```py | ||
| import operator | ||
| OPERATORS = { | ||
| "+": operator.add, # lambda a, b: a + b と同じ | ||
| "-": operator.sub, | ||
| "*": operator.mul, | ||
| "/": lambda a, b: int(a / b), | ||
| } | ||
|
|
||
| if token in OPERATORS: | ||
| right = operands.pop() | ||
| left = operands.pop() | ||
| operands.append(OPERATORS[token](left, right)) | ||
| else: | ||
| operands.append(int(token)) | ||
| ``` | ||
|
|
||
| ## Step3 | ||
|
|
||
| エラーハンドリング版 | ||
| 気をつけるところ | ||
|
|
||
| - 割り算の定義で、`//`ではなくて`int`を使う | ||
|
|
||
| ```py | ||
| class Solution: | ||
| def evalRPN(self, tokens: List[str]) -> int: | ||
| operands = [] | ||
| OPERATORS = { | ||
| "+": lambda a, b: a + b, | ||
| "-": lambda a, b: a - b, | ||
| "*": lambda a, b: a * b, | ||
| "/": lambda a, b: int(a / b) | ||
| } | ||
| for token in tokens: | ||
| if token in OPERATORS: | ||
| if len(operands) < 2: | ||
| raise ValueError(f"operator {token!r} requires 2 operands, stack has {len(operands)}") | ||
|
|
||
| right = operands.pop() | ||
| left = operands.pop() | ||
| if token == "/" and right == 0: | ||
| raise ValueError(f"division by zero in {left!r} / {right!r}") | ||
|
|
||
| operands.append(OPERATORS[token](left, right)) | ||
| continue | ||
|
|
||
| try: | ||
| value = int(token) | ||
| operands.append(value) | ||
| except ValueError: | ||
| raise ValueError(f"invalid token: {token!r}") | ||
|
|
||
| if len(operands) != 1: | ||
| raise ValueError(f"expression did not reduce to a single value: {operands!r}") | ||
|
|
||
| return operands.pop() | ||
| ``` | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pythonの//は0方向じゃなくて負の無限方向に丸めますね。自分も同じところで躓きました
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
忘れがちなところですね