읽기 전
- 불필요한 코드나 잘못 작성된 내용에 대한 지적은 언제나 환영합니다.
- 개인적으로 사용해보면서 배운 점을 정리한 글입니다.
정의
어떤 데이터 덩어리(Node)를 저장할 때 그 다음 좌표가 있는 위치를 데이터에 포함시키는 방식으로 자료를 저장하는 구조이다.

배열과 비교
장점 - 새로운 노드를 뒤에 연결하거나 중간에 노드를 끼워넣기가 쉽다. 즉, 크기를 키우거나 줄이기가 쉽다.
단점 - 자료번호로 데이터를 구분하는 배열과 달리 연결관계만 가지고 있기 때문에 특정 노드를 불러오기가 힘들다.
결론 - 탐색/정렬은 배열이, 추가/ 삭제는 연결리스트가 유리하다. 물론 순차 탐색만 하지 않고 효율적인 탐색을 한다면 단점이 없어진다. 그에 관한 내용은 추가적으로 공부하면 작성할 예정.
Python의 리스트가 너무 사기적으로 슬라이싱, 동적 할당을 지원하기에 실제 문제에서 적용하기 조금 애매한 경우도 있다.
시간복잡도

유형
단일 연결리스트(Singly Linked List)
다음 노드에 대한 참조값만을 갖는 가장 단순한 형태의 연결리스트다. 큐를 구현할 때 사용하는 방식이다. 단방향이기 때문에 역방향 탐색이 어려운 단점이 있다. 알고리즘 문제에서 역방향 탐색을 요구하지 않는 문제가 나오면 단일 연결리스트를 구현해서 풀기도 한다.
이중 연결리스트(Doubly Linked List)
단일 연결리스트가 다음 노드에 대한 참조값만을 가졌다면 이중 연결리스트는 이전 노드에 대한 참조값도 갖는다.
환(원)형 연결리스트(Circular Linked List)
단일 연결리스트에서 마지막 노드의 참조값은 Null이지만 이를 첫 번째 노드를 가리키게 하면 환형 연결리스트가 된다. 이중 연결리스트의 경우에도 처음 노드의 이전 노드 참조값을 마지막 노드로, 마지막 노드의 다음 노드 참조값을 첫 번째 노드로 가리키게 하면 된다.
단일 연결리스트(Singly Linked List) 구현
Node 정의
| class ListNode(object): |
| def __init__(self, val, next=None): |
| self.val = val |
| self.next = next |
Singly Linked List 정의
| class SinglyLinkedList(object): |
| def __init__(self): |
| self.head = ListNode(None) |
| self.size = 0 |
Singly Linked List 사이즈 반환
| def size(self): |
| return self.size |
Singly Linked List 원소 조회
| def get_node(self, idx): |
| if self.size == 0: |
| print("ERROR: Already Empty Linked List") |
| return |
| elif idx >= self.size: |
| print("ERROR: Out of Range") |
| return |
| else: |
| target = self.head |
| for _ in range(idx): |
| target = target.next |
| return target |
Singly Linked List 원소 우측 삽입
| def append(self, val): |
| if self.size == 0: |
| self.head = ListNode(val) |
| else: |
| target = self.get_node(self.size - 1) |
| target.next = ListNode(val) |
| self.size += 1 |
Singly Linked List 원소 좌측 삽입
| def appendleft(self, val): |
| if self.size == 0: |
| self.head = ListNode(val) |
| else: |
| self.head = ListNode(val, self.head) |
| self.size += 1 |
Singly Linked List 좌표 기반 원소 삽입
| def insert(self, val, idx): |
| if self.size == 0: |
| print("ERROR: Already Empty Linked List") |
| elif idx >= self.size: |
| print("ERROR: Out of Range") |
| else: |
| target = self.get_node(idx - 1) |
| tmp = target.next |
| target.next = ListNode(val, tmp) |
| self.size += 1 |
Singly Linked List 원소 우측 삭제
| def pop(self): |
| if self.size == 0: |
| print("ERROR: Already Empty Linked List") |
| else: |
| target = self.get_node(idx - 2) |
| out = target.next |
| target.next = None |
| self.size -= 1 |
| return out |
Singly Linked List 좌표 기반 원소 삭제
| def delete(self, idx): |
| if self.size == 0: |
| print("ERROR: Already Empty Linked List") |
| elif idx >= self.size: |
| print("ERROR: Out of Range") |
| else: |
| target = self.get_node(idx - 1) |
| target.next = target.next.next |
Singly Linked List 원소 출력
| def print(self): |
| target = self.head |
| while target: |
| if target.next != None: |
| print(f'{target.val} -> ', end='') |
| else: |
| print(target.val) |
| target = target.next |
실습 결과
| linked_list = SingleLinkedList() |
| linked_list.append(1) |
| linked_list.print() |
| linked_list.append(2) |
| linked_list.print() |
| linked_list.appendleft(0) |
| linked_list.print() |
| linked_list.append(4) |
| linked_list.insert(3, 3) |
| linked_list.print() |
| linked_list.delete(0) |
| linked_list.print() |
| a = linked_list.pop() |
| print(f'a value is {a.val}') |
| linked_list.print() |
| b = linked_list.popleft() |
| print(f'b value is {b.val}') |
| linked_list.print() |
