Computer Science/Data Structure w. Python
연결 리스트 (Linked List) 4 - 원형 연결 리스트 (Circular Linked List)
sekimm
2024. 9. 18. 00:56
원형 연결 리스트(Circular Linked List)
연결 리스트에서는 첫 노드와 마지막 노드에 접근성이 극적으로 차이난다. 첫 노드에는 단번에 연결 가능하고, 두번째 노드에는 링크를 끝까지 따라가야 접근할 수 있다. 그 결과 원소를 삽입/삭제할 때, 맨 앞, 맨 끝에 따라서 차이가 난다. 따라서, 이 차이는 마지막 노드가 첫 번째 노드를 링크하도록 바꾸면 해소될 수 있고, 이런 구조의 연결 리스트를 원형 연결 리스트라 한다.
레퍼런스 __head 대신 마지막 노드에 대한 레퍼런스 __tail을 두고, 마지막 노드가 맨 앞의 더미 헤드 노드를 링크하게 만든다. 빈 원형 연결 리스트는 더미 헤드 노드가 자신을 링크한다. __tail을 마지막 노드를 가리키게 만들었을 때, 맨 뒤와맨 앞에 대한 접근성의 차이는 업서진다.
원형 연결 리스트 구현 및 개선점
원형 연결 리스트
리스트의 맨 앞을 레퍼런스 __head 대신 맨 끝을 레퍼런스 __tail을 가리키도록 함으로써, 리스트의 맨 앞과 맨 뒤에 접근성의 차이가 없어졌다.
가변 파라미터
기존에 pop, get 같은 함수는 인자가 없거나 -1인 경우는 처리하지 않았다. 하지만 원형 연결 리스트를 사용하면, 맨 끝 원소에 접근을 용이하게 해서 처리할 수 있다.
def pop(self, *args):
if self.isEmpty():
return None
if len(args) != 0: # 인자가 있으면 인가 값을 i에 할당
i = args[0]
if len(args) == 0 or i == -1: # 인자 값이 -1일 경우 맨 끝 원소의 인덱스 할당
i = self.__numItems -1
if (i>=0 and i <= self.__numItems-1):
prev = self.getNode(i-1)
retItem = prev.next.item
prev.next = prev.next.next
self.__numItems -= 1
return retItem
else:
return None
순회자(Iterator)
원형 연결 리스트를 사용하면 클래스를 순회 가능한 클래스로 만들어줄 수 있다. 순회 가능 클래스가 되려면 __iter__()와 __next__()가 준비되어 있어야 한다.
def count(self, x) -> int:
cnt = 0
for element in self:
if element == x:
cnt += 1
return cnt
def extend(self, a):
for element in a:
self.append(element)
def copy(self) 'CircularLinkedList' :
a = CircularLinkedList()
for element in self:
a.append(element)
return a
전체 코드
class ListNode:
def __init__(self, item, link):
self.item = item
self.next = link
class CircularLinkedListIterator:
def __init__(self, alist):
self.__head = alist.getNode(-1)
self.iterPosition = self.__head.next
def __next__(self):
if self.iterPosition == self.__head:
raise StopIteration
else:
item = self.iterPosition.item
self.iterPosition = self.iterPosition.next
return item
class CircularLinkedList:
def __init__(self):
self.__tail = ListNode("dummy", None)
self.__tail.next = self.__tail
self.__numItems = 0
def insert(self, i:int, newItem)-> None:
if(i>=0 and i <= self.__numItems):
prev = self.getNode(i-1)
newNode = ListNode(newItem, prev.next)
prev.next = newNode
if i == self.numItems:
self.__tail = newNode
self.__numItems += 1
else:
print("out of bound in insert")
def append(self, newItem) -> None:
newNode = ListNode(newItem, self.__tail.next)
self.__tail.next = newNode
self.__tail = newNode
self.__numItems += 1
def pop(self, *args):
if self.isEmpty():
return None
if len(args) != 0:
i = args[0]
if len(args) == 0 or i == -1:
i = self.__numItems-1
if(i>=0 and i<=self.__numItems-1):
prev = self.getNode(i-1)
retItem = prev.next.item
prev.next = prev.next.next
if i == self.__numItems-1:
self.__tail = prev
self.__numItems -= 1
return retItem
else:
return None
def remove(self, x):
(prev, curr) = self.__findNode(x)
if curr != None:
prev.next = curr.next
if curr == self.__tail:
self.__tail = prev
self.__numItems -= 1
return x
else:
return None
def get(self, *args):
if self.isEmpty():
return None
if len(args) != 0:
i = args[0]
if len(args) == 0 or i == -1:
i = self.__numItems - 1
if (i>= 0 and i<=self.__numItems-1):
return self.getNode(i).item
else:
return
def index(self, x) -> int:
cnt = 0
for element in self:
if element == x:
cnt+=1
return -12345
def isEmpty(self) -> bool:
return self.__numItems == 0
def size(self) -> int:
return self.__numItems
def clear(self):
self.__tail = ListNode("dummy", None)
self.__tail.next = self.__tail
self.__numItems = 0
def count(self, x):
cnt = 0
for element in self:
if element == x:
cnt+= 1
return cnt
def extend(self, a):
for x in a:
self.append(a)
def copy(self) -> b'CircularLinkedList':
a = CircularLinkedList()
for element in self:
a.append(element)
return a
def reverse(self) -> None:
__head = self.__tail.next # dummy head
prev = __head
curr = prev.next
next_node = curr.next
curr.next = __head
__head.next = self.__tail
self.__tail = curr
for _ in range(self.__numItems - 1):
prev = curr
curr = next_node
next_node = next_node.next
curr.next = prev
def sort(self) -> None:
a = []
for element in self:
a.append(element)
a.sort()
self.clear()
for element in a:
self.append(element)
def __findNode(self, x):
__head = prev = self.__tail.next
curr = prev.next
while curr != __head:
if curr.item == x:
return(prev, curr)
else:
prev = curr; curr = curr.next
return (None, None)
def getNode(self, i:int) -> ListNode:
curr = self.__tail.next
for index in range(i+1):
curr = curr.next
return curr
def printList(self) -> None:
for element in self:
print(element, end = ' ')
print()
def __iter__(self):
return CircularLinkedListIterator(self)