일관적인 로직을 위해서 아무 값도 없는 head, tail Node를 사용하여 구현.
class Node:
def __init__(self, val):
self.val = val
self.prev = None
self.next = None
class DoublyLinkedList:
def __init__(self):
self.head = Node(None) # 더미
self.tail = Node(None) # 더미
self.head.next = self.tail
self.tail.prev = self.head
self.size = 0
def _get(self, idx):
# pop()/append() 최적화. 가까운 쪽에서 접근.
if idx <= self.size // 2:
cur = self.head.next
for _ in range(idx):
cur = cur.next
else:
# idx == size면 tail 자체 반환 (insert_at 맨 끝 삽입용)
cur = self.tail
for _ in range(self.size - idx):
cur = cur.prev
return cur
def insert_at(self, idx, val):
# idx 범위: 0 ~ size (size 허용 = 맨 끝 삽입. list.insert 표준 관례)
ref = self._get(idx)
new = Node(val)
prev = ref.prev
prev.next = new
new.prev = prev
new.next = ref
ref.prev = new
self.size += 1
def delete_at(self, idx):
# idx 범위: 0 ~ size-1 (존재하는 노드만 삭제 가능)
cur = self._get(idx)
cur.prev.next = cur.next
cur.next.prev = cur.prev
cur.prev = None
cur.next = None
self.size -= 1
def __len__(self):
return self.size
# ──────────────────────────────────
# 위 4개 조합으로 파생 가능
# ──────────────────────────────────
# append(val) → insert_at(size, val)
# appendleft(val) → insert_at(0, val)
# pop() → delete_at(size - 1) ← _get 최적화로 O(1)
# popleft() → delete_at(0) ← O(1)
# get(idx) → _get(idx).val
1.
_get()최적화 불필요 시 단순 버전2.
insert_at(size, val)와append(val)는 동일하다.insert_at은 존재하지 않는 맨 끝 인덱스(size)에 삽입을 허용한다.Python
list.insert, JavaList.add(index, elem), C++std::list::insert모두 동일한 관례.따라서
insert_at(size, val)와append(val)는 동일한 결과를 보여준다.delete_at은 존재하는 노드만 대상이므로0 ~ size-1만 유효.3. 위 템플릿의 한계
IndexError등)__iter__,__repr__등 편의 메서드 미구현.