nathan_H
연결리스트의 특징과 파이썬 코드 구현 본문
Linked List
연결리스트는 대표적은 추상적 자료구조의 한 예로 기존 리스트 자료구조와 달리 Node와 Link를 가진 자료구조이다.
위에 그림처럼 데이터를 담고 있는 Node 그리고 각 노드들은 연결하는 Link를 통해 이루어져 있다.
연결 리스트의 장단점
연결리스트의 경우 링크를 통해 각 데이터가 연결이 되어 있기 때문에 배열에 비해 추가 / 삭제가 용이한 점을 가지고 있다.
반면에 특정 데이터를 찾기 위한 순차 탐색에 있어 탐색 속도가 배열에 비해 느린 단점을 가지고 있다.
하지만 연결 리스트는 '동적 할당'을 기반으로 한 자료구조이기 때문에 크기를 미리 지정할 필요가 없다는 큰 장점을 가지고 있기 때문에 많이 사용되어 진다.
그래서 궁극적으로 내가 어떠한 목적을 가지고 코드를 구성하느냐에 따라 배열과 연결리스트의 장단점을 고려해서 사용해야 한다.
연결 리스트 구현
노드 객체
class Node: def __init__(self, item): self.data = item self.next = None
앞서 말했듯이 노드는 데이터와 노드를 연결하는 링크로 구성되어 있기 때문에 객체 생성시 self.data, self.next로 구성한다.
연결리스트 객체
class LinkedList: ''' Node class을 통해 연결하고 구성된다. ''' def __init__(self): self.nodeCount = 0 self.head = None self.tail = None
그리고 연결리스트의 객체룰 생성하는 경우에는 총 노드의 개수 = 0, 맨 앞의 노드, 맨 뒤의 노드에 대해 None 값을 생성해 객체를 만들어 준다.
연결리스트 메소드
이제 노드와 연결 리스트의 객체를 생성했으니 연결리스트가 가지는 메소드들에 대해 구성을 할 차례이다
- traversal() - list traversal
- getAt(pos) - certain element reference
- getLenth() - get lenth
- insert(pos, newNode) - insert element
- popAt(pos) - delete element
- merge(L) - merge list to list
위와 같이 연결리스트의 메소드는 총 6가지로 이루어져 있다.
특정 위치 노드 찾기.
getAt method의 경우 특정 위치를 인자로 받아 그 노드를 찾아 반환하는 메소드이며 구현한 코드는 다음과 같다.
def getAt(self, pos): if pos < 1 or pos > self.nodeCount: return None curr = self.head i = 1 while i < pos: curr = curr.next i += 1 return curr
노드 삽입
연결리스트의 경우 배열처럼 데이터를 삽입 하는 메소드를 구현할 수 있는데 기존 배열과는 달리 연결리스트의 경우 링크를 연결해 노드를 삽입하기 때문에 알고리즘 설계는 다음과 같다.
- newNode 를 삽입
- 성공/실패에 따라 true/false 를 리턴.
주의사항
-
삽입하려는 위치가 리스트 맨앞일때 -> prev 없음, head 조정 필요
-
삽입하려는 위치카 리스트 맨 끝일때 -> prev = tail
-
빈리스트의 삽입할 경우. → 1) 조건을 잘 활용하여 tail 조정.
def insert(self, pos, newNode): if pos < 1 and pos > self.nodeCount + 1: return False if pos == 1: newNode.next = self.head.next self.head = newNode else: if pos == self.nodeCount + 1: prev = self.tail else: prev = self.getAt(pos - 1) newNode.next = prev.next prev.next = newNode if pos == self.nodeCount + 1: newNode = self.tail self.nodeCount += 1 return True
노드 삭제.
노드 삭제의 경우도 앞서 한 노드의 삽입의 경우와 마찬가지로 설계시 몇가지 고려할 사항이 있다.
- node를 삭제
- node의 데이터를 리턴.
주의사항.
-
삭제하려는 node가 맨앞의 것일때 -> prev 없음, head 조정
-
리스트 맨 끝의 node를 삭제할 때 -> tail 조정 필요.
-
유일한 노드를 삭제할때 -> 위 조건 만으로 만족 하는가?
def popAt(self, pos): data = 0 if pos < 1 and pos > self.nodeCount: raise IndexError('index out of range') if pos == 1: data = self.head.data self.head = self.head.next else: prev = self.getAt(pos - 1) curr = prev.next data = curr.data if pos == self.nodeCount: prev.next = None self.tail = prev else: prev.next = curr.next if pos == self.nodeCount: self.head = None self.tail = None self.nodeCount -= 1 return data
노드 순회 및 길이, 노드 병합 메소드
def traversal(self): result = [] curr = self.head while curr: result += [curr.data] curr = curr.next return result def getLenth(self): return self.nodeCount def concat(self, L): self.tail.next = L.head if L.tail: self.tail = L.tail self.nodeCount += L.nodeCount
Dummy Linked List
Dummy Linked List는 기존 리스트와 달리 앞에 빈 Dummy 노드를 추가해 구현한 연결리스트 이다.
연결리스느의 가장 큰 장점은 앞서 말했듯이 원소의 삽입/ 삭제가 용이하다는 점이다.
리스트를 따라서 하나 하나 원소들을 대상으로 어떤 작업을 하다가, 그 위치에 새로운 데이터 원소를 삽입하거나, 아니면 그 위치에 있는 데이터 원소를 삭제하는 경우입니다. 앞선 연결리스트에서는 프로그래밍 연습을 쉽게 하기 위해서 특정 번째 를 지정하여 원소를 삽입/삭제하는 연산을 정의하고 코딩으로 구현하였는데, 이번에는 Dummy Linked List를 통해 특정 원소의 바로 다음 을 지정하여 원소를 삽입/삭제하는 연산을 정의하고 구현하면 보다 나은 코드가 될 것이다.
그러나 맨 앞에 원소를 추가 (삽입) 하거나 맨 앞의 원소를 제거 (삭제) 하는 연산을 지정하기가 좀 애매한데 이런 경우에도 동일한 방법을 적용하기 위해서, 이번에는 연결 리스트의 맨 앞에다가 데이터 원소를 담고 있지 않은, 그냥 자리만 차지하는 노드를 추가한, 더비 연결 리스트를 정의한 것이다.
코드 구현
class Node: def __init__(self, item): self.data = item self.next = None class DummyLinkedList: def __init__(self): self.head = Node(None) self.tail = None self.head.next = self.tail self.nodeCount = 0 def getAt(self, pos): if pos < 1 and pos > self.nodeCount: return None curr = self.head i = 0 while i < pos: curr = curr.next i += 1 return curr def getLenth(self): return self.nodeCount def traversal(self): result = [] curr = self.head while curr.next: result += [curr.data] curr = curr.next return result def concat(self, L): self.tail.next = L.head if L.tail: L.tail = self.tail self.nodeCount += L.nodeCount def insertAfter(self, prev, newNode): newNode.next = prev.next if prev.next is None: self.tail = newNode prev.next = newNode self.nodeCount += 1 return True def insertAt(self, pos, newNode): if pos < 1 and pos > self.nodeCount + 1: return False if pos != 1 and pos == self.nodeCount + 1: prev = self.tail else: prev = self.getAt(pos - 1) return self.insertAfter(prev, newNode) def popAfter(self, prev): curr = prev.next if self.nodeCount == 1: self.head.next = None self.tail = None else: if curr is None: prev.next = None self.tail = prev else: prev.next = curr.next self.nodeCount -= 1 return curr.data def popAt(self, pos): if pos < 1 and self.nodeCount < pos: raise IndexError('out of index') prev = self.getAt(pos - 1) return self.popAfter(prev)
'Computer Science > DataStructure' 카테고리의 다른 글
[자료구조] Queue 의 특징 및 활용예제 (0) | 2019.07.09 |
---|---|
[Stack] python으로 구현한 자료구조 stack (1) | 2019.07.06 |
양방향 연결리스트(Double LinkedList) (0) | 2019.07.04 |