Data structure & Algorithms study

원형 연결 리스트 (Circular Linked List)

adulty22 2024. 6. 29. 20:40

원형 연결 리스트(Circular Linked List)는 연결 리스트의 한 종류로, 마지막 노드가 첫 번째 노드를 가리켜 리스트의 끝과 처음이 연결된 형태를 가지는 자료 구조이다. 원형 연결 리스트는 단일 연결 리스트(Singly Linked List)와 이중 연결 리스트(Doubly Linked List)로 나눌 수 있다. 원형 연결 리스트는 리스트의 처음과 끝이 연결되어 있어 순환 구조를 이루며, 특정 상황에서 메모리 사용과 알고리즘 구현에 유용하다.

구조

  1. 노드 (Node): 원형 연결 리스트의 기본 구성 요소로, 데이터와 포인터를 포함한다.
    • 데이터 (Data): 노드가 저장하는 값.
    • 다음 (Next): 다음 노드를 가리키는 포인터. 마지막 노드의 경우, 첫 번째 노드를 가리킨다.
  2. 헤드 (Head): 리스트의 첫 번째 노드를 가리키는 포인터.
  3. 테일 (Tail): (이중 연결 리스트의 경우) 리스트의 마지막 노드를 가리키는 포인터.

원형 단일 연결 리스트와 원형 이중 연결 리스트의 차이

  • 원형 단일 연결 리스트: 각 노드가 하나의 포인터(다음 노드)를 가지며, 마지막 노드의 포인터가 첫 번째 노드를 가리킨다.
  • 원형 이중 연결 리스트: 각 노드가 두 개의 포인터(다음 노드와 이전 노드)를 가지며, 마지막 노드의 다음 포인터가 첫 번째 노드를, 첫 번째 노드의 이전 포인터가 마지막 노드를 가리킨다.

주요 연산

  1. 삽입 (Insertion):
    • 앞쪽 삽입 (Insert at the Beginning): 새 노드를 헤드 앞에 추가하고, 헤드를 새 노드로 갱신한다.
    • 끝쪽 삽입 (Insert at the End): 새 노드를 리스트의 끝에 추가하고, 마지막 노드의 포인터를 업데이트한다.
    • 중간 삽입 (Insert in the Middle): 특정 위치에 새 노드를 삽입한다.
  2. 삭제 (Deletion):
    • 앞쪽 삭제 (Delete from the Beginning): 첫 번째 노드를 삭제하고, 헤드를 다음 노드로 갱신한다.
    • 끝쪽 삭제 (Delete from the End): 마지막 노드를 삭제하고, 새로운 마지막 노드를 갱신한다.
    • 중간 삭제 (Delete in the Middle): 특정 위치의 노드를 삭제한다.
  3. 탐색 (Search): 리스트를 순회하면서 특정 값을 가진 노드를 찾는다.
  4. 길이 반환 (Get Length): 리스트에 포함된 노드의 개수를 반환한다.
  5. 출력 (Display): 리스트의 모든 노드를 순서대로 출력한다.

장단점

장점:

  1. 항상 연결: 마지막 노드가 첫 번째 노드를 가리켜 리스트가 항상 연결된 상태를 유지한다.
  2. 순환 구조: 순환 구조를 이용해 리스트를 여러 번 순회할 때 유용하다.
  3. 메모리 효율: 헤드 포인터와 테일 포인터만으로 리스트의 처음과 끝을 관리할 수 있다.

단점:

  1. 복잡한 구현: 리스트의 끝과 처음이 연결되어 있어 삽입과 삭제 연산 시 포인터 조작이 복잡하다.
  2. 순회 중단 어려움: 리스트가 순환 구조를 가지므로 무한 루프에 빠질 가능성이 있다.

활용 예

  1. 라운드 로빈 스케줄링: 프로세스 스케줄링에서 원형 연결 리스트를 사용하여 각 프로세스에 공평하게 CPU 시간을 할당한다.
  2. 메모리 관리: 원형 연결 리스트는 메모리 블록을 관리하는 데 사용될 수 있다.
  3. 게임 구현: 턴 기반 게임에서 플레이어의 순서를 관리하는 데 유용하다.
class Node:
    def __init__(self, data) -> None:
        self.data = data
        self.next = None
        self.prev = None

class CircularLinkedList:
    def __init__(self) -> None:
        self.head = None
        self.tail = None

    def insertBeginning(self, data) -> None:
        new_node = Node(data)

        if self.head is None:
            self.head = new_node
            self.tail = new_node
            self.head.next = self.head
            self.head.prev = self.head
        else:
            new_node.next = self.head
            new_node.prev = self.tail
            self.tail.next = new_node
            self.head.prev = new_node

    def insertEnd(self, data) -> None:
        new_node = Node(data)

        if self.head is None:
            self.head = new_node
            self.tail = new_node
            new_node.next = new_node
            new_node.prev = new_node
        else:
            new_node.next = self.head
            new_node.prev = self.tail
            self.tail.next = new_node
            self.head.prev = new_node
            self.tail = new_node

    def insertMiddle(self, data, index: int) -> None:
        if index < 0:
            print("Invalid position")
            return

        new_node = Node(data)

        if index == 0:
            self.insertBeginning(data)

        cur = self.head

        for _ in range(index - 1):
            if cur.next == self.head:
                print("Index out of bounds")
                return
            cur = cur.next

        new_node.next = cur.next
        new_node.prev = cur
        cur.next.prev = new_node
        cur.next = new_node

    def deleteNode(self, key) -> None:
        if self.head is None:
            return

        cur = self.head
        while True:
            if cur.data == key:
                if cur == self.head:
                    self.head = cur.next
                    self.tail.next = self.head
                    self.head.prev = self.tail
                elif cur == self.tail:
                    self.tail = cur.prev
                    self.tail.next = self.head
                    self.head.prev = self.tail
                else:
                    cur.prev.next = cur.next
                    cur.next.prev = cur.prev
                return

            cur = cur.next
            if cur == self.head:
                break

        print("Key not found")

    def search(self, key) -> bool:
        if self.head is None:
            return False

        cur = self.head
        while True:
            if cur.data == key:
                return True

            elif cur.next == self.head:
                break

            cur = cur.next
        return False

    def getLength(self) -> int:
        if self.head is None:
            return 0

        count = 1
        cur = self.head
        while cur.next is not self.head:
            count += 1
            cur = cur.next

        return count

    def display(self) -> None:
        if self.head is None:
            print("List is empty")
            return

        cur = self.head
        while True:
            print(cur.data, end=" <-> ")
            cur = cur.next
            if cur == self.head:
                break

        print("Head")

cdll = CircularLinkedList()
cdll.insertEnd(10)
cdll.insertEnd(20)
cdll.insertBeginning(5)
cdll.insertMiddle(15, 2)  # Insert 15 at position 2
cdll.display()  # Output: 5 <-> 10 <-> 15 <-> 20 <-> Head
print(cdll.search(10))  # Output: True
print(cdll.getLength())  # Output: 4
cdll.deleteNode(10)
cdll.display()  # Output: 5 <-> 15 <-> 20 <-> Head

 

모든 코드는 github에 저장되어 있습니다.

https://github.com/SeongUk18/Algorithm/tree/main/Data_structure_algorithms

728x90