-
Notifications
You must be signed in to change notification settings - Fork 0
98. Validate Binary Search Tree #29
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?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| ## 考察 | ||
| - 過去に解いたことあり | ||
| - 方針 | ||
| - 各ノードについて値の取り得る範囲を追跡すれば良い | ||
| - time: O(n), space: O(n) | ||
| - DFSでもBFSでもOK | ||
| - 再帰の深さについて | ||
| - C++で考えるのは初めてなのでしっかり見積もってみる | ||
| - スタックメモリのサイズについてはLinuxではデフォルトで8MBとして良さそう | ||
| - 手元のmacで確認したところ同様に8MB | ||
| - 各スタックフレームのサイズ | ||
| - 引数 | ||
| - TreeNode* root: 8 bytes (以下、64bitシステムとして考える) | ||
| - long long lower: 8 bytes | ||
| - long long upper: 8 bytes | ||
| - ローカル変数 | ||
| - なし | ||
| - その他 | ||
| - 戻りアドレス: 8 bytes | ||
| - ベースポインタ: 8 bytes | ||
| - 合計: 40 bytes | ||
| - 最大で 8000000 / 40 = 2 * 10^5 くらいは大丈夫そう | ||
| - 今回はノード数が最大で10^4なので、見積もり上は再帰で書いても大丈夫 | ||
| - Step1では、再帰DFSで実装する | ||
|
|
||
| ## Step1 | ||
| - 再帰DFSで実装 | ||
| - time: O(n), space: O(n) | ||
|
|
||
| ## Step2 | ||
| - 非再帰でも実装 -> `step2.cpp` | ||
| - 他の人のPRを検索 | ||
| - https://github.com/fhiyo/leetcode/pull/30 | ||
| - inorder traversal したときにノードの値が昇順に整列されていればいいとして解いている | ||
| - 再帰でyieldする解法は中々思いつかないので訓練したい | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. C++ 20 から coroutine が実装されたみたいですね。(期待の眼差し。)
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. C++のcoroutineについて調べてgenerator実装してみました。 23089ba There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. これは初めてみました。ちょっと面白いですね。 |
||
| - この発想はなかった | ||
| - n-ary treeについても見ておく | ||
| - https://github.com/fhiyo/leetcode/pull/30#discussion_r1659876811 | ||
| - https://github.com/sakupan102/arai60-practice/pull/29 | ||
| - Noneで初期化しておいて、最初のノードは比較せずに代入する方法もあります。Optional[int]です。 | ||
| - C++の書き方が参考になる | ||
| - Pythonのように関数内に関数を定義する書き方 | ||
| - https://github.com/sakupan102/arai60-practice/pull/29#discussion_r1597676869 | ||
| - 練習しておく -> `step2_function.cpp` | ||
| - https://github.com/Mike0121/LeetCode/pull/8 | ||
| - Inorderによる解法 | ||
| - 初見なら自分もこの方法をやるかもしれない | ||
|
|
||
| ## Step3 | ||
| - 1回目: 2m03s | ||
| - 2回目: 1m55s | ||
| - 3回目: 1m50s | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| /** | ||
| * Definition for a binary tree node. | ||
| * struct TreeNode { | ||
| * int val; | ||
| * TreeNode *left; | ||
| * TreeNode *right; | ||
| * TreeNode() : val(0), left(nullptr), right(nullptr) {} | ||
| * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} | ||
| * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} | ||
| * }; | ||
| */ | ||
| class Solution { | ||
| public: | ||
| bool isValidBST(TreeNode* root) { | ||
| return isValidBSTHelper(root, numeric_limits<long long>::min(), numeric_limits<long long>::max()); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 1 行 80 文字程度に収まるよう、適宜改行することをお勧めいたします。
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ありがとうございます。適宜改行するようにしてみます。 |
||
| } | ||
|
|
||
| private: | ||
| bool isValidBSTHelper(TreeNode* root, long long lower, long long upper) { | ||
| if (!root) return true; | ||
| if (!(lower < root->val && root->val < upper)) return false; | ||
| return isValidBSTHelper(root->left, lower, root->val) && isValidBSTHelper(root->right, root->val, upper); | ||
| } | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| /** | ||
| * Definition for a binary tree node. | ||
| * struct TreeNode { | ||
| * int val; | ||
| * TreeNode *left; | ||
| * TreeNode *right; | ||
| * TreeNode() : val(0), left(nullptr), right(nullptr) {} | ||
| * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} | ||
| * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} | ||
| * }; | ||
| */ | ||
| class Solution { | ||
| public: | ||
| bool isValidBST(TreeNode* root) { | ||
| stack<NodeWithRange> nodes_to_visit; | ||
| nodes_to_visit.emplace(root, numeric_limits<long long>::min(), numeric_limits<long long>::max()); | ||
| while (!nodes_to_visit.empty()) { | ||
| auto node_with_range = nodes_to_visit.top(); | ||
| nodes_to_visit.pop(); | ||
| auto node = node_with_range.node; | ||
| if (!node) continue; | ||
| auto lower = node_with_range.lower; | ||
| auto upper = node_with_range.upper; | ||
|
Comment on lines
+18
to
+23
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| if (!(lower < node->val && node->val < upper)) return false; | ||
| nodes_to_visit.emplace(node->left, lower, node->val); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. とすると、 nodes_to_visit への要素の emplace() と top() の回数が減り、少し早くなるかもしれません。ただ、あまり変わらないかもしれません。 |
||
| nodes_to_visit.emplace(node->right, node->val, upper); | ||
| } | ||
| return true; | ||
| } | ||
|
|
||
| private: | ||
| struct NodeWithRange { | ||
| TreeNode* node; | ||
| long long lower; | ||
| long long upper; | ||
| }; | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| /** | ||
| * Definition for a binary tree node. | ||
| * struct TreeNode { | ||
| * int val; | ||
| * TreeNode *left; | ||
| * TreeNode *right; | ||
| * TreeNode() : val(0), left(nullptr), right(nullptr) {} | ||
| * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} | ||
| * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), | ||
| * right(right) {} | ||
| * }; | ||
| */ | ||
| class Solution { | ||
| public: | ||
| bool isValidBST(TreeNode* root) { | ||
| function<bool(TreeNode*, long long, long long)> is_valid = [&](auto node, auto lower, auto upper) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. &は不要ではないですか?
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is_validを再帰的に参照しているため & は必要そうです。 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 確かにそうですね。ローカル変数を参照する必要がなかったら、普通の関数にした方がスッキリしそうですね。 |
||
| if (!node) return true; | ||
| if (!(lower < node->val && node->val < upper)) return false; | ||
| return is_valid(node->left, lower, node->val) && is_valid(node->right, node->val, upper); | ||
| }; | ||
| return is_valid(root, numeric_limits<long long>::min(), numeric_limits<long long>::max()); | ||
| } | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| /** | ||
| * Definition for a binary tree node. | ||
| * struct TreeNode { | ||
| * int val; | ||
| * TreeNode *left; | ||
| * TreeNode *right; | ||
| * TreeNode() : val(0), left(nullptr), right(nullptr) {} | ||
| * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} | ||
| * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} | ||
| * }; | ||
| */ | ||
| class Solution { | ||
| public: | ||
| bool isValidBST(TreeNode* root) { | ||
| return isValidBSTHelper(root, numeric_limits<long long>::min(), numeric_limits<long long>::max()); | ||
| } | ||
|
|
||
| private: | ||
| bool isValidBSTHelper(TreeNode* root, long long lower_bound, long long upper_bound) { | ||
| if (!root) return true; | ||
| if (!(lower_bound < root->val && root->val < upper_bound)) return false; | ||
| return isValidBSTHelper(root->left, lower_bound, root->val) && isValidBSTHelper(root->right, root->val, upper_bound); | ||
| } | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,145 @@ | ||
| #include <coroutine> | ||
|
|
||
| /** | ||
| * Definition for a binary tree node. | ||
| * struct TreeNode { | ||
| * int val; | ||
| * TreeNode *left; | ||
| * TreeNode *right; | ||
| * TreeNode() : val(0), left(nullptr), right(nullptr) {} | ||
| * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} | ||
| * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} | ||
| * }; | ||
| */ | ||
|
|
||
| template<std::movable T> | ||
| class Generator | ||
| { | ||
| public: | ||
| struct promise_type | ||
| { | ||
| Generator<T> get_return_object() | ||
| { | ||
| return Generator{Handle::from_promise(*this)}; | ||
| } | ||
| static std::suspend_always initial_suspend() noexcept | ||
| { | ||
| return {}; | ||
| } | ||
| static std::suspend_always final_suspend() noexcept | ||
| { | ||
| return {}; | ||
| } | ||
| std::suspend_always yield_value(T value) noexcept | ||
| { | ||
| current_value = std::move(value); | ||
| return {}; | ||
| } | ||
| // Disallow co_await in generator coroutines. | ||
| void await_transform() = delete; | ||
| [[noreturn]] | ||
| static void unhandled_exception() { throw; } | ||
|
|
||
| std::optional<T> current_value; | ||
| }; | ||
|
|
||
| using Handle = std::coroutine_handle<promise_type>; | ||
|
|
||
| explicit Generator(const Handle coroutine) : | ||
| m_coroutine{coroutine} | ||
| {} | ||
|
|
||
| Generator() = default; | ||
| ~Generator() | ||
| { | ||
| if (m_coroutine) | ||
| m_coroutine.destroy(); | ||
| } | ||
|
|
||
| Generator(const Generator&) = delete; | ||
| Generator& operator=(const Generator&) = delete; | ||
|
|
||
| Generator(Generator&& other) noexcept : | ||
| m_coroutine{other.m_coroutine} | ||
| { | ||
| other.m_coroutine = {}; | ||
| } | ||
| Generator& operator=(Generator&& other) noexcept | ||
| { | ||
| if (this != &other) | ||
| { | ||
| if (m_coroutine) | ||
| m_coroutine.destroy(); | ||
| m_coroutine = other.m_coroutine; | ||
| other.m_coroutine = {}; | ||
| } | ||
| return *this; | ||
| } | ||
|
|
||
| // Range-based for loop support. | ||
| class Iter | ||
| { | ||
| public: | ||
| void operator++() | ||
| { | ||
| m_coroutine.resume(); | ||
| } | ||
| const T& operator*() const | ||
| { | ||
| return *m_coroutine.promise().current_value; | ||
| } | ||
| bool operator==(std::default_sentinel_t) const | ||
| { | ||
| return !m_coroutine || m_coroutine.done(); | ||
| } | ||
|
|
||
| explicit Iter(const Handle coroutine) : | ||
| m_coroutine{coroutine} | ||
| {} | ||
|
|
||
| private: | ||
| Handle m_coroutine; | ||
| }; | ||
|
|
||
| Iter begin() | ||
| { | ||
| if (m_coroutine) | ||
| m_coroutine.resume(); | ||
| return Iter{m_coroutine}; | ||
| } | ||
|
|
||
| std::default_sentinel_t end() { return {}; } | ||
|
|
||
| private: | ||
| Handle m_coroutine; | ||
| }; | ||
|
|
||
| class Solution { | ||
| public: | ||
| bool isValidBST(TreeNode* root) { | ||
| auto generator = traverse_inorder(root); | ||
| long long previous_value = std::numeric_limits<long long>::min(); | ||
| for (int value : generator) { | ||
| if (value <= previous_value) { | ||
| return false; | ||
| } | ||
| previous_value = value; | ||
| } | ||
| return true; | ||
| } | ||
|
|
||
| private: | ||
| Generator<int> traverse_inorder(TreeNode* root) { | ||
| if (root->left) { | ||
| for (int value : traverse_inorder(root->left)) { | ||
| co_yield value; | ||
| } | ||
| } | ||
| co_yield root->val; | ||
| if (root->right) { | ||
| for (int value : traverse_inorder(root->right)) { | ||
| co_yield value; | ||
| } | ||
| } | ||
| } | ||
| }; |
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.
見ました。問題ないと思いました。