diff --git a/142.Linked_List_Cycle_II.md b/142.Linked_List_Cycle_II.md new file mode 100644 index 0000000..7adf7ac --- /dev/null +++ b/142.Linked_List_Cycle_II.md @@ -0,0 +1,160 @@ +1st. +方針として141.Linked List Cycleと同様にsetで訪れたnodeを保持し、訪れていた場合そのnodeを返す。訪れていない場合はsetに追加しnodeを次に進めるという操作をnodeがNoneになるまで繰り返すという方針を思いつき実装した。 +141を解いてから日が空いていないないためかスムーズに書くことが出来た。 +```Python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, x): +# self.val = x +# self.next = None + +class Solution: + def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]: + visited = set() + node = head + while node: + if node in visited: + return node + visited.add(node) + node = node.next + return None +``` + +2nd. +1st.の段階でフロイドの循環検知でも解けるか考えたが、循環していることは検知できても、fastがslowに追い付いたnodeがCycleの始まりとなっているnodeとは限らないためどうしようか迷い思いつかなかったため他の方の回答を見て実装した。 +(https://discord.com/channels/1084280443945353267/1246383603122966570/1252209488815984710) +を見て理解は出来たがこれを思いつくのは厳しそう +```Python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, x): +# self.val = x +# self.next = None + +class Solution: + def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]: + fast = head + slow = head + + while fast and fast.next: + fast = fast.next.next + slow = slow.next + if fast == slow: + break + else: + return None + + fast = head + while fast != slow: + fast = fast.next + slow = slow.next + return fast +``` +始めは下のように書いていた。 +```Python + while fast and fast.next: + fast = fast.next.next + slow = slow.next + + if fast == slow: + fast = head + while fast is not slow: + fast = fast.next + slow = slow.next + return fast + +return None +``` +しかし、whileの中のifが長くなるが見づらくなるので外に出すようにした(https://discord.com/channels/1084280443945353267/1221030192609493053/1225674901445283860) +while - else を使うのがしっくりきた。 + +Discordを見ていくとsetを用いる方法の亜種といえると思うが再帰を使う方法もあると知った。 + +その中で(https://github.com/h1rosaka/arai60/pull/4/files#diff-97a2b5510d65cd5c736cab9a3675eb2122bfe5e9ccbe491032aad6fee9d3f74d)での話は大変参考になった。 + +再帰の場合はstack overflowに注意する必要がある。これは無限再帰などでstackにdataをpushし続けることでおきる。 +* Pythonの場合は再帰呼び出しの上限回数がsys.getrecursionlimit()で確認出来る。環境依存だがデフォルトでは1000回のようで手元で試しても1000であった。(https://docs.python.org/ja/3/library/sys.html#sys.getrecursionlimit) +* setrecursionlimit()で変更もできるが大きくしすぎるとクラッシュする恐れがある。 +* C/C++でも大きくて10M程度であるようだ + +今回の例で言えばn個のnodeがcycleを持たずに並んでいた場合再帰はn回呼び出されstackにpushされることになるので入力nodeの大きさが大きくなるとstack over flowの恐れがあると考えられる。(LeetCodeのコードが実行される環境での再帰呼び出し回数の上限は分からない) + +また,クイックソートで再帰を用いる場合、stack over flow対策として再帰は短い方からやる。長い方では末尾再帰最適化を行うなどの工夫があるよう。(https://nuc.hatenadiary.org/entry/2021/03/31#:~:text=%E7%9F%AD%E3%81%84%E6%96%B9%E3%81%8B%E3%82%89%E5%86%8D%E5%B8%B0%E3%81%97%E3%81%A6%E3%80%81%E9%95%B7%E3%81%84%E6%96%B9%E3%81%AF%E6%9C%AB%E5%B0%BE%E5%86%8D%E5%B8%B0%E6%9C%80%E9%81%A9%E5%8C%96%E3%81%99%E3%82%8B%E3%81%93%E3%81%A8) + +```Python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, x): +# self.val = x +# self.next = None + +class Solution: + def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]: + visited = set() + + def _check_node(node): + if node is None: + return None + if node in visited: + return node + + visited.add(node) + + return _check_node(node.next) + + return _check_node(head) +``` + +3rd. +set()とtwo pointer no2通りの方法について3回書けるまで行った。 +```Python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, x): +# self.val = x +# self.next = None + +class Solution: + def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]: + visited = set() + node = head + + while node: + if node in visited: + return node + visited.add(node) + node = node.next + + return None +``` + +```Python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, x): +# self.val = x +# self.next = None + +class Solution: + def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]: + fast = head + slow = head + + while fast and fast.next: + fast = fast.next.next + slow = slow.next + if fast == slow: + break + else: + return None + + # breakした場合 + fast = head + while fast != slow: + fast = fast.next + slow = slow.next + return fast +``` + + +