Menu
Coddy logo textTech

이중 연결 리스트

마지막 업데이트

이중 연결 리스트는 각 노드가 두 개의 포인터를 갖는 연결 리스트입니다: next(다음 노드로)와 prev(이전 노드로). 이 추가된 역방향 링크 덕분에 양방향으로 순회할 수 있고, 이미 참조를 가지고 있는 노드는 O(1)에 삭제할 수 있습니다. 머리부터 걸어가지 않고 그 앞 노드에 곧바로 도달할 수 있기 때문입니다. 위의 재생 버튼을 눌러 노드가 양방향으로 연결되고 다시 연결되는 모습을 확인하세요.

그 비용은 노드마다 포인터 하나가 더 필요하고 관리 부담이 커진다는 점입니다. 모든 삽입과 삭제는 이웃 노드의 nextprev를 모두 갱신해야 합니다. 이는 양쪽 끝에서의 빠른 제거가 중요한 여러 덱(deque)과 LRU 캐시의 바탕이 되는 구조입니다.

시간 복잡도

연산복잡도비고
머리/꼬리에 삽입O(1)머리와 꼬리 포인터가 있을 때
알려진 노드 삭제O(1)prev에 직접 도달, 순회 없음
검색O(n)여전히 선형 탐색
인덱스로 접근O(n)임의 접근 불가

단일 연결 리스트와 이중 연결 리스트 비교

측면단일이중
노드당 포인터1 (next)2 (next + prev)
역방향 순회불가가능
알려진 노드 삭제O(n) (prev 찾기)O(1)
메모리 오버헤드더 낮음더 높음

예제 풀이

뒤에 추가하여 [10, 20, 30]을 만든 뒤 노드 20을 삭제하기:

단계구조동작
시작null빈 리스트: headtail 모두 null
10 추가10첫 노드; headtail이 이를 가리키고 prev = next = null
20 추가10 <-> 2010.next = 2020.prev = 10 설정; tail = 20
30 추가10 <-> 20 <-> 3020.next = 3030.prev = 20 설정; tail = 30
20 삭제10 <-> 3020.prev20.next를 사용해 10.next = 3030.prev = 10O(1)에 설정

이중 연결 리스트를 사용할 때

사용할 때피할 때
양방향으로 순회하거나 이어 붙여야 할 때앞으로만 이동할 때: 단일 리스트가 더 가볍다
이미 참조를 가진 임의의 노드를 삭제할 때 (O(1))주로 위치로 인덱싱할 때: O(1) 접근에는 배열을 사용하라
덱, LRU 캐시, 실행 취소 기록을 구현할 때메모리가 빠듯할 때: 추가 prev 포인터가 링크 오버헤드를 두 배로 만든다
삽입과 제거가 양쪽 끝에서 자주 일어날 때데이터가 작고 거의 바뀌지 않을 때: 배열 복사가 더 저렴하다

Doubly Linked List 코드

Python, JavaScript, Java, C++, C로 작성된 깔끔하고 실행 가능한 Doubly Linked List 구현입니다. 언어를 선택해 코드를 복사하거나 Coddy 플레이그라운드에서 바로 열어보세요.

Python로 구현한 Doubly Linked List 코드

Python
1class Node:2    def __init__(self, value):3        self.value = value4        self.prev = None5        self.next = None6
7
8class DoublyLinkedList:9    def __init__(self):10        self.head = None11        self.tail = None12
13    def append(self, value):14        node = Node(value)15        if self.tail is None:16            self.head = self.tail = node17            return18        # Wire both directions: old tail <-> new node19        node.prev = self.tail20        self.tail.next = node21        self.tail = node22
23    def prepend(self, value):24        node = Node(value)25        if self.head is None:26            self.head = self.tail = node27            return28        node.next = self.head29        self.head.prev = node30        self.head = node31
32    def forward(self):33        values, current = [], self.head34        while current:35            values.append(current.value)36            current = current.next37        return values38
39    def backward(self):40        values, current = [], self.tail41        while current:42            values.append(current.value)43            current = current.prev44        return values45
46
47lst = DoublyLinkedList()48for value in [2, 3, 4]:49    lst.append(value)50lst.prepend(1)51
52print("Forward: ", lst.forward())53print("Backward:", lst.backward())
이 코드를 Python 플레이그라운드에서 실행하기

이중 연결 리스트 FAQ

단일 연결 리스트와 이중 연결 리스트의 차이는 무엇인가요?
단일 연결 리스트는 노드마다 포인터가 하나(next)라서 앞으로만 이동할 수 있습니다. 이중 연결 리스트는 prev 포인터를 추가하여 역방향 순회와, 참조를 가진 노드의 O(1) 삭제를 가능하게 합니다. 그 대가로 노드마다 포인터가 하나 더 늘고 변경 때마다 두 개의 링크를 갱신해야 합니다.
이중 연결 리스트가 추가 메모리를 감수할 만한 때는 언제인가요?
역방향 순회나, 이미 참조하는 임의 노드의 O(1) 삭제가 필요할 때입니다. 대표적인 예는 덱(양방향 큐)과 LRU 캐시로, 항목이 양쪽 끝에서 자주 옮겨지고 밀려납니다.
이중 연결 리스트가 노드를 O(1)에 삭제할 수 있는 이유는 무엇인가요?
노드를 제거하려면 그 앞 노드를 뒤 노드에 이어 붙여야 합니다. 단일 연결 리스트에서는 앞 노드를 찾으려고 머리부터 걸어가야 하지만(O(n)), 이중 연결 리스트에서는 노드의 prev 포인터가 앞 노드를 곧바로 알려주므로 다시 연결하는 작업이 O(1)입니다.
이중 연결 리스트와 배열 중 무엇을 사용해야 하나요?
인덱스로 O(1) 접근과 캐시 친화적인 반복이 필요하고 삽입이 주로 끝에서 일어난다면 배열을 사용하세요. 노드 참조를 가진 채 중간이나 양쪽 끝에 자주 삽입/삭제한다면 이중 연결 리스트를 사용하세요. 이는 배열의 O(n) 이동에 비해 O(1)이기 때문입니다. 배열은 메모리와 지역성에서 유리하고, 리스트는 구조적 편집에서 유리합니다.
이중 연결 리스트의 센티넬(sentinel) 노드란 무엇인가요?
센티넬(또는 더미) 노드는 리스트가 결코 진짜로 비지 않도록 머리 앞이나 꼬리 뒤에 두는 자리 표시자입니다. head, tail, 경계에서의 삽입/삭제에 대한 null 검사를 없애 모든 연산이 동일한 재연결 코드 경로를 따르게 합니다. 많은 실제 LRU 캐시 구현은 양 끝을 특수 처리하지 않으려고 센티넬을 두 개 사용합니다.
이중 연결 리스트에서 삭제할 때 가장 흔한 버그는 무엇인가요?
첫 번째나 마지막 노드를 삭제할 때 headtail 갱신을 잊어, 해제된 노드를 가리키는 댕글링 포인터가 남는 것입니다. 이웃을 다시 연결하기 전에 node.prevnull인지(head 갱신), node.nextnull인지(tail 갱신) 항상 확인하세요.
Coddy programming languages illustration

Coddy로 알고리즘을 마스터하세요

시작하기