diff --git a/Arai60/27. Minimum Depth of Binary Tree/Makefile b/Arai60/27. Minimum Depth of Binary Tree/Makefile new file mode 100644 index 0000000..94580bd --- /dev/null +++ b/Arai60/27. Minimum Depth of Binary Tree/Makefile @@ -0,0 +1,12 @@ +NAME = a.out +SRC = main.cpp +CXX = g++ +CXXFLAGS = -Wall -Wextra -Werror -std=c++11 + +all: $(NAME) + +$(NAME): $(SRC) step1.hpp + $(CXX) -o $(NAME) $(SRC) $(CXXFLAGS) + +run: $(NAME) + ./$(NAME) diff --git a/Arai60/27. Minimum Depth of Binary Tree/main.cpp b/Arai60/27. Minimum Depth of Binary Tree/main.cpp index e69de29..a231c12 100644 --- a/Arai60/27. Minimum Depth of Binary Tree/main.cpp +++ b/Arai60/27. Minimum Depth of Binary Tree/main.cpp @@ -0,0 +1,63 @@ +#include "step3.hpp" +#include +#include + +#define null 9999999 + +TreeNode *buildTree(std::initializer_list values) { + if (values.size() == 0) { + return nullptr; + } + TreeNode *root = new TreeNode(*values.begin()); + std::queue nodes; + nodes.push(root); + for (size_t i = 1; i < values.size(); ++i) { + auto node = nodes.front(); + nodes.pop(); + int val1 = *(values.begin() + i); + int val2 = *(values.begin() + ++i); + if (val1 != null) { + node->left = new TreeNode(val1); + nodes.push(node->left); + } + if (val2 != null) { + node->right = new TreeNode(val2); + nodes.push(node->right); + } + } + return root; +} + +void printTestCase(std::initializer_list values) { + std::cout << "Test case: ["; + for (auto it = values.begin(); it != values.end(); ++it) { + if (*it == null) { + std::cout << "null"; + } else { + std::cout << *it; + } + if (it != values.end() - 1) { + std::cout << ","; + } + } + std::cout << "]" << std::endl; +} + +void test(std::initializer_list values, int expected) { + TreeNode *root = buildTree(values); + printTestCase(values); + Solution solution; + int result = solution.minDepth(root); + if (result == expected) { + std::cout << "PASSED" << std::endl; + } else { + std::cout << "FAILED" << std::endl; + } +} + +int main() { + test({}, 0); + test({1}, 1); + test({3, 9, 20, null, null, 15, 7}, 2); + test({2, null, 3, null, 4, null, 5, null, 6}, 5); +} diff --git a/Arai60/27. Minimum Depth of Binary Tree/step1.hpp b/Arai60/27. Minimum Depth of Binary Tree/step1.hpp new file mode 100644 index 0000000..3d6d556 --- /dev/null +++ b/Arai60/27. Minimum Depth of Binary Tree/step1.hpp @@ -0,0 +1,59 @@ +/* + 何がわからなかったか + - N/A + 何を考えて解いていたか + 1. min depthを求めるので、BFSを使いたい + 正解してから気づいたこと + - 最後のunreachableなcodeについて、どうしたらいいか + std::unreachableはC++23から使えるらしいが、新しすぎるか + https://en.cppreference.com/w/cpp/utility/unreachable + __builtin_unreachable()を使うのが良いかもしれないが、それもどうか + - depthはqueueに入れてしまった方が見通しが良いかも +*/ +#ifndef STEP1_HPP +#define STEP1_HPP +/** + * 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) {} +}; + +#include + +class Solution { +public: + int minDepth(TreeNode *root) { + if (root == nullptr) + return 0; + std::queue visitQueue; + int depth = 1; + visitQueue.push(root); + while (!visitQueue.empty()) { + int numInThisDepth = visitQueue.size(); + for (int i = 0; i < numInThisDepth; ++i) { + auto node = visitQueue.front(); + visitQueue.pop(); + if (node->left == nullptr && node->right == nullptr) { + return depth; + } + if (node->left) { + visitQueue.push(node->left); + } + if (node->right) { + visitQueue.push(node->right); + } + } + ++depth; + } + // This should never be reached + return depth; + } +}; +#endif // STEP1_HPP diff --git a/Arai60/27. Minimum Depth of Binary Tree/step2.hpp b/Arai60/27. Minimum Depth of Binary Tree/step2.hpp new file mode 100644 index 0000000..90ba996 --- /dev/null +++ b/Arai60/27. Minimum Depth of Binary Tree/step2.hpp @@ -0,0 +1,89 @@ +/* + 講師陣はどのようなコメントを残すだろうか? + - コードの移植性について + 他の人のコードを読んで考えたこと + - https://github.com/colorbox/leetcode/pull/36 + pushの代わりにemplaceを使っているが、どう違うんだっけ + - (!ptr)と(ptr == nullptr)は + 郷に行っては郷に従えスタンスだけど、自分で書く場合はいつも迷う。 + 個人的には前者は短いけど分かりづらく、後者好きだけど長くなる。 + + 改善する時にかんがえたこと + - そもそもgcc/clang以外でコンパイルしたことがないから他のコンパイラ(主にMSVC?)のためにどれくらいポータブルなコードを書くべきなのかがよくわかっていない。 + (std::unreachableはC++23からで新しすぎるかな) + https://en.cppreference.com/w/cpp/utility/unreachable + - なるほど、新しく値をコンストラクトしてpushするとcopy/moveしてしまうので、 + コンストラクタの引数をemplaceに渡す方が効率が良いということか。 + https://en.cppreference.com/w/cpp/container/queue/emplace + https://stackoverflow.com/questions/26198350/c-stacks-push-vs-emplace + - DFSで書くときには、当初leftかrightが0の場合を考慮せずreturnしてしまった + なんか考慮漏れがあってスッと書けない感じがした + +*/ +#ifndef STEP2_HPP +#define STEP2_HPP +/** + * 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) {} +}; + +#include +#include + +class Solution { +public: + // 1. depthをqueueに + // 2. __builtin_unreachable()を使ってみた(gcc/clangのみだが) + // 3. pushの代わりにemplaceを使ってみた + int minDepth(TreeNode* root) { + if (root == nullptr) return 0; + std::queue> visitQueue; + visitQueue.emplace(root, 1); + while (!visitQueue.empty()) { + auto [node, depth] = visitQueue.front(); + visitQueue.pop(); + if (node->left == nullptr && node->right == nullptr) { return depth; } + if (node->left) { visitQueue.emplace(node->left, depth + 1); } + if (node->right) { visitQueue.emplace(node->right, depth + 1); } + } + __builtin_unreachable(); + } + + // root == nullptrを特別扱いせずにdummyを使ってみる + int minDepth2(TreeNode* root) { + TreeNode dummy(0, root, nullptr); + std::queue> visitQueue; + visitQueue.emplace(&dummy, 0); + while (!visitQueue.empty()) { + auto [node, depth] = visitQueue.front(); + visitQueue.pop(); + if (node->left == nullptr && node->right == nullptr) { return depth; } + if (node->left) { visitQueue.emplace(node->left, depth + 1); } + if (node->right) { visitQueue.emplace(node->right, depth + 1); } + } + __builtin_unreachable(); + } + + // DFSでもやってみる + // どちらにせよO(N)だが平衡に近い木ではだいぶ不利なはず + int minDepth3(TreeNode* root) { + auto dfs = [&](auto &&self, TreeNode *node) -> int { + if (node == nullptr) { return 0; } + int left = self(self, node->left); + int right = self(self, node->right); + if (left == 0) { return 1 + right; } + if (right == 0) { return 1 + left; } + return 1 + std::min(left, right); + }; + return dfs(dfs, root); + } +}; +#endif // STEP2_HPP + diff --git a/Arai60/27. Minimum Depth of Binary Tree/step3.hpp b/Arai60/27. Minimum Depth of Binary Tree/step3.hpp new file mode 100644 index 0000000..c2c5d9c --- /dev/null +++ b/Arai60/27. Minimum Depth of Binary Tree/step3.hpp @@ -0,0 +1,37 @@ +/* + 3問連続で正解するのにかかった時間 10分 + 時間計算量: O(N) + 空間計算量: O(N) +*/ + +#ifndef STEP3_HPP +#define STEP3_HPP + +#include + +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: + int minDepth(TreeNode *root) { + if (root == nullptr) return 0; + std::queue> visitQueue; + visitQueue.emplace(root, 1); + while (!visitQueue.empty()) { + auto [node, depth] = visitQueue.front(); + visitQueue.pop(); + if (node->left == nullptr && node->right == nullptr) return depth; + if (node->left) visitQueue.emplace(node->left, depth + 1); + if (node->right) visitQueue.emplace(node->right, depth + 1); + } + __builtin_unreachable(); + } +}; +#endif