Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions 776.SplitBST/loop_and_recursion.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* 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:
vector<TreeNode*> splitBST(TreeNode* root, int target) {
TreeNode* left;
TreeNode* right;

splitBSTHelper(root, target, left, right);
return {left, right};
}

private:
void splitBSTHelper(TreeNode* node, int target, TreeNode*& left, TreeNode*& right) {
if (!node) {
return;
}

if (node->val > target) {
right = node;
node = node->left;
left = nullptr;
splitBSTHelper(node, target, left, right->left);
} else {
left = node;
node = node->right;
right = nullptr;
splitBSTHelper(node, target, left->right, right);
}
}
};
54 changes: 54 additions & 0 deletions 776.SplitBST/memo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
## ステップ1
まず思いついたのはrootとは別に2つ新たにnodeを用意して、
targetより小さいものと大きいものをそれぞれのツリーを作りそれらをvectorに入れて返却する。

一旦プログラムを書いて上手く動かなかったので、答えを確認し1.5hほどでacceptできた。
理解出来ていなかった箇所はループの小さい側、大きい側のそれぞれに2回目以降に紐付ける場合の動き。
root = [4,2,6,1,3,5,7], target = 2の場合
val = 4 smaller_node [] larger_node [4,6,5,7]
val = 2 smaller_node [2, 1] larger_node [4,6,5,7]
val = 3 smaller_node [2, 1] larger_node [4,3,6,5,7] // 探索終了
ここで3をどのように大きい側に繋ぐのかは答えを確認した。

3つのnodeを使っているんのでそれぞれの値をどう更新するのか理解するのに時間がかかった。

時間計算量
O(n)
左右に偏っている場合

空間計算量
O(1)

## ステップ2
・変数名の修正
・理解するためにコメントを追記
・if文を反転

再帰でも実装recusive.cpp
構造体を使いたかったが、返却値に合わせて型変換を行う必要があるので今回はそのまま

## ステップ3
**3回書き直しやりましょう、といっているのは、不自然なところや負荷の高いところは覚えられないからです。**

## 他の方の解法
gotoさんは再帰の解法を用いている。
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

再帰とループの中間を念頭において、対応関係から相互に変換できるようにしておくといいでしょう。
一例として、中間に来るものは、このような感じです。

void splitBSTHelper(TreeNode* node, int target, TreeNode** left, TreeNode** right) {
  if (!node) {
    // *left = nullptr;
    // *right = nullptr;
    return;
  }
  if (node->val > target) {
    *left = node;
    node = node->left;
    (*left)->left = nullptr;
    splitBSTHelper(node, target, &(*left)->left, right);
  } else {
    *right = node;
    node = node->right;
    (*right)->right = nullptr;
    splitBSTHelper(node, target, left, &(*right)->right);
  }
}

vector<TreeNode*> splitBST(TreeNode* node, int target) {
  TreeNode* left;
  TreeNode* right;
  splitBSTHelper(node, target, &left, &right);
  return {left, right};
}

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@oda

レビューありがとうございます。またコードもありがとうございます。
こちらを元に自分でも中間のバージョンで実装してみました。

対応関係から相互に変換できるようにしておくといいでしょう。

変換する作業の中でより処理が理解出来たような気がします。

一点質問ですが、TreeNode** leftや&(*left)->leftを用いる意図などあるのでしょうか?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

選択肢は色々です。
Google のスタイルが書き込み先はポインターというものだったのでそれに従っています。

あれ、このコード左右逆になっていますか。

Copy link
Owner Author

@Ryotaro25 Ryotaro25 Jan 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@oda
この辺りですかね。
https://google.github.io/styleguide/cppguide.html#Inputs_and_Outputs

Non-optional input parameters should usually be values or const references, while non-optional output and input/output parameters should usually be references (which cannot be null).

Google guideも何度か見返すようにします🙇‍♂️

あれ、このコード左右逆になっていますか。

このまま実行したところ左右が逆となっておりました。

node->val > targetがtrueの場合、*left = node;としておりますが
targetが現在のnodeより小さいので、
今いるNodeがleftに紐づくのではなくrightに紐づく必要があるのかと思いました。

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

昔はアウトプットはポインターでしたが、2020年のこのあたりで変更されていますね。
google/styleguide@7a7a2f5#diff-bcadcf8be931ffdd5d6a65c60c266039cf1f96b7f35bfb772662db811214c5a0L1710

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@oda
1708行目あたりですねありがとうございます🙇‍♂️


>手作業時にいきなりプログラムありきで解き方を考えている気がする
これは自分も本当にそうでかつ、ロジックから答えを導くのではなく答えからロジックを導こうとしている。

