-
Notifications
You must be signed in to change notification settings - Fork 0
Remove Nth Node From End Of List #142
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| # 19. Remove Nth Node From End of List | ||
|
|
||
| ## step1 | ||
| 一巡して長さを求めて、もう一巡して削除を行う。8mぐらい。今回はミスがなかった。 | ||
|
|
||
| 削除したNodeのnextをNoneに変更した。メモリリークを明示的に防ぐためである。 | ||
|
|
||
| 制約も小さく、解くだけなら簡単な問題なので、他の方針も考える。 | ||
|
|
||
| > Follow up: Could you do this in one pass? | ||
|
|
||
| hashmapを使えば実現できる | ||
|
|
||
| nth がendから数えたものなので、一度最後まで走査しないと削除が実行できない。これ以外の方針は思いつかない。 | ||
|
|
||
| ## 他の人のコード | ||
|
|
||
| https://github.com/thonda28/leetcode/pull/18 | ||
|
|
||
| slowとfastを使うのか、なるほど。自分で思いつきたいところだった。 | ||
|
|
||
| > step1のslow, fastの方が(個人的には)読みやすく感じました | ||
|
|
||
|
|
||
| このコードでは上述のメモリリークの対策はしていないようだが、した方が良いように思う | ||
|
|
||
| 追記: このコードがなくても参照カウントが0になるためメモリリークは起きないと思われる | ||
|
|
||
| ## step2 | ||
|
|
||
| ## pythonのメモリ管理について | ||
|
|
||
| https://daobook.github.io/devguide/garbage_collector.html | ||
|
|
||
| > The main garbage collection algorithm used by CPython is reference counting. | ||
|
|
||
| > ... it would never be cleaned just by simple reference counting. For this reason some additional machinery is needed to clean these reference cycles between objects once they become unreachable. This is the cyclic garbage collector, usually called just Garbage Collector (GC), ... | ||
|
|
||
| > Doubly linked lists are used because they efficiently support most frequently required operations. In general, the collection of all objects tracked by GC are partitioned into disjoint sets, each in its own doubly linked list. | ||
|
|
||
| - 主に参照カウント方式でgcが実装されている | ||
| - それだけでは防げない循環参照をcyclic garbage collectorで処理する | ||
| - 双方向リストによってオブジェクトの集合ごとに管理されている | ||
| - 循環参照の特定: 双方向連結リストにまとめる -> 身内同士の参照カウントをdecrement -> 参照が正のオブジェクトから辿れるオブジェクトをBFSし、参照カウント0のものは1にする -> 参照カウント0のオブジェクトを削除 | ||
| - 再帰関数を使わない -> 省メモリ | ||
| - 世代別GC: 「新しく作られたオブジェクトのほとんどは、すぐに使われなくなる」仮説を利用する。世代の小さいものを優先的に削除する |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| # Definition for singly-linked list. | ||
| # class ListNode: | ||
| # def __init__(self, val=0, next=None): | ||
| # self.val = val | ||
| # self.next = next | ||
| class Solution: | ||
| def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]: | ||
| index_to_node = {} | ||
| index = 0 | ||
| node = head | ||
| while node is not None: | ||
| index_to_node[index] = node | ||
| node = node.next | ||
| index += 1 | ||
|
|
||
| index_from_start = index - n | ||
| if index_from_start < 0: | ||
| raise ValueError(f"n is too large: {n}") | ||
| if index_from_start == 0: | ||
| return head.next | ||
|
|
||
| index_to_node[index_from_start - 1].next = index_to_node[index_from_start].next | ||
| index_to_node[index_from_start].next = None | ||
|
|
||
| return head |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| # Definition for singly-linked list. | ||
| # class ListNode: | ||
| # def __init__(self, val=0, next=None): | ||
| # self.val = val | ||
| # self.next = next | ||
| class Solution: | ||
| def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]: | ||
| count = 0 | ||
| node = head | ||
| while node is not None: | ||
| node = node.next | ||
| count += 1 | ||
|
|
||
| index_from_start = count - n | ||
| if index_from_start < 0: | ||
| raise ValueError(f"n is too large: {n}") | ||
| if index_from_start == 0: | ||
| return head.next | ||
|
|
||
| node = head | ||
| for _ in range(index_from_start - 1): | ||
| node = node.next | ||
|
|
||
| temp = node.next.next | ||
| node.next.next = None | ||
| node.next = temp | ||
|
|
||
| return head |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| # Definition for singly-linked list. | ||
| class ListNode: | ||
| def __init__(self, val=0, next=None): | ||
| self.val = val | ||
| self.next = next | ||
|
|
||
|
|
||
| class Solution: | ||
| def removeNthFromEnd(self, head: ListNode | None, n: int) -> ListNode | None: | ||
| dummy = ListNode(next=head) | ||
| slow = dummy | ||
| fast = dummy | ||
| for _ in range(n): | ||
| fast = fast.next | ||
|
|
||
| if fast is None: | ||
| raise ValueError(f"n is too large: {n}") | ||
|
|
||
| while fast.next: | ||
| slow = slow.next | ||
| fast = fast.next | ||
|
|
||
| removed_node = slow.next | ||
| slow.next = removed_node.next | ||
| removed_node.next = None | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
上の話に相当する部分だと思いますが、removed_node自体へのレファレンスカウントが0になり削除されそうなので、Noneにセットしないでも、特に悪影響はなさそうですかね。 Pythonでメモリリークが起きるのはどういう時ですかね?
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. この部分はなくてもメモリリークは起こらなそうです。参照カウント方式のGCが行われるためですね。 グローバル変数などプログラム終了時まで解放されないオブジェクトが膨らむとメモリリークが起きそうです(AIに聞きました)
調べてみるとpandasなどc言語で書かれたライブラリの内部でメモリリークが起きる場合もあるようです
|
||
|
|
||
| return dummy.next | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
スピード自体は同じなので、behind、aheadなど位置関係を表す単語を使っても良さそうです。