diff --git a/arai60/validate-binary-search-tree/README.md b/arai60/validate-binary-search-tree/README.md new file mode 100644 index 0000000..c540bb2 --- /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: 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する解法は中々思いつかないので訓練したい + - この発想はなかった + - 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); + } +}; 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; + } + } + } +};