https://github.com/goto-untrapped/Arai60/pull/54/commits/570e2c2f1ee6ddcbdeaead4c40dbb2fb2a25d817

型名を決めるときの注意点
>型は何かをできなくするための制限なので、何をできないようにしたかと、そのメリットのバランスで決めるもの

>分割後のtreeはleft, rightよりsmaller, largerのほうが性質の違いが表現できていていいと思った
>戻り値がleft, rightだとどっちから見てleft, rightなのかわからなくなりそうと思った
小さい側(大きい側)のツリーにもleftとrightが存在するためsmaller largerが良さそう
https://github.com/Yoshiki-Iwasa/Arai60/pull/41

>2 個連続で入れるのは、トップレベルの関数や変数の間だけが良いと思います。
知らなかった。
https://github.com/Mike0121/LeetCode/pull/16

## Discordなど

30 changes: 30 additions & 0 deletions 776.SplitBST/recursive.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/**
* 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:
vector<TreeNode*> splitBST(TreeNode* node, int target) {
if (!node) {
return {nullptr, nullptr};
}

if (node->val <= target) {
// 左側はtargetより小さいので右側を探索する
vector<TreeNode*> right_child = splitBST(node->right, target);
node->right = right_child[0];
return {node, right_child[1]}
} else {
vector<TreeNode*> left_child = splitBST(node->left, target);
node->left = left_child[1];
return {left_child[0], node}
}
}
};
39 changes: 39 additions & 0 deletions 776.SplitBST/step1.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/**
* 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:
vector<TreeNode*> splitBST(TreeNode* root, int target) {
TreeNode* node = root;

TreeNode* smaller_head = new TreeNode();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

head はリンクリストの先頭のノードを表す単語だと思います。 smaller_root はいかがでしょうか?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nodchip
いつもレビューありがとうございます。
同じものだとごっちゃになっておりました。step4にて修正しました。

TreeNode* smaller_node = smaller_head;

TreeNode* larger_head = new TreeNode();
TreeNode* larger_node = larger_head;

while (node) {
if (node->val <= target) {
smaller_node->right = node;
smaller_node = smaller_node->right;
node = node->right;
smaller_node->right = nullptr;
} else {
larger_node->left = node;
larger_node = larger_node->left;
node = node->left;
larger_node->left = nullptr;
}
}

return {smaller_head->right, larger_head->left};
}
};
45 changes: 45 additions & 0 deletions 776.SplitBST/step2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/**
* 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:
vector<TreeNode*> splitBST(TreeNode* root, int target) {
TreeNode* node = root;

TreeNode* smaller_head = new TreeNode();
TreeNode* smaller = smaller_head;

TreeNode* bigger_head = new TreeNode();
TreeNode* larger = bigger_head;

while (node) {
if (node->val > target) {
// 現在のnodeの値の方が小さいので、
// largerの左側に現在のnodeを紐づける
larger->left = node;
larger = larger->left;
// 小さい数字が含まれているので左側に探索を進める
node = node->left;
larger->left = nullptr;
} else {
// 現在のnodeの値の方が大きいので、
// smallerの右側にnodeを右側に紐づける
smaller->right = node;
smaller = smaller->right;
// 大きい数字が含まれているので右側に探索を進める
node = node->right;
smaller->right = nullptr;
}
}

return {smaller_head->right, bigger_head->left};
}
};
38 changes: 38 additions & 0 deletions 776.SplitBST/step3.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/**
* 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:
vector<TreeNode*> splitBST(TreeNode* root, int target) {
TreeNode* node = root;
TreeNode* smaller_head = new TreeNode();
auto smaller = smaller_head;

TreeNode* larger_head = new TreeNode();
auto larger = larger_head;

while (node) {
if (node->val > target) {
larger->left = node;
larger = larger->left;
node = node->left;
larger->left = nullptr;
} else {
smaller->right = node;
smaller = smaller->right;
node = node->right;
smaller->right = nullptr;
}
}

return {smaller_head->right, larger_head->left};
}
};
38 changes: 38 additions & 0 deletions 776.SplitBST/step4.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/**
* 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:
vector<TreeNode*> splitBST(TreeNode* root, int target) {
TreeNode* node = root;
TreeNode* smaller_root = new TreeNode();
auto smaller = smaller_root;

TreeNode* larger_root = new TreeNode();
auto larger = larger_root;

while (node) {
if (node->val > target) {
larger->left = node;
larger = larger->left;
node = node->left;
larger->left = nullptr;
} else {
smaller->right = node;
smaller = smaller->right;
node = node->right;
smaller->right = nullptr;
}
}

return {smaller_root->right, larger_root->left};
}
};