Skip to content
Open
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
156 changes: 156 additions & 0 deletions 142. Linked List Cycle II/LinkedListCycleII.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
# step1 何も見ずに解く

## 解答
- 147. Linked List Cycleそのままなので、何も悩まずかけた。
- 計算量
- 時間計算量: O(n)
- 空間計算量: O(n)
```java
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode detectCycle(ListNode head) {
if (head == null) {
return null;
}

HashSet<ListNode> visitedNodes = new HashSet<>();
Copy link

Choose a reason for hiding this comment

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

自分なら、HashSet の実装に依存しないコードであれば、

Set<ListNode> visitedNodes = new HashSet<>();

のように、インターフェースで受け取るのですが、好みの問題かもしれません。

Copy link
Owner Author

Choose a reason for hiding this comment

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

確かにHashSet固有のメソッドを使用していないので、HashSetで宣言する意味はあまり無いですね。
一旦、nodchipさんの好みを真似してインターフェース優先で宣言するようにしてみます。

ListNode node = head;

while (node != null) {
if (visitedNodes.contains(node)) {
return node;
}
visitedNodes.add(node);
node = node.next;
}
return null;
}
}
```

## 解答2
- [Rでフロイドの循環検出法を可視化する](https://qiita.com/nozma/items/bfa3e089cd432b74c10d) で始点検出方法を理解し、実装してみる
- 運用するプログラムの場合、フロイドの循環検出法の概要からコメントを書くが、この場合どこまで書くか
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.

たしかにそれくらいは書いておくべきでした。
名前さえ分かればググれますからね。

- 計算量
- 時間計算量: O(n)
- 空間計算量: O(1)
```java
public class Solution {
public ListNode detectCycle(ListNode head) {
if (head == null) {
return null;
}

// ループを検知
ListNode slowNode = head;
ListNode fastNode = head;
while (fastNode != null && fastNode.next != null) {
slowNode = slowNode.next;
fastNode = fastNode.next.next;
// 衝突を検知したら中断
if (slowNode == fastNode) {
break;
}
}

if (fastNode == null || fastNode.next == null) {
return null;
}

// ループの始点を探索
// fastNodeを先頭に戻し、衝突するまで進める。
// 衝突した地点がループの始点
fastNode = head;
while (fastNode != slowNode) {
slowNode = slowNode.next;
fastNode = fastNode.next;
}
return fastNode;
}
}
```


# step2 他の方の解答を見る

Choose a reason for hiding this comment

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

どの方の解答を参考したのかがわかるようにPRのリンクを貼っておくと、参考にした実装からどのあたりを変えた・変えなかったのかがわかってより思考プロセスが浮き彫りになり、議論が盛り上がるかなと思いますので、貼ることをお勧めします。

Copy link
Owner Author

Choose a reason for hiding this comment

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

ありがとうございます!
次のPRあたりから参照したPRを明示し始めました。

Setの解法についてはあまり変更する余地が思いつかなかったので、フロイドの循環検出法で実装

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.

実装例の紹介ありがとうございます!

## 解答
- ループ検知部分を関数化してみる。
- collisionがパッと思い浮かんだのですがcollisionは違和感あるでしょうか?
Copy link

Choose a reason for hiding this comment

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

(GPTに聞いた意見ですが)collisionは例外やバグっぽい響きがあるということでGPTにはmeetingを勧められました

Copy link
Owner Author

Choose a reason for hiding this comment

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

やはりcollisionはそういうニュアンスを感じますよね。
「出会う」や「追いつく」みたいなニュアンスの語をあてた方が親切でした。ありがとうございます!

- step1だと最初に定義した変数を使い回していたが、ループ始点検知のための処理の部分で再定義した方が意味がイメージしやすいなと思い真似してみる
```java
public class Solution {
public ListNode detectCycle(ListNode head) {
if (head == null) {
return null;
}

ListNode collisionNode = findCollisionNode(head);
if (collisionNode == null) {
return null;
}

ListNode fromStart = head;
ListNode fromCollision = collisionNode;

while (fromStart == fromCollision) {
Copy link

Choose a reason for hiding this comment

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

javaに詳しくないので違うかもしれませんが、等しくない限り続けるので!=ではないでしょうか?

Copy link
Owner Author

Choose a reason for hiding this comment

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

おっしゃる通り、!=が正しいです。
お恥ずかしいです。
ありがとうございます!

Choose a reason for hiding this comment

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

これは、step2通ったんでしょうか?
通してみることをおすすめします。通るのであれば原因が気になりますし。通してないのであれば、それはそれで手に馴染んでないので気になります。

Choose a reason for hiding this comment

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

あー理解しました。Step3で通す前に修正して、passは考えてない感じなんですね。すいません

Copy link
Owner Author

Choose a reason for hiding this comment

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

ご指摘のとおり、正しく動作しません。
コード整理するときにミスってました。
きちんと確認する癖つけたいと思います。

fromStart = fromStart.next;
fromCollision = fromCollision.next;
}
return fromStart;
}

// 衝突したノードを返す、衝突しなければnullを返す
private ListNode findCollisionNode(ListNode node) {
ListNode fastNode = node;
ListNode slowNode = node;

while (fastNode != null && fastNode.next != null) {
fastNode = fastNode.next.next;
slowNode = slowNode.next;

if (fastNode == slowNode) {
return fastNode;
}
}
return null;
}
}
```

# step3 3回ミスなく書く
Set解法で。

Choose a reason for hiding this comment

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

ちなみに Set 解法で step 3に臨もうと思った理由はなんでしょうか?
step 2で hare and tortoise の解法を学んでいらっしゃったのであえてまた Set に戻して解いた理由が気になります。

Copy link
Owner Author

Choose a reason for hiding this comment

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

Setを用いた実装のほうがシンプルで分かりやすいからです。
hare and tortoiseの方が高速なのは確かですが、そこの高速化が必要になる場面は少ないかなと思いまして。
それであれば誰が見ても一目で分かるSetを使った解法を優先すべきかなと判断しました。

141と合わせて何度も書いたのでスラスラ書ける。
step1と同様のコード。
所要時間 2分足らず
## 解答
```java
public class Solution {
public ListNode detectCycle(ListNode head) {
if (head == null) {
Copy link

Choose a reason for hiding this comment

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

一応こちらの条件分岐は無くてもいけますね。nullの場合は確かに例外的な感じがするのであってもよいとも思います。

return null;
}

HashSet<ListNode> visitedNodes = new HashSet<>();
ListNode node = head;

while (node != null) {
if (visitedNodes.contains(node)) {
return node;
}
visitedNodes.add(node);
node = node.next;
}
return null;
}
}
```