-
Notifications
You must be signed in to change notification settings - Fork 0
98. validate binary seach tree #35
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
base: main
Are you sure you want to change the base?
Conversation
Given the root of a binary tree, determine if it is a valid binary search tree (BST). A valid BST is defined as follows: The left subtree of a node contains only nodes with keys less than the node's key. The right subtree of a node contains only nodes with keys greater than the node's key. Both the left and right subtrees must also be binary search trees.
fhiyo
left a comment
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.
コメントはたくさんしましたが、良いと思いました!
とても勉強にもなりました、ありがとうございます
| - 基本的にはrightに行ってinorder順にたどろうとするが、まだ左が見れてないならばそっちを先に見る | ||
| - その時左にたどるついでに、今のnodeが後で.rightで戻れるようにつなげておく | ||
| - くらいの理解(むずい) |
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.
高さ3くらいの適当な二分木を図に書いて、実際にtraverseしてみると分かるかもしれないです (自分はそれで納得しました)。要するに自分の右の子にinorder順で次のノードが常に来るように先にワープを作っておいて、ワープを実際に使った後は消して元に戻しておくんだと思います。
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.
絵を描いて実際シミュレーションしたら理解が深まりました!
ありがとうございます!
|
|
||
| class Solution: | ||
| def isValidBST(self, root: Optional[TreeNode]) -> bool: | ||
| def calculate_min_max_and_check_valid(node): |
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.
クラスかnamedtupleかdataclass辺りを作って返した方が分かりやすいかもしれません。いい名前が思いつかないですが...
(自分はTreeStatusと付けましたがうーん...という感じです)
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.
namedtuple, dataclass共、名前は聞いたことあるけどこういう時使うんだなあと思いました。
ドキュメントとか読んで調べてみます。ありがとうございます
| if right_max is not None and right_max <= node.val: | ||
| return False | ||
| return is_valid_val(node.left, left_min, node.val) \ | ||
| and is_valid_val(node.right, node.val, right_max) |
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.
上の行の続きならそれと分かるようにスペースをいくつか入れて行の先頭がずれるようにした方が良い気がします。
| if not node or next_direction_ref[0] == "parent": | ||
| nodes_stack.pop() | ||
| continue | ||
| if next_direction_ref[0] == "left": | ||
| nodes_stack.append((node.left, ["left"])) | ||
| next_direction_ref[0] = "right" | ||
| continue | ||
| if next_direction_ref[0] == "right": | ||
| inordered_val.append(node.val) | ||
| nodes_stack.append((node.right, ["left"])) | ||
| next_direction_ref[0] = "parent" |
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.
parentを見るのあんまりしっくり気ませんでした。rightの時点で抜ければ良さそうな。
while nodes_stack:
node, next_direction_ref = nodes_stack[-1]
if not node:
nodes_stack.pop()
continue
if next_direction_ref[0] == "left":
nodes_stack.append((node.left, ["left"]))
next_direction_ref[0] = "right"
continue
if next_direction_ref[0] == "right":
inordered_vals.append(node.val)
nodes_stack.pop()
nodes_stack.append((node.right, ["left"]))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.
https://discord.com/channels/1084280443945353267/1227073733844406343/1236682759649497099
これを書いた時の気持ちを思い出していたんですが、この辺の行きと帰りをstackに入れるとかを考えながらやってました。ただ、この場合は帰りになんか処理してるわけじゃないので意味なかったですね。
sort_inorder_val(node.left)
inorder_vals.append(node.val)
sort_inorder_val(node.right)
の処理順を素直にstackに書き換えるとfhiyoさんのコードになりますね。
理解深まりました。ありがとうございます
| def isValidBST(self, root: Optional[TreeNode]) -> bool: | ||
| def sort_node_val_inorder(node): | ||
| nodes_stack = [(root, ["left"])] | ||
| inordered_val = [] |
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.
inordered_valsで複数形ですかね
|
|
||
| class Solution: | ||
| def isValidBST(self, root: Optional[TreeNode]) -> bool: | ||
| def check_valid(node): |
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.
inner functionにする必要がないような?
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.
rootという変数名が動くのが微妙だなあと思ったんですが、そこまで考慮する必要はなかったですかね
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.
そこを気にするなら、そのrootという変数名をnodeに変更すれば良い気がしますね。
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.
確かにそうでした、、
|
|
||
| class Solution: | ||
| def isValidBST(self, root: Optional[TreeNode]) -> bool: | ||
| def is_valid(node): |
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.
同じく、inner functionにしなくて良さそうです
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.
inner にすることは、たとえば、その外側のローカル変数を使うとかで、せざるをえないことがありますが、そうでなければ、class method として並べちゃったほうが私は読みやすいと思いますね。入れ子にすると外側の関数が呼ばれるたびに内側が作られることになるので。
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.
ありがとうございます、この辺の選択基準が自分の中になかったので参考になりました
|
|
||
| left_children_stack = [] | ||
| append_left_children_to_stack(node) | ||
| prev_node = TreeNode(-sys.maxsize) |
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.
個人的には -sys.maxsize より小さい値は来ないのか不安になります。コメントでそういう仮定を置いている (実務ならその仮定がある程度妥当な) ことを書いておいてほしい気がします。
| ## Step2 | ||
|
|
||
| - [探索順自体をin-orderにする方法もあった](https://github.com/TORUS0818/leetcode/pull/30/files) | ||
| - あと、math.infがマジックナンバーぽいという意見もあった。すぐわかるものだと思っていた。 |
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.
一般論として、便宜上minにinfやmaxに-infを入れることはありますが、min/maxとしては実際の値ではないので、calculate_miin_maxを含む名前の関数で最小値としてinfが返ってくると、ちょっとぎょっとするかもしれないですね。個人的にはこのminやmaxの型はOptional[int]みたいな方が安心するかもしれません。
あるいは、min/maxという表現ではなくて、inf/supという表現であれば正しいと思います。(ただ、任意の同僚に対してinf/supがすぐ通じるかというと、微妙かも?)
https://en.wikipedia.org/wiki/Infimum_and_supremum
(あと、pythonだとvalの型が明示されていないですが、他の言語を選択するとvalの型はintとかIntとかなので、infはfloatみたいな型の話もあるかもしれません。)
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.
ありがとうございます。
確かにmin, maxでinfが帰ってくると変ですね
| left_children_stack = [] | ||
| append_left_children_to_stack(node) | ||
| prev_node = TreeNode(-sys.maxsize) | ||
| while True: |
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.
left_children_stackが空でない間はループするの方が分かりやすくないでしょうか?🙇
while left_children_stack:
current_node = left_children_stack.pop()
if prev_node.val >= current_node.val:
return False
if current_node.right:
append_left_children_to_stack(current_node.right)
prev_node = current_node
return True
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.
ありがとうございます。
これは正直今も迷ってます。(早期にreturn Trueする方がその条件がわかりやすいか迷いました)
|
|
||
| - 本当はFalseが来た時にすぐ返したい。(いちいちmin, maxを計算するのも大変)nonlocalの方がよかったか | ||
| - でもnonlocalはnonlocalで実装がちょっと見にくいかな | ||
| - returnする値も多い気もする |
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.
left_min, left_max, left_validがそれぞれどのように更新されるのか追う必要があり、step2に比べて追うのが辛いと感じました。step2でも3つの引数を関数に渡ししていますが、関数に渡すタイミングで引数の内2つはvalueが取りうる値のレンジだと読み取ることができたので読みやすいと感じました。個人的な意見になります🙇
|
|
||
| 2, inorderでの再帰 yield fromの利用 | ||
|
|
||
| - generatorにすればfor文で回せるので、単純なreturnより前との比較が楽 |
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.
Generator + 再帰、強力ですね。pros cons の cons としては、再帰の深さの限界があること、走りかけの Generator を木の深さ分だけ作るのでそこそこ重いことがありそうですね。
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.
Consのところいまいちわかってなかったので参考になりました。ありがとうございます。
| def is_current_node_valid(node): | ||
| return left_max < node.val < right_min |
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.
これも中で定義しない方がいいかもですね。
| - でもnonlocalはnonlocalで実装がちょっと見にくいかな | ||
| - returnする値も多い気もする | ||
| - 引数が多いのと、returnする値が多いのと、どちらの方がいいのか、選択の基準を持てていない | ||
| - calculate_min_max_and_check_validは一つの関数で2つの動作、役割があり、微妙な気もするが、しょうがない? |
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.
この点でもstep2の方が自然なのかもしれないですね。
| class Solution: | ||
| def isValidBST(self, root: Optional[TreeNode]) -> bool: | ||
| def sort_node_val_inorder(node): | ||
| nodes_stack = [(root, ["left"])] |
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.
nodes_stack = [(node, ["left"])]でしょうか?
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.
確かに関数化するならそうしないとダメですね、ありがとうございます。
https://leetcode.com/problems/validate-binary-search-tree/description/
Given the root of a binary tree, determine if it is a valid binary search tree (BST).
A valid BST is defined as follows:
The left subtree of a node contains only nodes with keys less than the node's key.
The right subtree of a node contains only nodes with keys greater than the node's key. Both the left and right subtrees must also be binary search trees.