From 93bc4738b73bf75109e33a5fd4cee6ccc7dfb857 Mon Sep 17 00:00:00 2001 From: kazukiii Date: Mon, 15 Jul 2024 02:31:28 -0700 Subject: [PATCH 1/3] =?UTF-8?q?step1,=20step2,=20step3=E3=82=92=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- arai60/validate-binary-search-tree/README.md | 52 +++++++++++++++++++ arai60/validate-binary-search-tree/step1.cpp | 24 +++++++++ arai60/validate-binary-search-tree/step2.cpp | 37 +++++++++++++ .../step2_function.cpp | 23 ++++++++ arai60/validate-binary-search-tree/step3.cpp | 24 +++++++++ 5 files changed, 160 insertions(+) create mode 100644 arai60/validate-binary-search-tree/README.md create mode 100644 arai60/validate-binary-search-tree/step1.cpp create mode 100644 arai60/validate-binary-search-tree/step2.cpp create mode 100644 arai60/validate-binary-search-tree/step2_function.cpp create mode 100644 arai60/validate-binary-search-tree/step3.cpp diff --git a/arai60/validate-binary-search-tree/README.md b/arai60/validate-binary-search-tree/README.md new file mode 100644 index 0000000..1bc1253 --- /dev/null +++ b/arai60/validate-binary-search-tree/README.md @@ -0,0 +1,52 @@ +## 考察 +- 過去に解いたことあり +- 方針 + - 各ノードについて値の取り得る範囲を追跡すれば良い + - time: O(n), space: O(n) + - DFSでもBFSでもOK +- 再帰の深さについて + - C++で考えるのは初めてなのでしっかり見積もってみる + - スタックメモリのサイズについてはLinuxではデフォルトで8MBとして良さそう + - 手元のmacで確認したところ同様に8MB + - 各スタックフレームのサイズ + - 引数 + - TreeNode* root: 4 + 8 + 8 = 20 byte (以下、64bitシステムとして考える) + - long long lower: 8 byte + - long long upper: 8 byte + - ローカル変数 + - なし + - その他 + - 戻りアドレス: 8 byte + - ベースポインタ: 8 byte + - 合計: 52 byte + - 最大で 8000000 / 52 = 153846 くらいは大丈夫そう + - 今回はノード数が最大で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する解法は中々思いつかないので訓練したい + - この発想はなかった + - 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 diff --git a/arai60/validate-binary-search-tree/step1.cpp b/arai60/validate-binary-search-tree/step1.cpp new file mode 100644 index 0000000..fc44ab9 --- /dev/null +++ b/arai60/validate-binary-search-tree/step1.cpp @@ -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::min(), numeric_limits::max()); + } + +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); + } +}; diff --git a/arai60/validate-binary-search-tree/step2.cpp b/arai60/validate-binary-search-tree/step2.cpp new file mode 100644 index 0000000..a1c88d1 --- /dev/null +++ b/arai60/validate-binary-search-tree/step2.cpp @@ -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 nodes_to_visit; + nodes_to_visit.emplace(root, numeric_limits::min(), numeric_limits::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; + if (!(lower < node->val && node->val < upper)) return false; + nodes_to_visit.emplace(node->left, lower, node->val); + nodes_to_visit.emplace(node->right, node->val, upper); + } + return true; + } + +private: + struct NodeWithRange { + TreeNode* node; + long long lower; + long long upper; + }; +}; diff --git a/arai60/validate-binary-search-tree/step2_function.cpp b/arai60/validate-binary-search-tree/step2_function.cpp new file mode 100644 index 0000000..d8bc988 --- /dev/null +++ b/arai60/validate-binary-search-tree/step2_function.cpp @@ -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 is_valid = [&](auto node, auto lower, auto upper) { + 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::min(), numeric_limits::max()); + } +}; diff --git a/arai60/validate-binary-search-tree/step3.cpp b/arai60/validate-binary-search-tree/step3.cpp new file mode 100644 index 0000000..6a846b2 --- /dev/null +++ b/arai60/validate-binary-search-tree/step3.cpp @@ -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::min(), numeric_limits::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); + } +}; From 6c3645679a9138d5b495d37a469207313f737b34 Mon Sep 17 00:00:00 2001 From: kazukiii Date: Tue, 16 Jul 2024 01:24:44 -0700 Subject: [PATCH 2/3] =?UTF-8?q?fix:=20=E8=A6=8B=E7=A9=8D=E3=82=82=E3=82=8A?= =?UTF-8?q?=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- arai60/validate-binary-search-tree/README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/arai60/validate-binary-search-tree/README.md b/arai60/validate-binary-search-tree/README.md index 1bc1253..c540bb2 100644 --- a/arai60/validate-binary-search-tree/README.md +++ b/arai60/validate-binary-search-tree/README.md @@ -10,16 +10,16 @@ - 手元のmacで確認したところ同様に8MB - 各スタックフレームのサイズ - 引数 - - TreeNode* root: 4 + 8 + 8 = 20 byte (以下、64bitシステムとして考える) - - long long lower: 8 byte - - long long upper: 8 byte + - TreeNode* root: 8 bytes (以下、64bitシステムとして考える) + - long long lower: 8 bytes + - long long upper: 8 bytes - ローカル変数 - なし - その他 - - 戻りアドレス: 8 byte - - ベースポインタ: 8 byte - - 合計: 52 byte - - 最大で 8000000 / 52 = 153846 くらいは大丈夫そう + - 戻りアドレス: 8 bytes + - ベースポインタ: 8 bytes + - 合計: 40 bytes + - 最大で 8000000 / 40 = 2 * 10^5 くらいは大丈夫そう - 今回はノード数が最大で10^4なので、見積もり上は再帰で書いても大丈夫 - Step1では、再帰DFSで実装する From 23089ba134f9ac3535c178feda7105b45d9aeabd Mon Sep 17 00:00:00 2001 From: kazukiii Date: Thu, 18 Jul 2024 01:41:13 -0700 Subject: [PATCH 3/3] =?UTF-8?q?step4=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../step4_generator.cpp | 145 ++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 arai60/validate-binary-search-tree/step4_generator.cpp diff --git a/arai60/validate-binary-search-tree/step4_generator.cpp b/arai60/validate-binary-search-tree/step4_generator.cpp new file mode 100644 index 0000000..b9a410c --- /dev/null +++ b/arai60/validate-binary-search-tree/step4_generator.cpp @@ -0,0 +1,145 @@ +#include + +/** + * 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 +class Generator +{ +public: + struct promise_type + { + Generator 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 current_value; + }; + + using Handle = std::coroutine_handle; + + 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::min(); + for (int value : generator) { + if (value <= previous_value) { + return false; + } + previous_value = value; + } + return true; + } + +private: + Generator 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; + } + } + } +};