티스토리 뷰
반응형
팰린드롬 연결 리스트(Palindrome Linked List - 234 from Leet Code)
Reference: Palindrome Linked List
🎯 문제 의의
- 문제 자체의 난이도는 높지 않았습니다. 연결 리스트로 구현된 자료를 반환하며 리스트에 담아 처리하면 기존의 팰린드롬 문제와 다를게 없기 때문이고, 또한 이러한 풀이가 다른 방식의 풀이와 속도 측면에서 큰 차이가 나지 않기 때문에 이 방법으로도 충분한 풀이입니다. 아래는 제 풀이입니다.
node를 순회하며 값을 head_list에 담아 처리하고, head_list의 palindrome 여부를 판단합니다. 위의 return문의 Boolean은 요소를 하나하나 탐색하며 팰린드롬 여부를 파악하는 것보다 더 빠른 효율을 보여줍니다. 기능적으로는 큰 차이가 없지만 이 문제를 통해 새로운 방식인 런너 기법을 알게 되었습니다. -
''' 문제 조건class ListNode:def __init__(self, val=0, next=None):self.val = valself.next = next'''# 나의 풀이class Solution:def isPalindrome(self, head: ListNode) -> bool:head_list = []while head:head_list.append(head.val)head = head.nextreturn head_list == head_list[::-1]
cs
💻 런너 기법
- 런너(Runner)는 연결 리스트를 순회할 때 2개의 포인터를 동시에 사용합니다. 두 포인터의 위치를 다르게 하여 한 포인터가 다른 포인터보다 앞서게 되면 병합 지점이나, 중간 위치, 길이 등을 판별하는 기준으로 사용할 수 있습니다.
- 2개의 포인터는 빠른 런너(Fast Runner), 느린 런너(Slow Runner)라고 부르는데, 일반적으로 빠른 런너는 두 칸씩 건너뛰고, 느린 런너는 한 칸씩 이동하게 됩니다. 이렇게 되면, 빠른 런너가 끝에 도달했을 경우 느린 런너는 중앙에 도착하게 됩니다. 이 상황을 이용해 문제를 해결할 수 있습니다.
- 성능은 위의 풀이와 비슷하지만 연결 리스트를 다른 자료형으로 변환하거나 편법을 사용하지 않고 그 자리에서 바로 풀이함으로써 좀 더 연결 리스트답게 풀 수 있습니다.
📈 런너를 이용한 문제 풀이
- 런너를 이용해 연결리스트의 Palindrome 문제를 풀이하면 아래와 같습니다.코드를 해석해보겠습니다.
-
def isPalindrome(self, head: ListNode) -> bool:rev = Noneslow = fast = headwhile fast and fast.next:fast = fast.next.nextrev, rev.next, slow = slow, rev, slow.nextif fast:slow = slow.nextwhile rev and rev.val == slow.val:slow, rev = slow.next, rev.nextreturn not rev
cs - 변수 설명
-
rev = None -> 느린 런너를 역순으로 담을 연결리스트입니다. 느린 런너의 끝이 None이어야 하므로 rev의 초기값을 None으로 초기화해줍니다.slow -> 한번에 한칸씩 움직이는 느린 런너입니다. 시작은 head 노드로 합니다.fast -> 한번에 두칸씩 움직이는 빠른 런너입니다. 느린 런너와 동시점인 head 노드에서 시작합니다.
cs - 런너 이동 경로
에서 빠른 런너(fast)가 1 -> 3 -> 5 -> 3 -> 1의 경로로 이동했을 때, 느린 런너(slow)는 1 -> 2 -> 3 -> 4 -> 5로 이동한 상태이며, rev는 4 -> 3 -> 2 -> 1 -> None 이 연결된 연결리스트가 됩니다. 즉, 여기서 느린 런너가 한칸씩 앞으로 가며 rev와 비교해서 모든 요소가 같다면 이는 Palindrome을 이룬다고 할 수 있습니다.1 -> 2 -> 3 -> 4 -> 5 -> 4 -> 3 -> 2 -> 1
- 즉, 두칸씩 움직이는 빠른 런너가 마지막에 도착했을 때, 느린 런너는 연결리스트의 정중앙에 위치하고, rev는 slow가 움직인 기록이 거꾸로 기록되어 있습니다. 이를 예시로 표현하면
- 역순 연결 리스트 구성(rev)slow = slow.next는 slow가 한 칸씩 이동한다는 것입니다.
- rev, rev.next = slow, rev는 slow가 1 -> 2로 이동할 때, rev는 2 -> 1이 됩니다. 정확히 말하면 rev라는 것은 slow의 현재보다 한칸 이전 상태이며, rev.next에는 현재 slow의 노드를 할당합니다.
-
while fast and fast.next: # fast.next가 없다면 fast.next.next에서 에러가 발생하므로 둘다 체크해야합니다.(개수가 짝수인지 홀수인지 모르기 때문)fast = fast.next.next # 두 칸씩 이동rev, rev.next, slow = slow, rev, slow.next
cs -
fast가 남아있다는 뜻은 위의 반복문에서 마지막에 fast는 존재하지만 fast.next는 존재하지 않아서 탈출한다는 뜻이고, fast가 두칸 씩 움직일 때 해당상황이 발생하기 위해서는 홀수개의 런너가 존재해야합니다. 그래서 가운데 값에 slow가 멈추는 상황을 방지하기 위해 한 칸 더 움직입니다. 위의 예시를 기준으로 생각해보면, rev는 4에 있는 상황이지만 slow는 가운데 값인 5에 멈춰있습니다. 그래서 한 칸 더 앞으로 움직여 4의 노드로 움직여주는 작업이 필요한 것입니다.if fast: slow = slow.next
- 팰린드롬을 판별합니다.
-
while rev and rev.val == slow.val:slow, rev = slow.next, rev.next
cs
📌 Learning Point
- 단순히 문제를 풀고 넘어가는 것보다 중요한 것은 문제에서 배울 수 있는 점을 찾는 것이고, 해당 문제의 난이도는 쉬웠지만 이 문제를 통해 런너의 개념을 학습하게 되었습니다. 연결리스트의 경우 이러한 속도차의 개념으로 문제를 해결할 수 있고, 여기에 추가적으로 생각해본 문제는 다음과 같습니다.
- 연결리스트가 아닌 다른 자료구조에서 사용 가능한 개념인가?
- 일반적인 투포인터 풀이 등 리스트 같은 자료형에서 index의 움직임을 다르게하여 문제를 해결할 수 있을 것 같습니다. 물론 다른 자료형의 경우 하나 하나 자료를 탐색할 경우가 아니라면 더 많은 옵션이 있기 때문에 불필요할 것 같긴 하지만 가능한 풀이라고 생각됩니다.
- 속도 차이가 2배가 아닌 문제상황이 있을 수 있는가?
- 팰린드롬은 가운데를 기준으로 양쪽이 데칼코마니 같은 형태이므로 속도차이가 2배로 진행되었지만 문제 상황에 따라 다른 속도도 가능하다고 생각됩니다.
- 연결리스트가 아닌 다른 자료구조에서 사용 가능한 개념인가?
반응형
'알고리즘 학습 > 파이썬 알고리즘 인터뷰' 카테고리의 다른 글
[Leet Code] 937. Reorder Log Files(로그 파일 재정렬) (0) | 2021.12.08 |
---|---|
[Leet Code] 125. Valid Palindrome(유효한 팰린드롬) (0) | 2021.12.05 |
[파이썬 알고리즘 인터뷰] - 7장 p.193 (Leet Code 238번) (0) | 2021.01.05 |
[파이썬 알고리즘 인터뷰] - LeetCode 15. 3Sum (7장) (0) | 2020.11.23 |
[파이썬 알고리즘 인터뷰] - 7장 (배열) - 빗물트래핑 (0) | 2020.11.22 |
댓글
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
링크
TAG
- 텀 프로젝트#백준알고리즘#Python
- filter#isalnum#lower
- N으로 표현#DP#Programmers#Python
- 공유기 설치#BOJ#이분탐색#Python
- API#lazy#
- 백준 알고리즘#BackTracking
- Brackets#Stacks and Queues#Codility#Python
- 토마토#백준알고리즘#Python
- 병든 나이트#BOJ#탐욕법#Python
- NumberofDiscIntersections#Codility#Sort#Python
- 날짜 계산#BOJ#완전탐색#Python
- 파이썬알고리즘인터뷰#4장
- PassingCars#Codility#Python
- 종이자르기#분할정복#BOJ#Python
- 쿼드트리#BOJ#분할정복#Python
- 터틀비치#리콘#xbox#controller
- 반복수열#백준알고리즘#Python
- 리모컨#완전탐색#BOJ#Python
- Distinct#Codility#Python
- 랜선자르기#이분탐색#BOJ#Python
- 미로 탐색#백준알고리즘#Python
- 암호코드#dp#BOJ#Python
- 섬의개수#백준알고리즘#Python
- 나무자르기#BOJ#이분탐색#Python
- django#slicing
- 배열합치기#분할정복#BOJ#Python
- django
- 순열사이클#BOJ#Python
- Swift#Tuples#Range
- Triangle#Sorting#Codility#Python
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
글 보관함