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
99 changes: 99 additions & 0 deletions 100_SameTree/memo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
## ステップ1での思考過程
まず思いついた方法は、inorder、preorder、postorderを使って探索して
その探索結果をvectorか何かに格納して等しいか判断。
使う探索は一つでいいのか、複合的に確認する必要があるのか小さい例で確認する
Trueの場合は、いずれの探索方法でも出力結果は同じになるはずなのでfalseのケースを確認。

例よりnodeが2つある場合
1. p = [1,2], q = [1,null,2] output = false
inorder
p [2, 1] | q [1, 2]
preorder
p [1, 2] | q [1, 2]
postorder
p [2, 1] | q [2, 1]
inorderを用いれば判断できる?

2. p = [1,2,1], q = [1,1,2] output = false
inorder
p [1, 2, 1] | q [1, 1, 2]
preorder
p [1, 2, 1] | q [1, 1, 2]
postorder
p [2, 1, 1] | q [1, 2, 1]
いずれの探索でも判断できる?

3. p = [1, 2, 3, 4] q = [1, 2, 3, null, null, 4] output = false
inorder
p [4, 2, 1, 3] | q [2, 1, 3, 4]
preorder
p [1, 2, 4, 3] | q [1, 2, 3, 4]
postorder
p [4, 2, 3, 1] | q [2, 4, 3, 1]
inorderだけで十分そう。

再帰関数で探索を行いながら、vectorに数字を入れていく。
実装前に見積もってみる
計算量はO(m)もしくはO(n)
mはpのノード数
nはqのノード数
mとnの大きさに依存

### step1_wa
上記の方法だと、全て同じ数字が入っている場合検出できない
pとqのnodeを同時に探索しつつvectorに入れないでその場で判定する
無駄に難しく考えたけどこの方が素直ですね。

探索途中で、
片方のnodeのみnullptr -> false
valueが一致しない -> false
全体でacceptまで35分(NGまで25分、acceptまで10分)ほど

## ステップ2
* step1では、探索部分を外だしをして関数定義をしたが
特に何かしているわけではないので外だしをやめる
したかったことといえば、pとqの変数名を書き換えたかったこと

* prefixにfirstやsecondを使って表現していたが
nodeとother_nodeとしてみた

* 再帰からqueueを使ったバージョンへの変更
何度も同じようなチェックをしている気がする
ノード数は最大でも100個なので今回は再帰の方が可読性が高いので個人的には好き

>C 言語の場合の話となりますが、関数呼び出しを行うたびにスタックフレームが作られます。スタックフレームには以下のような値が格納されます。
>・引数
>・オート変数
>・リターンアドレス
>・リターン値設定アドレス
>・レジスタ退避域
>・一時変数
https://github.com/Ryotaro25/leetcode_first60/pull/30#discussion_r1734602153

* 過去にいただいた指摘から64bitコンピュータを想定して計算してみる
・引数 それぞれ8バイト
・オート変数 今回は未使用
・リターンアドレス 8バイト
・リターン値設定アドレス 8バイト
・レジスタ退避域 16(たいていが16のよう)
・一時変数 今回は未使用
なので40バイト * 100 = 4000バイト ≒ 4KB
>リンカーで使用される既定のスタック予約サイズは 1 MB です
https://learn.microsoft.com/ja-jp/windows/win32/procthread/thread-stack-size
とのことなので問題なしか
Linuxは8MB

下記みたいな話もあるんですね。
>you can approximate that with the size of the arguments plus a constant return address size.
https://stackoverflow.com/questions/47606967/how-can-i-calculate-approximate-the-memory-used-by-a-stack-frame-of-a-recursive


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

## 他の解法
調べた感じ、再帰で探索するかstackもしくはqueueを用いて探索するかのどちらかかな


## Discorなど

31 changes: 31 additions & 0 deletions 100_SameTree/step1.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* 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 isSameTree(TreeNode* p, TreeNode* q) {
return IsIdenticalTree(p, q);
}
Comment on lines +14 to +16
Copy link

Choose a reason for hiding this comment

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

同じなので一つにしてもいいですね。

Copy link
Owner Author

Choose a reason for hiding this comment

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

レビューありがとうございます。
pとqの名前を変えたかっただけですので、step2で一つしました。

Copy link

Choose a reason for hiding this comment

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

C++ のシグネチャーの変数名は変わっても同じものとして扱われるので、変えてしまっても構わないでしょう。
Python だと、キーワード引数による呼び出しがあるのでそうはいきませんが。

Copy link
Owner Author

Choose a reason for hiding this comment

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

