Skip to content

98. Validate Binary Search Tree#19

Open
n6o wants to merge 1 commit intomainfrom
validate-binary-search-tree
Open

98. Validate Binary Search Tree#19
n6o wants to merge 1 commit intomainfrom
validate-binary-search-tree

Conversation

@n6o
Copy link
Copy Markdown
Owner

@n6o n6o commented Feb 17, 2026

今回の問題

Validate Binary Search Tree - LeetCode

使用言語

Go

次に解く問題

Minimum Depth of Binary Tree - LeetCode

func isValidBST(root *TreeNode) bool {
return validate(root, NodeRange{
lower: math.MinInt,
upper: math.MaxInt,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

TreeNode.Val の制約が

-2^31 <= Node.val <= 2^31 - 1

なので、32-bit環境の math.MaxInt == 2^31 - 1だとContains(境界を含まない)を誤判定しそうだなと思いました。
NullIntなどを用いて明確に値がある状態とない状態を区別した方が環境・制約が変わった時も比較的安全かなと思います。LeetCodeは64-bit環境のようですし、考えすぎな気はしますが。

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

自分も同じ感想を持ちました。*int にして null 判定した方が安全だと思いました。

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.

お二人ともありがとうございます。

今回の場合では、まずは int64 として明示することを検討すると考えました。
公開パッケージなどでは *int で定義するのが安全ですね。
勉強になりました。

- cpp
- シンプルなデータ構造 => 処理速度が速くメモリ使用量が少なくなる可能性がある
- TraverseInorder が何をしているのかわからなかった
- inorder でノードの値を格納していき、並び順をチェックするみたい
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

binary search tree を in-order traversal すると increasing sequence が得られるのは周りを見渡すと結構知られていることのような気がします。(私の肌感覚がずれているのかもしれません。)

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.

ありがとうございます。

シュッとイメージできなかったので、イメージできるようにしておきます。

return leftIsValid && rightIsValid
}

type NodeRange struct {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Q. この構造体をグローバルにしているのは何か意図がありますか?

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.

ありがとうございます。

グローバルにしているという感覚はありませんでした。パッケージ構成を意識していないためだと思います。

public にしている理由は、 nodeRange と悩んだのですが、この練習会のレビュアーの方が読みやすいのでは(型として一般的なフォーマット)という理由で public にしました。

実務では private にしてパッケージ外からは使えないようにすると思います。

Comment on lines +167 to +176
leftIsValid := validate(node.Left, NodeRange{
lower: nodeRange.lower,
upper: node.Val,
})
rightIsValid := validate(node.Right, NodeRange{
lower: node.Val,
upper: nodeRange.upper,
})

return leftIsValid && rightIsValid
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

短絡評価されず右側のサブツリーも全部見ないと return されないので下記のように一行で書いた方が無駄な走査が減ります。

Suggested change
leftIsValid := validate(node.Left, NodeRange{
lower: nodeRange.lower,
upper: node.Val,
})
rightIsValid := validate(node.Right, NodeRange{
lower: node.Val,
upper: nodeRange.upper,
})
return leftIsValid && rightIsValid
return validate(node.Left, NodeRange{lower: nodeRange.lower, upper: node.Val}) && validate(node.Right, NodeRange{lower: node.Val, upper: nodeRange.upper})

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.

ありがとうございます。

ご指摘の通り、 false とわかった時点でその後の処理は不要ですね。

1行が長くなるので、書くとしたら下記のようにします。

leftIsValid := validate(node.Left, NodeRange{
    lower: nodeRange.lower,
    upper: node.Val,
})
if !leftIsValid {
    return false
}

return validate(node.Right, NodeRange{
   lower: node.Val,
   upper: nodeRange.upper,
})

func isValidBST(root *TreeNode) bool {
return validate(root, NodeRange{
lower: math.MinInt,
upper: math.MaxInt,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

自分も同じ感想を持ちました。*int にして null 判定した方が安全だと思いました。


type NodeRange struct {
lower int
upper int
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

node *TreeNode をフィールドに持たせるのもアリかなと思いました。

}

func (r *NodeRange) IsBetween(v int) bool {
return r.lower < v && v < r.upper
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

lower や upper について、それらの値を含む(min, max同様に)解釈も一定ありそうです。私の感覚でもそうで「ここは等号が成り立たなくていいのかな?」と一瞬不安になりました。lowerExclusive のように明記するのが一番間違いが無さそうです。これは ContainsIsBetween などでの関数名でも同様です。

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.

ありがとうございます。

ここも命名に悩みました。
go だったかは忘れたのですが、何らかのライブラリでわかりやすい名前で表現していたものがあった気がするので、探してみます。

Comment on lines +87 to +88
leftNodeRange := NodeRange{start: math.MinInt, end: root.Val}
rightNodeRange := NodeRange{start: root.Val, end: math.MaxInt}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

勘違いだったら申し訳ないのですが,例えば一つのノードからなり,root.Val == math.MinInt(= -2^31)やroot.Val == math.MaxInt(=2^32 - 1)の入力に対して正しくTrueを返せるのでしょうか?
contains()は等号なしなのでFalseにならないかなあと思いました.

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.

ありがとうございます。

32ビット環境だと意図した通りには動かないと思います。

go では int32int64int があり、 int は64ビット環境では int64 となります。
その前提で書いたので、 int64 と明示すべきだと考えました。

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

intが環境依存なのですね,知らなかったので理解が深まりました.ありがとうございます!

Comment on lines +87 to +91
leftNodeRange := NodeRange{start: math.MinInt, end: root.Val}
rightNodeRange := NodeRange{start: root.Val, end: math.MaxInt}

leftIsValid := isValidBSTWithNodeRange(root.Left, leftNodeRange)
rightIsValid := isValidBSTWithNodeRange(root.Right, rightNodeRange)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

好みの問題ですが,isValidBSTWithNodeRange(root, NodeRange{start: math.MinInt, end: math.MaxInt})rootをそのままチェック関数に投げる方がわかりやすく感じます.

Comment on lines +101 to +113
func (r NodeRange) leftNodeRange(rootValue int) NodeRange {
return NodeRange{
start: r.start,
end: rootValue,
}
}

func (r NodeRange) rightNodeRange(rootValue int) NodeRange {
return NodeRange{
start: rootValue,
end: r.end,
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

この辺りはメソッド化しなくても良いように感じました.実際,Step2のコードの方が読みやすく感じました.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants