Skip to content

98. Validate Binary Search Tree #42

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
wants to merge 1 commit into
base: main
Choose a base branch
from
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
52 changes: 52 additions & 0 deletions 98/step1.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
Solve Time: 18:52

Time: O(N^2)
Space: O(N)

最初、部分木の最大値/最小値の算出とBST判定を同時にやろうとしてハマりかけたのでそれぞれの判定を関数に切り出してシンプルにした
同時にいろんなことをしようとすると、書くのも読むのも大変なコードになりそう、というかそういうコードを考えること自体が負荷だった
Copy link

Choose a reason for hiding this comment

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


*/
class Solution {
public:
bool isValidBST(TreeNode* root) {
if (!root) {
return true;
}
if (!isValidBST(root->left) || !isValidBST(root->right)) {
return false;
}
if (root->left && max_val_in_tree(root->left) >= root->val) {
return false;
}
if (root->right && min_val_in_tree(root->right) <= root->val) {
return false;
}
return true;
}
private:
int max_val_in_tree(TreeNode* root) {
Copy link

Choose a reason for hiding this comment

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

自分は関数名は UpperCamel で書くことが多いです。

https://google.github.io/styleguide/cppguide.html#Function_Names

Regular functions have mixed case; accessors and mutators may be named like variables.
Ordinarily, functions should start with a capital letter and have a capital letter for each new word.

int max_val = root->val;
if (root->left) {
max_val = max(max_val, max_val_in_tree(root->left));
}
if (root->right) {
max_val = max(max_val, max_val_in_tree(root->right));
}
return max_val;
}

int min_val_in_tree(TreeNode* root) {
int min_val = root->val;
if (root->left) {
min_val = min(min_val, min_val_in_tree(root->left));
}
if (root->right) {
min_val = min(min_val, min_val_in_tree(root->right));
}
return min_val;

}
};

29 changes: 29 additions & 0 deletions 98/step2_1.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
Time: O(N)
Space: O(N)

下限と上限を持って再帰を構築する。
step1でこういった解法に思い至れなかったのは、関数を分けるという発想が出来ず
同時に複数のことを一つの関数でやろうとして混乱していた
思考中に負荷を感じたら何らかを分割するみたいな考え方が必要そう
*/
class Solution {
public:
bool isValidBST(TreeNode* root) {
if (!root) {
return true;
}
return is_valid_bst_recursive(root, numeric_limits<int64_t>::min(), numeric_limits<int64_t>::max());
}

private:
bool is_valid_bst_recursive(TreeNode* node, int64_t left_val, int64_t right_val) {
if (!node) {
return true;
}
if (!(left_val < node->val && node->val < right_val)) {
return false;
}
return is_valid_bst_recursive(node->left, left_val, node->val) && is_valid_bst_recursive(node->right, node->val, right_val);
Copy link

Choose a reason for hiding this comment

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

1 行がやや長いように感じました。定義改行を入れるとよいと思います。

}
};
38 changes: 38 additions & 0 deletions 98/step2_2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
Time: O(N)
Space: O(N)

in-order走査でvalがソートされるBSTの性質を利用した解法

*/
class Solution {
public:
bool isValidBST(TreeNode* root) {
if (!root) {
return true;
}
vector<int> sorted_vals;
stack<TreeNode*> in_order_operating_nodes;
in_order_operating_nodes.push(root);
while (in_order_operating_nodes.top()->left) {
in_order_operating_nodes.push(in_order_operating_nodes.top()->left);
}
while (!in_order_operating_nodes.empty()) {
auto node = in_order_operating_nodes.top();
in_order_operating_nodes.pop();
sorted_vals.push_back(node->val);
if (node->right) {
in_order_operating_nodes.push(node->right);
while (in_order_operating_nodes.top()->left) {
in_order_operating_nodes.push(in_order_operating_nodes.top()->left);
}
}
}
for (int i = 0; i < sorted_vals.size() - 1; ++i) {
if (sorted_vals[i] >= sorted_vals[i+1]) {
return false;
Copy link

Choose a reason for hiding this comment

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

この判定、push_back のところでできませんか。

Copy link

Choose a reason for hiding this comment

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

下でしてましたね。

}
}
return true;
}
};
38 changes: 38 additions & 0 deletions 98/step2_3.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
Time: O(N)
Space: O(N)

in-order走査のメモリ改良版

Choose a reason for hiding this comment

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

inorderの解法は再帰関数で書いてましたが、スタックでも解けるんですね。勉強になりました。


*/
class Solution {
public:
bool isValidBST(TreeNode* root) {
if (!root) {
return true;
}
stack<TreeNode*> in_order_operating_nodes;
in_order_operating_nodes.push(root);
TreeNode* checking_node;
int64_t under_limit = numeric_limits<int64_t>::min();
while (in_order_operating_nodes.top()->left) {
in_order_operating_nodes.push(in_order_operating_nodes.top()->left);
}
while (!in_order_operating_nodes.empty()) {
auto node = in_order_operating_nodes.top();
in_order_operating_nodes.pop();
if (under_limit >= node->val) {
return false;
}
under_limit = node->val;
if (node->right) {
in_order_operating_nodes.push(node->right);
while (in_order_operating_nodes.top()->left) {
in_order_operating_nodes.push(in_order_operating_nodes.top()->left);
}
}
}
return true;
}
};

18 changes: 18 additions & 0 deletions 98/step3.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
class Solution {
public:
bool isValidBST(TreeNode* root) {
return is_valid_bst_recursive(root, numeric_limits<int64_t>::min(), numeric_limits<int64_t>::max());
Copy link

Choose a reason for hiding this comment

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

-2^31 <= Node.val <= 2^31 - 1 の制約を受けて int64_t型を使っているかと思うのですが、もう一つのmin/max対策として、*intのようなポインタ型を使ってnull判定をする方法もあります。

Copy link
Owner Author

Choose a reason for hiding this comment

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

ポインタ型を使ってnull判定をする方法もあります。

コメントありがとうございます。
それって具体的にどのようなコードになるのでしょうか?

Copy link

Choose a reason for hiding this comment

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

Go言語のコードですが、こんな感じです。

func isValidBST(root *TreeNode) bool {
	return isValidBSTRecursively(root, nil, nil)
}

func isValidBSTRecursively(node *TreeNode, lowerBound, upperBound *int) bool {
	if node == nil {
		return true
	}
	if lowerBound != nil && node.Val <= *lowerBound {
		return false
	}
	if upperBound != nil && node.Val >= *upperBound {
		return false
	}
	return isValidBSTRecursively(node.Left, lowerBound, &node.Val) && isValidBSTRecursively(node.Right, &node.Val, upperBound)
}

Copy link
Owner Author

Choose a reason for hiding this comment

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

共有ありがとうございます。
個人的な感覚ですが、nullチェックを毎回行うのが冗長なので下限上限のみを使う方がしっくり来ると感じます。

}

private:
bool is_valid_bst_recursive(TreeNode* node, int64_t under_limit, int64_t upper_limit) {
if (!node) {
return true;
}
if (node->val <= under_limit || upper_limit <= node->val) {
return false;
}
return is_valid_bst_recursive(node->left, under_limit, node->val) &&
is_valid_bst_recursive(node->right, node->val, upper_limit);
}
};