🤔 나의 풀이
📌 문제
- 파이썬 알고리즘 인터뷰 19번 문제
- 연결리스트의 인덱스 m~n까지를 역순으로 만들어라.
📌 날짜
2020.01.28~29
📌 시도 횟수
10 try+ / 이틀 고민해서 결국! 풀어냈지만 풀이가 너무 구닥다리니까 예쁜 풀이로 다시 풀어보자~
💡 Code
class Solution:
def reverseList(self, head: ListNode):
root, node, prev = head, head, None
while node:
next, node.next = node.next, prev
prev, node = node, next
return prev, root
def reverseBetween(self, head: ListNode, m: int, n: int) -> ListNode:
root = start = ListNode(0)
root.next = head
if not head or m == n:
return head
for i in range(1, m):
start = start.next
end = start.next
new_root = node = ListNode()
for i in range(n - m + 1):
node.next = ListNode(end.val)
end = end.next
node = node.next
check_n, check_m = self.reverseList(new_root.next)
start.next = check_n
check_m.next = end
return root.next
💡 문제 해결 방법
☆15번째 문제 15.reverse Linked List에서 역순 링크드 리스트를 만드는 방법을 활용했다☆
1. 먼저 m~n번쨰 노드들로 이루어진 새로운 링크드리스트를 따로 만든다.
2. 그리고 이 링크드리스트의 head(루트)를 역순링크드리스트 만드는 함수에 넘긴다.
>> (def reverseList(self, head: ListNode):)로 head를 넘김
3. 참고로 넘기기 전에 이 부분 역순 링크드 리스트를 연결하기 위해 해당 링크드 리스트의 앞, 뒤 노드를 미리 찾아논다.
>> ex. [1, 2, 3, 4, 5]에서 m=2, n=4라고 하면 start = 1, end = 5이다.
>> 위의 코드에서는 start, end가 역순 링크드리스트의 앞, 뒤 노드이다.
4. 역순 링크드리스트의 반환값을 살짝 변형했다. 변환한 역순 링크드 리스트의 처음, 끝을 차례대로 반환했다.
5. 이렇게 부분 역순 링크드리스트의 처음, 끝을 각각 check_n, check_m에 받았다.
>> ex. [1, 2, 3, 4, 5]에서 m=2, n=4라고 하면...
>> self.reverseList(new_root.next)의 반환값은 각각 4, 2이다.
>> 따라서 check_m = 4, check_n = 2이다.
6. 이제 start, end와 check_n, check_m을 차례대로 연결해준다.
start.next = check_n이고, check_n.next = end이다.
>> ex. [1, 2, 3, 4, 5]에서 m=2, n=4라고 하면...
>> start = 1 ---> check_m = 4
>> check_n = 2 ---> end = 5
7. root.next를 반환한다.
따라서 최종적으로 1 -> 4 -> 3 -> 2 -> 5가 된다~
💡 새롭게 알게 된 점
- m~n번쨰 노드들로 이루어진 새로운 링크드리스트를 따로 만들었다는 점이 좀 비효율적인 것 같다.
❌ (한번에 맞추지 못한 경우) 오답의 원인
1. root 설정 오류! (이부분! ↓ ↓ ↓)
----------------------------
root = start = ListNode(0)
root.next = head
----------------------------
- 처음에는 그냥 root = head라고 해주었더니 처음부터 reversed 된 경우를 적용하지 못했다ㅠ
- 따라서 실제로 이동시킬 start에 root를 연결해야 (m = 1, n = 1보다 큰 수)인 경우를 제대로 reverse 할 수 있다.
2. 예외 처리 안함
----------------------------
if not head or m == n:
return head
----------------------------
- 이 부분이 생각보다 골칫거리였다ㅠ
- 그냥 코드 처음부터 예외로 처리해주니까 얼마나 편한가~~😵
😉 다른 풀이
📌 하나. 반복 구조로 뒤집기💯
class Solution:
def reverseBetween(self, head: ListNode, m: int, n: int) -> ListNode:
if not head or m == n:
return head
root = start = ListNode(0)
root.next = head
for _ in range(1, m):
start = start.next
end = start.next
for _ in range(n-m):
temp = start.next
start.next = end.next
end.next = end.next.next
start.next.next = temp
return root.next
문제 해결 방법
- start, end는 끝까지 값이 변하지 않는 점을 이해하자.
- 변하는 것은 start와 end가 가리키는 대상이다.
- 이때 temp는 현재 역순으로 바뀐 값들 중 맨 앞에 위치하고 있는 값이다.
- 즉 temp만 계속해서 갱신될 뿐, start와 end는 변하지 않고 각각이 가리키는 대상만 변한다.
1. 일단 세팅을 다 해논다. (m위치에 있는 값이 start, m+1위치에 있는 값이 end)
>> 참고로 end는 인덱스가 n인 위치로 한칸씩 한칸씩 이동할 것이다.
2. 우리가 역순으로 바꿔야 할 부분은 인덱스가 m ~ n인 부분이다.
3. 따라서 역순으로 바꿔야 할 부분의 처음을 항상 temp로 설정한다.
(temp의 갱신이 m-n번 실행되면 end는 최종적으로 인덱스 n의 위치가 된다.)
4. 이제 이 temp 앞으로 end.next를 가져올 것이다.
---------------------------------------------------
* 예시로 이해해보자
ex. 1 -> 2 -> 3 -> 4 -> 5 (m=2, n=4라고 가정하자.)
[현재 1회]
- start = 2, end = 3, temp = 2
- 역순 맨 앞에 end.next를 가져온다.
=> 1 -> "3" -> 2 -> 4 -> 5
[현재 2회]
- start = 2, end = 2, temp = 3
- 역순 맨 앞에 end.next를 가져온다.
=> 1 -> "4" -> 3 -> 2 -> 5
-> 최종적으로 (m-n)번 갱신한 결과, end이 인덱스 n의 위치로 이동했다!
---------------------------------------------------
5. temp 앞으로 end.next를 가져오기 위해...
(1) 일단 현재 temp = start.next(맨 앞)
(2) start.next = end.next (맨 앞으로 end.next 끌어오기)
(3) end.next = end.next.next (원래 end 위치를 end.next와 바꿈)
(4) start.next.next = temp (역방향 연결)
- 으아 머리로 생각하려니까 너무 복잡하다.
- 다시 풀어보면서 익숙해지자. 머리도 좀 빨리빨리 굴리고..
💡 새롭게 알게 된 점
-