a. 연결리스트 기본 개념
연결리스트는 노드가 링크에 의해 기차처럼 연결된 순차 자료구조.
노드는 실제 값을 가지고 있는 data 정보와 인접 노드로 향하는 link 정보로 구성된 클래스로 구현한다.
다른 값에 접근하려면 링크에 따라 원하는 노드의 데이터에 접근한다.
다만, 배열처럼 index로 접근할 수 없다.
어떤 값을 찾으려면 처음부터 순차적으로 찾아야한다.
배열과 연결리스트의 차이를 그림으로 파악해보자!
b. 한방향 연결리스트
노드들이 한 방향으로 연결된 리스트
- 가장 앞에 있는 노드를 head 노드라고 부르자.
- 가장 마지막 노드는 다음 노드가 없으므로 마지막 노드의 next 링크는 None이다.
b-1. 노드 클래스 구현하기
1 | class Node: |
b-2. 한방향 연결 리스트 클래스 구현하기
1 | class SinglyLinkedList: |
c. 지원하는 연산 (삽입, 삭제, 탐색 등…)
pushFront(key) | key값을 갖는 새 노드를 만들어 head 앞에 삽입 |
pushBack(key) | key값을 갖는 새 노드를 마지막 노드 뒤에 삽입 |
popFront() | 첫 노드(head)를 삭제 후 key값을 리턴 |
popBack() | 마지막 노드를 삭제 후 key값을 리턴 |
search(key) | key값을 갖는 노드를 찾아 리턴 |
remove(v) | 노드 v를 제거 |
c-1. pushFront vs pushBack
pushFront는 현재 head 앞에 새 노드를 생성해 앞에 삽입한다.
1 | def pushFront(self, key, value=None): |
pushBack은 마지막 노드의 next 링크가 새로운 노드로 연결되도록 해야한다. 만약 마지막 노드가 없다면(즉 리스트가 비었다면) 새 노드가 리스트의 head가 되어야 한다.
1 | def pushBack(self, key, value=None): |
c-2. popFront vs. popBack
popFront는 빈 리스트라서 head를 지울 수 없는 경우와 그렇지 않은 경우로 나눠 구현한다.
1 | def popFront(self): |
popBack은 마지막 노드를 찾고 지우고 그 전 노드의 링크를 수정해야 하므로, 마지막 전 노드를 알아야 한다.
(다시 말하면 마지막 노드는 next링크가 None인 노드이다.
마지막 전 노드를 마지막 노드로 하고 싶으면 해당 노드의 next링크를 None으로 해줘야 된다는 의미다.)
연결리스트는 인덱스로 접근이 안되니, 일일히 처음부터 마지막 전 노드까지 찾아가야 한다.(O(n))
총 세가지로 나눠 구현해야 한다.
- 빈 리스인 경우
- 리스트에 노드가 하나만 있는 경우
- 리스트에 두 개 이상의 노드가 있는 경우
1 | def popBack(self): |
c-3. search(key)
key 값을 저장한 노드를 찾아 리턴하거나 없으면 None을 반환하는데, 두가지 방법이 있다.
head부터 next 링크를 따라가면서 찾기
1
2
3
4
5
6
7def search(self, key):
v = self.head
while v != None:
if v.key == key:
return v
v = v.next
return vfor 루프를 이용하는 방법(__iter__(self)에 의해 가능)
1
2
3
4
5def search(self, key):
for v in self:
if v.key == key:
return v
return None
c-4. remove(v)
노드 v를 리스트에서 제거하는 함수. 세가지 경우를 고려해 구현한다.
- 리스트가 비어있거나, 노드 v가 None인 경우 -> do nothing
- 노드 v가 head인 경우 -> popFront 호출
- 그 외의 경우 -> v의 전 노드 w를 찾은 후 w.next = v.next로 w의 링크 수정
1 | def remove(self, x): |