오늘 문제는 링크드 리스트를 이용하는 기본적인 문제입니다. 하지만 구현적으로는 나름 skillful하다고 생각하는 문제입니다.
그럼 살펴보겠습니다!
Given the head
of a singly linked list, reverse the list, and return the reversed list.
Example 1:
Input: head = [1,2,3,4,5]
Output: [5,4,3,2,1]
Example 2:
Input: head = [1,2]
Output: [2,1]
Example 3:
Input: head = []
Output: []
Constraints:
[0, 5000]
.5000 <= Node.val <= 5000
Follow up: A linked list can be reversed either iteratively or recursively. Could you implement both?
링크드 리스트 head가 주어졌을때, 이것을 뒤집은 링크드 리스트를 반환하는 문제입니다.
링크드 리스트 내 노드를 순회하면서 노드의 next 방향을 뒤집으면서 진행하면 될 것으로 보입니다. 문제의 입출력을 제약 조건과 함께 보도록 하겠습니다.
이 내용들을 기반으로 바로 문제를 더 자세하게 분석해보겠습니다.
주어진 링크드 리스트의 노드를 순회하면서, 현재 노드가 가리키는 방향을 바꾸면 됩니다.
노드를 순차적으로 순회하기 때문에 반복문과 재귀를 사용할 수 있습니다.
두 방법 모두 시간 복잡도가 으로 매우 효율적일것으로 예상됩니다.
1. 반복문 접근
next
포인터를 이전 노드를 가리키도록 뒤집습니다.prev
(이전 노드), curr
(현재 노드), next_node
(다음 노드)를 사용하여 포인터를 조작합니다.prev
: 결과 리스트의 마지막 노드를 가리킵니다. 초기값은 None
으로 설정합니다.curr
: 현재 작업 중인 노드입니다. 초기값은 입력받은 head
입니다.next_node
: curr
의 다음 노드를 임시 저장하여 포인터를 잃지 않도록 합니다.2. 재귀 접근
next
포인터를 뒤집습니다.next
포인터를 역방향으로 설정합니다.head
가 됩니다.node
가 None
이면, 현재 prev
를 반환합니다.바로 코드 설계를 진행해보도록 하겠습니다.
반복문 방법
prev = None
: 이전 노드를 초기화합니다.curr = head
: 현재 작업할 노드를 초기화합니다.curr
가 None
이 아닐 때까지 반복합니다.curr.next
포인터를 prev
로 변경합니다.prev
를 curr
로 이동하고, curr
을 다음 노드로 이동합니다.prev
는 뒤집힌 리스트의 새로운 head
가 되므로 반환합니다.재귀 방법
recursive_reversed(prev, node)
:prev
: 현재 노드의 이전 노드.node
: 현재 작업 중인 노드.node
가 None
이면, 현재 prev
를 반환합니다.node.next
를 prev
로 설정하고, 다음 호출로 이동합니다.recursive_reversed(None, head)
를 호출하여 시작합니다.prev
를 최종 반환합니다.반복문 방법
# 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. iterative
def reverseList(self, head):
# https://leetcode.com/problems/reverse-linked-list/submissions/1467929284
"""
:type head: Optional[ListNode]
:rtype: Optional[ListNode]
"""
prev = None
curr = head
while curr:
next_node = curr.next # 현재 노드의 다음 노드를 저장
curr.next = prev # 현재 노드의 다음 포인터를 뒤집어서 이전 노드를 가리키게 함
prev = curr # 결과 리스트의 헤드를 현재 노드로 업데이트
curr = next_node # 다음 노드로 이동
return prev
코드 설명:
next_node
는 현재 노드의 next
를 저장하여 순회를 계속 진행합니다.curr
)의 next
포인터를 이전 노드(prev
)로 뒤집습니다.prev
와 curr
을 업데이트하며 리스트를 순회합니다.결과: https://leetcode.com/problems/reverse-linked-list/submissions/1467929284
재귀 방법
# 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. recursion
def reverseList(self, head):
# https://leetcode.com/problems/reverse-linked-list/submissions/1467928535
"""
:type head: Optional[ListNode]
:rtype: Optional[ListNode]
"""
def recursive_reversed(prev, node):
if node:
next_node, node.next = node.next, prev # 다음 노드를 저장하고 포인터 뒤집기
return recursive_reversed(node, next_node) # 현재 노드를 prev로, 다음 노드를 node로 진행
else:
return prev # node가 None일 때, 이전 노드(prev)가 새 헤드가 됨
return recursive_reversed(None, head)
코드 설명:
prev
와 node
를 인자로 받아, 현재 노드의 포인터를 뒤집은 뒤 다음 호출로 이동합니다.head
(prev
)를 반환합니다.시간 복잡도:
공간 복잡도:
결과: https://leetcode.com/problems/reverse-linked-list/submissions/1467928535
이번 문제는 Linked List의 구조와 포인터 조작을 이해하는 데 매우 유용한 기본 문제였습니다. 반복문과 재귀를 활용하여 문제를 해결하며, 각각의 접근 방식에서 장단점을 비교할 수 있었습니다.
주요 학습 내용
next
포인터를 변경하여 리스트를 뒤집는 방법을 학습했습니다.링크드 리스트를 뒤집는 문제는 단순히 알고리즘 구현을 넘어 데이터 구조의 동작 원리를 이해하는 데 매우 좋은 연습 문제였습니다. 특히, 반복문과 재귀 방식의 구현 차이를 비교하며 더 깊이 학습할 수 있었습니다.
이 문제를 통해 포인터 조작의 중요성과 효율적인 알고리즘 작성의 기본 원리를 다시 한번 깨달을 수 있었습니다. 앞으로도 다양한 방식으로 문제를 해결해보며, 상황에 맞는 최적의 방법을 선택하는 연습을 계속해 나가겠습니다. 💪
테스트 케이스를 전부 실행하는 코드는 다음 링크에서 확인할 수 있습니다.
읽어주셔서 감사합니다!!