-
Notifications
You must be signed in to change notification settings - Fork 0
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
108. Convert Sorted Array to Binary Search Tree #38
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,30 @@ | ||
/* | ||
Solve Time : 23:54 | ||
|
||
Time : O(N log N) | ||
Space : (N) | ||
|
||
方針はほぼ最初にできていたが、右側の再帰部分でミスってハマった。 | ||
右半分をreverseして再帰しないといけないという勘違いをしていた。 | ||
*/ | ||
class Solution { | ||
public: | ||
TreeNode* sortedArrayToBST(vector<int>& nums) { | ||
if (nums.size() == 0) { | ||
return nullptr; | ||
} | ||
if (nums.size() == 1) { | ||
return new TreeNode(nums[0]); | ||
} | ||
int half_size = nums.size() / 2; | ||
vector<int> left_nums; | ||
for (int i = 0; i < half_size; ++i) { | ||
left_nums.push_back(nums[i]); | ||
} | ||
vector<int> right_nums; | ||
for (int i = half_size + 1; i < nums.size(); ++i) { | ||
right_nums.push_back(nums[i]); | ||
} | ||
return new TreeNode(nums[half_size], sortedArrayToBST(left_nums), sortedArrayToBST(right_nums)); | ||
} | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
/* | ||
Time : O(N log N) | ||
Space : O(N) | ||
|
||
nums.size==1の無駄な処理減らし、vectorのコピーをループを使わずに実行するようにした。 | ||
*/ | ||
class Solution { | ||
public: | ||
TreeNode* sortedArrayToBST(vector<int>& nums) { | ||
if (nums.empty()) { | ||
return nullptr; | ||
} | ||
int center_index = nums.size() / 2; | ||
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. 変数名はmiddleが一般的かと思いました🙇♂️
|
||
auto node = new TreeNode(nums[center_index]); | ||
auto left_nodes = vector<int>(nums.begin(), nums.begin() + center_index); | ||
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. vectorをコピー生成を行っているので、indexで管理するのもありかと思いました。 |
||
node->left = sortedArrayToBST(left_nodes); | ||
auto right_nodes = vector<int>(nums.begin() + center_index + 1, nums.end()); | ||
node->right = sortedArrayToBST(right_nodes); | ||
return node; | ||
} | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
/* | ||
Time : O(N) | ||
Space : O(N) | ||
インデックスを使用してvectorのコピーをなくした。 | ||
*/ | ||
class Solution { | ||
public: | ||
TreeNode* sortedArrayToBST(vector<int>& nums) { | ||
return sorted_array_to_bst_recursive(nums, 0, nums.size()); | ||
} | ||
|
||
private: | ||
TreeNode* sorted_array_to_bst_recursive(vector<int>& nums, int left, int right) { | ||
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. nums は内部で変更されないため、 const を付けたほうがよいと思います。個人的には、変更しない変数について、プリミティブ型は const をつけず、複合型は const を付けています。ただ、あくまで個人的な目安のため、所属するチームの平均的な書き方に合わせることをお勧めいたします。 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. コメントありがとうございます、このnumsに対しては変更を加えないのでconstをつけるほうがよいですね。 |
||
if (left >= right) { | ||
return nullptr; | ||
} | ||
int mid = (left + right) / 2; | ||
auto node = new TreeNode(nums[mid]); | ||
node->left = sorted_array_to_bst_recursive(nums, left, mid); | ||
node->right = sorted_array_to_bst_recursive(nums, mid + 1, right); | ||
return node; | ||
} | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
/* | ||
Time : O(N) | ||
Space : O(N) | ||
|
||
再帰を使わずに実装。 | ||
https://github.com/colorbox/leetcode/pull/37 | ||
を説いたときにある程度理解したつもりだったが、ポインタのポインタを使おうとするとだいぶ混乱する。 | ||
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. 最後のポインターは「を保存する先」という意味になりますね。 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. 特に問題ないかと思います。 |
||
|
||
*/ | ||
class Solution { | ||
public: | ||
TreeNode* sortedArrayToBST(vector<int>& nums) { | ||
stack<tuple<TreeNode**, int, int>> parents_and_ranges; | ||
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. tuppleの代わりにstructを使うのも一つの方法だと思います。
参照 |
||
TreeNode *node; | ||
parents_and_ranges.emplace(&node, 0, nums.size()); | ||
while (!parents_and_ranges.empty()){ | ||
auto [parent_node_pointer, left, right] = parents_and_ranges.top(); | ||
parents_and_ranges.pop(); | ||
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. この2行を1行でできないかなと思ったのですが、C++のstack.pop()は何も返さないんですね 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. stack.pop() が値を返した場合、参照返しにできず、値返しにせざるを得ないものの、パフォーマンス的に微妙だからできないようにしたのかなと想像しました。実際に理由は分かりません。委員会の文書に載っているのでしょうか…。 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 (left >= right) { | ||
continue; | ||
} | ||
int mid = (left + right) / 2; | ||
TreeNode* parent_node = new TreeNode(nums[mid]); | ||
*parent_node_pointer = parent_node; | ||
parents_and_ranges.emplace(&parent_node->left, left, mid); | ||
parents_and_ranges.emplace(&parent_node->right, mid + 1, right); | ||
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. pushでもいいのかなと思ったのですが、emplaceとpushの違いがわからなくて調べました。pushだと引数をstackのtopに追加するという意味で、emplaceだとstackのtopでオブジェクトを構築して置くという意味で合ってますか? 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. emplaceを用いている理由はtupleのコピーを発生させないためではないでしょうか。
https://en.cppreference.com/w/cpp/container/stack/emplace pushを用いた場合は、stackに入れる前に&parent_node->left, left, midや&parent_node->right, mid + 1, right)のコピーが作られると思います。 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.
nit: new いりません。 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. なるほどです。勉強になります! |
||
} | ||
return node; | ||
} | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
/* | ||
再帰は慣れているので、ポインタに慣れるためにポインタを使用した解法でstep3 | ||
*/ | ||
class Solution { | ||
public: | ||
TreeNode* sortedArrayToBST(vector<int>& nums) { | ||
stack<tuple<TreeNode**, int, int>> node_pointers_and_ranges; | ||
TreeNode* root; | ||
node_pointers_and_ranges.emplace(&root, 0, nums.size()); | ||
while (!node_pointers_and_ranges.empty()) { | ||
auto [node_pointer, left, right] = node_pointers_and_ranges.top(); | ||
node_pointers_and_ranges.pop(); | ||
if (left >= right) { | ||
continue; | ||
} | ||
int mid = (left + right) / 2; | ||
TreeNode* node = new TreeNode(nums[mid]); | ||
*node_pointer = node; | ||
node_pointers_and_ranges.emplace(&node->left, left, mid); | ||
node_pointers_and_ranges.emplace(&node->right, mid + 1, right); | ||
} | ||
return root; | ||
} | ||
}; |
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.
Time: O(N)
Space: O(N^2)
ではないでしょうか?
sortedArrayToBST関数はnullptrを返す場合も含めると最悪で2N回(木が一直線の場合)呼ばれると思います。
空間計算量については、numsの部分コピーが、N + (N/2 + N/2) + (N/4 + N/4 + N/4 + N/4) + ... と作成されるのでO(N^2)だと思います
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.
空間計算量は時間計算量を超えることはないですね。
空間計算量ですが、この場合、必ず真ん中で分けているので、深さは log N で済みます。
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.
空間計算量については、numsの部分コピーが、N + N/2 + N/4 + ... と作成されるのでO(N)だと思います。
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.
@oda @nodchip
ご指摘ありがとうございます。まだ少し混乱しているので整理させてください
時間計算量: O(N logN)
auto left_nodes = vector<int>(nums.begin(), nums.begin() + center_index);
でnumsの半分の長さのコピーを作成するのにO(N/2)時間かかる(ここを失念していました)。再帰の深さは平衡木であるため(ここも失念していました)、logNで済む。よって、(N/2 + N/2) + (N/4 + N/4 + N/4 + N/4) + ... = N * logNとなり、O(N logN)時間でできる空間計算量: O(N)
こちらも(N/2 + N/2) + (N/4 + N/4 + N/4 + N/4) + ... = N * logN では?と思ったが、よく考えたらこれはスタックメモリの話をしていて、スタックメモリは関数呼び出しが終了すると解放されるので、ある瞬間に積み上がる最大のスタックフレームの個数はlogN個で、その中身は大きさが N + N/2 + N/4 + ... なのでO(N)