|
| 1 | +# https://leetcode.com/problems/remove-nth-node-from-end-of-list/ |
| 2 | + |
| 3 | +from typing import Optional |
| 4 | + |
| 5 | +# Definition for singly-linked list. |
| 6 | +class ListNode: |
| 7 | + def __init__(self, val=0, next=None): |
| 8 | + self.val = val |
| 9 | + self.next = next |
| 10 | + |
| 11 | +class Solution: |
| 12 | + def removeNthFromEnd_recur(self, head: Optional[ListNode], n: int) -> Optional[ListNode]: |
| 13 | + """ |
| 14 | + [Complexity] |
| 15 | + - TC: O(len) |
| 16 | + - SC: O(len) (call stack) |
| 17 | +
|
| 18 | + [Approach] |
| 19 | + 재귀적으로 linked list를 확인하며 끝에서부터의 순서를 확인 후, 다음 node의 순서가 n과 같을 때 다음 node를 건너뛰도록 한다. |
| 20 | + head node를 제거해야 하는 경우(= 끝에서부터 n번째인 node가 head인 경우)에는, 재귀 함수 내에서 node 제거가 불가능하므로 head.next를 반환한다. |
| 21 | + """ |
| 22 | + |
| 23 | + def check_order_from_end(curr): |
| 24 | + # base condition |
| 25 | + if not curr.next: |
| 26 | + return 1 |
| 27 | + |
| 28 | + # recur |
| 29 | + next_order_from_end = check_order_from_end(curr.next) |
| 30 | + # nth from end인 node를 건너뛰기 |
| 31 | + if next_order_from_end == n: |
| 32 | + curr.next = curr.next.next |
| 33 | + |
| 34 | + return next_order_from_end + 1 |
| 35 | + |
| 36 | + # head node를 제거해야 하는 경우라면, check_order_from_end() 내에서 node 제거가 불가능하므로 head.next를 반환해야 함 |
| 37 | + if check_order_from_end(head) == n: |
| 38 | + return head.next |
| 39 | + |
| 40 | + return head |
| 41 | + |
| 42 | + def removeNthFromEnd_recur2(self, head: Optional[ListNode], n: int) -> Optional[ListNode]: |
| 43 | + """ |
| 44 | + [Complexity] |
| 45 | + - TC: O(len) |
| 46 | + - SC: O(len) (call stack) |
| 47 | +
|
| 48 | + [Approach] |
| 49 | + 이전 풀이에서 head node를 제거해야 하는 경우를 따로 처리하지 않으려면, 주어진 head를 가리키는 dummy node(= prev)를 추가하고 |
| 50 | + dummy node의 next node를 반환하면 된다. |
| 51 | + """ |
| 52 | + |
| 53 | + def check_order_from_end(curr): |
| 54 | + # base condition |
| 55 | + if not curr.next: |
| 56 | + return 1 |
| 57 | + |
| 58 | + # recur |
| 59 | + next_order_from_end = check_order_from_end(curr.next) |
| 60 | + # nth from end인 node를 건너뛰기 |
| 61 | + if next_order_from_end == n: |
| 62 | + curr.next = curr.next.next |
| 63 | + |
| 64 | + return next_order_from_end + 1 |
| 65 | + |
| 66 | + prev = ListNode(next=head) |
| 67 | + check_order_from_end(prev) |
| 68 | + |
| 69 | + return prev.next |
| 70 | + |
| 71 | + def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]: |
| 72 | + """ |
| 73 | + [Complexity] |
| 74 | + - TC: O(len) |
| 75 | + - SC: O(1) |
| 76 | +
|
| 77 | + [Approach] |
| 78 | + slow, fast의 two pointer를 이용해 반복문으로 풀 수 있다. |
| 79 | + 1. fast를 n 번 전진 |
| 80 | + 2. fast가 끝에 도달한 경우, 첫 번째 node를 제거해야하므로 head.next 반환 |
| 81 | + 3. 현재 fast의 위치에서 slow와 fast를 함께 전진하면, fast가 끝에 도달할 때 slow는 뒤에서부터 n + 1번째 node임 |
| 82 | + 4. 뒤에서부터 n + 1번째인 node가 n - 1번째 node를 가리키도록 함 |
| 83 | + """ |
| 84 | + slow = fast = head |
| 85 | + |
| 86 | + # 1. fast를 n 번 전진 |
| 87 | + for _ in range(n): |
| 88 | + fast = fast.next |
| 89 | + |
| 90 | + # 2. fast가 끝에 도달한 경우, 첫 번째 node를 제거해야하므로 head.next 반환 |
| 91 | + if not fast: |
| 92 | + return head.next |
| 93 | + |
| 94 | + # 3. 현재 fast의 위치에서 slow와 fast를 함께 전진하면, |
| 95 | + # fast가 끝에 도달할 때 slow는 뒤에서부터 n + 1번째 node임 |
| 96 | + while fast.next: |
| 97 | + slow, fast = slow.next, fast.next |
| 98 | + |
| 99 | + # 4. 뒤에서부터 n + 1번째인 node가 n - 1번째 node를 가리키도록 함 |
| 100 | + slow.next = slow.next.next |
| 101 | + |
| 102 | + return head |
0 commit comments