From 38636a4c26fe7e02270cb20bfb793d4ccb285c88 Mon Sep 17 00:00:00 2001 From: seal_azarashi Date: Thu, 19 Sep 2024 11:50:45 +0900 Subject: [PATCH 1/6] step 1 --- arai60/Tree_BT_BST/path-sum.md | 53 ++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 arai60/Tree_BT_BST/path-sum.md diff --git a/arai60/Tree_BT_BST/path-sum.md b/arai60/Tree_BT_BST/path-sum.md new file mode 100644 index 0000000..105d010 --- /dev/null +++ b/arai60/Tree_BT_BST/path-sum.md @@ -0,0 +1,53 @@ +# 112. Path Sum + +LeetCode URL: https://leetcode.com/problems/path-sum/description/ + +この問題は Java で解いています。 +各解法において、メソッドが属するクラスとして `Solution` を定義していますが、これは Java の言語仕様に従い、コードを実行可能にするために必要なものです。このクラス自体には特定の意味はなく、単にメソッドを組織化し、実行可能にするためのものです。 + +## Step 1 + +```java +// 解いた時間: 7分ぐらい +// 時間計算量: O(n): 最大で全ノードを走査する +// 空間計算量: O(n): 最大で全ノードがスタックに入る +class Solution { + private record NodeWithPathSum(TreeNode node, int pathSum) {}; + + public boolean hasPathSum(TreeNode root, int targetSum) { + if (root == null) { + return false; + } + + Deque nodeStack = new ArrayDeque<>(); + nodeStack.push(new NodeWithPathSum(root, 0)); + while (!nodeStack.isEmpty()) { + NodeWithPathSum nodeWithPathSum = nodeStack.pop(); + TreeNode node = nodeWithPathSum.node; + int currentPathSum = node.val + nodeWithPathSum.pathSum; + + boolean isLeafNode = node.left == null && node.right == null; + if (isLeafNode && currentPathSum == targetSum) { + return true; + } + + if (node.right != null) { + nodeStack.push(new NodeWithPathSum(node.right, currentPathSum)); + } + if (node.left != null) { + nodeStack.push(new NodeWithPathSum(node.left, currentPathSum)); + } + } + + return false; + } +} +``` + +次のようなことを考えながら実装していました: + +- DFS アプローチで解くのが適切に思える + - もっと効率よく出来ないか考えるも、全ノードを順に見ていくのは避けられないと判断 + - 値が0以上しか入り得ないなら途中で打ち切る処理も書けるが、負の数が入り得るのは constraints にも明記されてる + - 明記されてなくても、面接官に合意を取らない限りは打ち切るような処理は勝手に入れないほうがよさそう (型システムによる保証とかもないので) +- スタックオーバーフローのリスクを回避するためスタックで実装したい From d1928bc1ff67dc7882f719f4875d3e2caafd67a8 Mon Sep 17 00:00:00 2001 From: seal_azarashi Date: Thu, 19 Sep 2024 13:25:46 +0900 Subject: [PATCH 2/6] step 2 --- arai60/Tree_BT_BST/path-sum.md | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/arai60/Tree_BT_BST/path-sum.md b/arai60/Tree_BT_BST/path-sum.md index 105d010..768bed0 100644 --- a/arai60/Tree_BT_BST/path-sum.md +++ b/arai60/Tree_BT_BST/path-sum.md @@ -47,7 +47,31 @@ class Solution { 次のようなことを考えながら実装していました: - DFS アプローチで解くのが適切に思える - - もっと効率よく出来ないか考えるも、全ノードを順に見ていくのは避けられないと判断 + - もっと効率よく出来ないか考えるも、全ノードを順にリーフノードまで見ていくのは避けられないと判断 - 値が0以上しか入り得ないなら途中で打ち切る処理も書けるが、負の数が入り得るのは constraints にも明記されてる - - 明記されてなくても、面接官に合意を取らない限りは打ち切るような処理は勝手に入れないほうがよさそう (型システムによる保証とかもないので) + - 明記されてなくても、面接官に合意を取らない限りは打ち切るような処理は勝手に入れないほうがよさそう + - 値が0以上であるかどうかについては型システムによって保証されてるわけでもないので - スタックオーバーフローのリスクを回避するためスタックで実装したい + +## Step 2 + +### 再帰で DFS + +スタックオーバーフローのリスクがあるので最適解にはならないと思いますが、一応練習も兼ねて書いてみます。 + +```java +class Solution { + public boolean hasPathSum(TreeNode root, int targetSum) { + if (root == null) { + return false; + } + boolean isLeafNode = root.left == null && root.right == null; + if (isLeafNode && root.val == targetSum) { + return true; + } + return + hasPathSum(root.left, targetSum - root.val) || + hasPathSum(root.right, targetSum - root.val); + } +} +``` From a47a43ca92e1d8f25789c426d03e0ce1beea8987 Mon Sep 17 00:00:00 2001 From: seal_azarashi Date: Thu, 19 Sep 2024 13:43:54 +0900 Subject: [PATCH 3/6] step 3 --- arai60/Tree_BT_BST/path-sum.md | 43 ++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/arai60/Tree_BT_BST/path-sum.md b/arai60/Tree_BT_BST/path-sum.md index 768bed0..afd561a 100644 --- a/arai60/Tree_BT_BST/path-sum.md +++ b/arai60/Tree_BT_BST/path-sum.md @@ -60,6 +60,8 @@ class Solution { スタックオーバーフローのリスクがあるので最適解にはならないと思いますが、一応練習も兼ねて書いてみます。 ```java +// 時間計算量: O(n): 最大で全ノードを走査する +// 空間計算量: O(n): 最大で全ノードがスタックフレームに積まれる class Solution { public boolean hasPathSum(TreeNode root, int targetSum) { if (root == null) { @@ -75,3 +77,44 @@ class Solution { } } ``` + +## Step 3 + +Step 1 と同じです。 + +```java +// 解いた時間: 5分ぐらい +// 時間計算量: O(n): 最大で全ノードを走査する +// 空間計算量: O(n): 最大で全ノードがスタックに入る +class Solution { + private record NodeWithPathSum(TreeNode node, int pathSum) {}; + + public boolean hasPathSum(TreeNode root, int targetSum) { + if (root == null) { + return false; + } + + Deque nodeStack = new ArrayDeque<>(); + nodeStack.push(new NodeWithPathSum(root, 0)); + while (!nodeStack.isEmpty()) { + NodeWithPathSum nodeWithPathSum = nodeStack.pop(); + TreeNode node = nodeWithPathSum.node; + int currentPathSum = node.val + nodeWithPathSum.pathSum; + + boolean isLeafNode = node.left == null && node.right == null; + if (isLeafNode && currentPathSum == targetSum) { + return true; + } + + if (node.right != null) { + nodeStack.push(new NodeWithPathSum(node.right, currentPathSum)); + } + if (node.left != null) { + nodeStack.push(new NodeWithPathSum(node.left, currentPathSum)); + } + } + + return false; + } +} +``` From fe6fffcddfe24ca66459f6ad5c9ead8f758da7b1 Mon Sep 17 00:00:00 2001 From: seal_azarashi Date: Fri, 20 Sep 2024 12:54:01 +0900 Subject: [PATCH 4/6] step 4 --- arai60/Tree_BT_BST/path-sum.md | 65 ++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/arai60/Tree_BT_BST/path-sum.md b/arai60/Tree_BT_BST/path-sum.md index afd561a..25a4e8d 100644 --- a/arai60/Tree_BT_BST/path-sum.md +++ b/arai60/Tree_BT_BST/path-sum.md @@ -118,3 +118,68 @@ class Solution { } } ``` + +## Step 4 + +### イテレーティブな DFS + +```java +// 時間計算量: O(n): 最大で全ノードを走査する +// 空間計算量: O(n): 最大で全ノードがスタックに入る +class Solution { + private record NodeWithPathSum(TreeNode node, int pathSum) {}; + + public boolean hasPathSum(TreeNode root, int targetSum) { + if (root == null) { + return false; + } + + Deque nodeStack = new ArrayDeque<>(); + nodeStack.push(new NodeWithPathSum(root, 0)); + while (!nodeStack.isEmpty()) { + NodeWithPathSum nodeWithPathSum = nodeStack.pop(); + TreeNode node = nodeWithPathSum.node; + int currentPathSum = node.val + nodeWithPathSum.pathSum; + + boolean isLeafNode = node.left == null && node.right == null; + if (isLeafNode) { + if (currentPathSum == targetSum) { + return true; + } + + continue; + } + + if (node.right != null) { + nodeStack.push(new NodeWithPathSum(node.right, currentPathSum)); + } + if (node.left != null) { + nodeStack.push(new NodeWithPathSum(node.left, currentPathSum)); + } + } + + return false; + } +} +``` + +### 再帰で DFS + +```java +// 時間計算量: O(n): 最大で全ノードを走査する +// 空間計算量: O(n): 最大で全ノードがスタックフレームに積まれる +class Solution { + public boolean hasPathSum(TreeNode root, int targetSum) { + if (root == null) { + return false; + } + boolean isLeafNode = root.left == null && root.right == null; + if (isLeafNode) { + return root.val == targetSum; + } + return + hasPathSum(root.left, targetSum - root.val) || + hasPathSum(root.right, targetSum - root.val); + } +} +``` From 1834c7179e9776bf0b660fb7d6b9c1bf02f3d935 Mon Sep 17 00:00:00 2001 From: seal_azarashi Date: Wed, 16 Oct 2024 07:26:08 +0900 Subject: [PATCH 5/6] fix --- arai60/Tree_BT_BST/path-sum.md | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/arai60/Tree_BT_BST/path-sum.md b/arai60/Tree_BT_BST/path-sum.md index 25a4e8d..6bfaa99 100644 --- a/arai60/Tree_BT_BST/path-sum.md +++ b/arai60/Tree_BT_BST/path-sum.md @@ -141,8 +141,7 @@ class Solution { TreeNode node = nodeWithPathSum.node; int currentPathSum = node.val + nodeWithPathSum.pathSum; - boolean isLeafNode = node.left == null && node.right == null; - if (isLeafNode) { + if (node.left == null && node.right == null) { if (currentPathSum == targetSum) { return true; } @@ -157,7 +156,6 @@ class Solution { nodeStack.push(new NodeWithPathSum(node.left, currentPathSum)); } } - return false; } } @@ -173,9 +171,8 @@ class Solution { if (root == null) { return false; } - boolean isLeafNode = root.left == null && root.right == null; - if (isLeafNode) { - return root.val == targetSum; + if (root.left == null && root.right == null) { + return root.val == targetSum; } return hasPathSum(root.left, targetSum - root.val) || From f06e9ad36b41eddde035cd198383a921ff5cb7b2 Mon Sep 17 00:00:00 2001 From: seal_azarashi Date: Thu, 17 Oct 2024 07:14:56 +0900 Subject: [PATCH 6/6] add step 4 --- arai60/Tree_BT_BST/path-sum.md | 43 ++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/arai60/Tree_BT_BST/path-sum.md b/arai60/Tree_BT_BST/path-sum.md index 6bfaa99..0091df0 100644 --- a/arai60/Tree_BT_BST/path-sum.md +++ b/arai60/Tree_BT_BST/path-sum.md @@ -123,6 +123,10 @@ class Solution { ### イテレーティブな DFS +次の指摘に対応: + +- https://github.com/seal-azarashi/leetcode/pull/24#discussion_r1781228402 + ```java // 時間計算量: O(n): 最大で全ノードを走査する // 空間計算量: O(n): 最大で全ノードがスタックに入る @@ -161,6 +165,45 @@ class Solution { } ``` +スタックには null も入るようにして、チェックを pop してから行うパターン (参考: https://github.com/seal-azarashi/leetcode/pull/24#discussion_r1781230995) + +```java +// 時間計算量: O(n): 最大で全ノードを走査する +// 空間計算量: O(n): 最大で全ノードがスタックに入る +class Solution { + private record NodeWithPathSum(TreeNode node, int pathSum) {}; + + public boolean hasPathSum(TreeNode root, int targetSum) { + if (root == null) { + return false; + } + + Deque nodeStack = new ArrayDeque<>(); + nodeStack.push(new NodeWithPathSum(root, 0)); + while (!nodeStack.isEmpty()) { + NodeWithPathSum nodeWithPathSum = nodeStack.pop(); + TreeNode node = nodeWithPathSum.node; + + if (node == null) { + continue; + } + + int currentPathSum = node.val + nodeWithPathSum.pathSum; + if (node.left == null && node.right == null) { + if (currentPathSum == targetSum) { + return true; + } + + continue; + } + nodeStack.push(new NodeWithPathSum(node.right, currentPathSum)); + nodeStack.push(new NodeWithPathSum(node.left, currentPathSum)); + } + return false; + } +} +``` + ### 再帰で DFS ```java