이번 문제는 주어진 링크드 리스트가 펠린드롬인지를 판단하는 문제입니다. 살펴보겠습니다.
Given the head
of a singly linked list, return true
if it is a palindrome or false otherwise.
Example 1:
Input: head = [1,2,2,1]
Output: true
Example 2:
Input: head = [1,2]
Output: false
Constraints:
[1, 105]
.0 <= Node.val <= 9
Follow up:
Could you do it in O(n) time and O(1) space?
주어진 연결 리스트가 펠린드롬인지를 판별하는 문제입니다.
Follow up에서 시간 복잡도 O(n), 공간 복잡도 O(1)이 가능하냐고 묻습니다. 이는 값을 저장하는 자료구조를 활용하지 않고 구현이 가능한 지를 묻는것입니다.
즉, 연결 리스트를 순회하면서 지점의 위치를 변경하는 Two Pointer를 사용한다면 공간 복잡도 O(1)이 가능할 것으로 보입니다.
주어진 문제는 연결 리스트의 노드를 순회하면서 펠린드롬인지를 확인해야 합니다.
가장 떠오른 방법은 연결 리스트의 노드 값을 빈 리스트에 저장합니다. 저장된 리스트의 값과 이것을 뒤집은 값이 같은지, 다른지를 판별하면 문제가 해결될 것으로 보입니다. 이 방법은 스택을 통해서도 비교가 가능합니다.
하지만, 해당 방법은 공간 복잡도가 O(n)이 나옵니다. 앞 서 말했듯이, 투 포인터를 활용한다면 조금 더 효율적으로 구성될 것 같습니다.
한 칸씩 이동하는 포인터와 두 칸씩 이동하는 포인터를 초기화 합니다. 그 다음 연결 리스트의 중간 까지 온다음 중간부터의 노드들의 next방향을 바꿉니다. 즉, 연결 리스트의 절반을 뒤집는 과정을 거칩니다(이전에 제가 올린 문제의 방법을 사용하면 됩니다.) 마지막으로 두 연결 리스트의 값을 비교하면 해결됩니다.
노드를 순회하기 때문에 재귀방법도 가능할 것으로 보입니다.
이렇게 문제에 대한 다양한 접근 방법을 구성 완료했습니다.
코드 설계를 해보도록 하겠습니다.
이 문제는 다양한 접근 방법을 활용할 수 있으며, 각각의 장단점과 효율성을 고려해 설계합니다.
Linked List
# Definition for singly-linked list.
class ListNode(object):
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class Solution(object):
# 1. Linked List
def isPalindrome(self, head):
# https://leetcode.com/problems/palindrome-linked-list/submissions/1467936746
"""
:type head: Optional[ListNode]
:rtype: bool
"""
a = []
curr = head
while curr:
a.append(curr.val)
curr = curr.next
if a != list(reversed(a)):
return False
return True
a
라는 리스트에 저장합니다.a
를 저장하기 위한 공간.Two Pointer
# Definition for singly-linked list.
class ListNode(object):
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class Solution(object):
# # 2. Two Pointer
def isPalindrome(self, head):
# https://leetcode.com/problems/palindrome-linked-list/submissions/1467944691
"""
:type head: Optional[ListNode]
:rtype: bool
"""
slow, fast = head, head
while fast and fast.next:
fast = fast.next.next
slow = slow.next
prev = None
while slow:
next_node = slow.next
slow.next = prev
prev = slow
slow = next_node
l, r = head, prev
while r:
if l.val != r.val:
return False
l = l.next
r = r.next
return True
fast
와 slow
포인터를 사용해 중간 지점을 찾습니다.slow
부터 끝까지 연결 리스트를 뒤집습니다.Stack
# Definition for singly-linked list.
class ListNode(object):
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class Solution(object):
# 3. Stack
def isPalindrome(self, head):
# https://leetcode.com/problems/palindrome-linked-list/submissions/1467948467
"""
:type head: Optional[ListNode]
:rtype: bool
"""
stack = []
curr = head
while curr:
stack.append(curr.val)
curr = curr.next
curr = head
while curr:
if curr.val != stack.pop():
return False
curr = curr.next
return True
재귀 방법
# Definition for singly-linked list.
class ListNode(object):
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class Solution(object):
# 4. Recursion
def isPalindrome(self, head):
# https://leetcode.com/problems/palindrome-linked-list/submissions/1467954119
"""
:type head: Optional[ListNode]
:rtype: bool
"""
self.pointer = head
def recursive_check(node):
# base case
if not node:
return True
if not recursive_check(node.next):
return False
if node.val != self.pointer.val:
return False
self.pointer = self.pointer.next
return True
return recursive_check(head)
이번 문제는 펠린드롬을 확인하는 알고리즘을 다양한 방식으로 구현해보며 연결 리스트의 다양한 접근 방법을 배울 수 있었습니다. 특히, Two Pointer 방식을 활용하면 O(n) 시간과 O(1) 공간 복잡도로 효율적으로 해결할 수 있음을 알 수 있었습니다.
연결 리스트와 펠린드롬 문제는 실전에서도 자주 등장하는 문제입니다. 이번 기회를 통해 다양한 구현 방법과 효율성을 익혀두시면 도움이 될 것입니다.
테스트 케이스를 전부 실행하는 코드는 다음 링크에서 확인할 수 있습니다.
읽어주셔서 감사합니다.