こういったところにも言語による差があるのですね。
この会でよくみるPythonやJavaについてはもう少し理解しようと思います。


private:
bool IsIdenticalTree(TreeNode* first_node, TreeNode* second_node) {
if (!first_node && !second_node) {
return true;
}
if (!first_node || !second_node) {
return false;
}
if (first_node->val != second_node->val) {
return false;
}
return IsIdenticalTree(first_node->left, second_node->left) && IsIdenticalTree(first_node->right, second_node->right);
}
};
41 changes: 41 additions & 0 deletions 100_SameTree/step1_wa.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/**
* 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 isSameTree(TreeNode* p, TreeNode* q) {
Copy link

Choose a reason for hiding this comment

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

p = [1, 2], q = [2, null, 1] に対して誤って true を返す気がしました。

// pとqをそれぞれinorderで探索し、vectorの値が同じか確認する
vector<int> first_tree_nums;
PushNumsByInorder(first_tree_nums, p);
vector<int> second_tree_nums;
PushNumsByInorder(second_tree_nums, q);

if (first_tree_nums.size() != second_tree_nums.size()) {
Copy link

Choose a reason for hiding this comment

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

return first_tree_nums.size() == second_tree_nums; のほうがシンプルだと思いました。

return false;
}
for (int i = 0; i < first_tree_nums.size(); i++) {
if (first_tree_nums[i] != second_tree_nums[i]) {
return false;
}
}
return true;
}

private:
void PushNumsByInorder(vector<int>& nums, TreeNode* node) {
if (!node) {
return;
}
PushNumsByInorder(nums, node->left);
nums.push_back(node->val);
PushNumsByInorder(nums, node->right);
}
};
26 changes: 26 additions & 0 deletions 100_SameTree/step2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* 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 isSameTree(TreeNode* node, TreeNode* other_node) {
if (!node && !other_node) {
return true;
}
if (!node || !other_node) {
return false;
}
if (node->val != other_node->val) {
return false;
}
return isSameTree(node->left, other_node->left) && isSameTree(node->right, other_node->right);
}
};
60 changes: 60 additions & 0 deletions 100_SameTree/step2_2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/**
* 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 isSameTree(TreeNode* p, TreeNode* q) {
queue<NodePair> node_pairs;
node_pairs.push({p, q});
while (!node_pairs.empty()) {
auto [node, other_node] = node_pairs.front();
node_pairs.pop();

if (!IsSameNode(node, other_node)) {
Copy link

Choose a reason for hiding this comment

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

IsSameNode() が同じノードに対して複数回呼ばれている点が無駄に感じました。

class Solution {
public:
  bool isSameTree(TreeNode* p, TreeNode* q) {
    queue<NodePair> node_pairs;
    node_pairs.push({p, q});
    while (!node_pairs.empty()) {
      auto [node, other_node] = node_pairs.front();
      node_pairs.pop();

      if (!IsSameNode(node, other_node)) {
        return false;
      }

      if (node && other_node) {
        node_pairs.push({node->left, other_node->left});
        node_pairs.push({node->right, other_node->right});
      }
    }
    return true;
  }

private:
  struct NodePair {
    TreeNode* node;
    TreeNode* other_node;
  };

  bool IsSameNode (TreeNode* node, TreeNode* other_node) {
    if (!node && !other_node) {
      return true;
    }
    if (!node || !other_node) {
      return false;
    }
    return node->val == other_node->val;
  }
};

return false;
}

// 初手がnullptrの場合のチェック
if (node && other_node) {
if (!IsSameNode(node->left, other_node->left)) {
return false;
}
if (node->left && other_node->left) {
node_pairs.push({node->left, other_node->left});
}

if (!IsSameNode(node->right, other_node->right)) {
return false;
}
if (node->right, other_node->right) {
node_pairs.push({node->right, other_node->right});
}
}
}
return true;
}

private:
struct NodePair {
TreeNode* node;
TreeNode* other_node;
};

bool IsSameNode (TreeNode* node, TreeNode* other_node) {
if (!node && !other_node) {
return true;
}
if (!node || !other_node) {
return false;
}
return node->val == other_node->val;
}
};
26 changes: 26 additions & 0 deletions 100_SameTree/step3.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* 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 isSameTree(TreeNode* node, TreeNode* other_node) {
if (!node && !other_node) {
return true;
}
if (!node || !other_node) {
return false;
}
if (node->val != other_node->val) {
return false;
}
return isSameTree(node->left, other_node->left) && isSameTree(node->right, other_node->right);
}
};