본문 바로가기
프로그래밍/자료구조

파이썬으로 이중연결리스트를 만들어보자! (추가 연산, 탐색 연산)

by 인성패밀리 2023. 1. 29.
반응형

지난 시간에는 단순연결리스트를 만들어보았습니다.

 

이번에는 연결이 이중으로 된 이중연결리스트를 만들어보겠습니다.

 

이중연결리스트의 노드는 자신을 기준으로 다음에 있는 노드가 next 노드, 이전을 prev 노드라고 하겠습니다.

 

이를 그림으로 그려보면 다음과 같습니다.

head node와 tail node 부분을 보면 None이 있는데 head node의 이전이 없어서 None이며, tail node도 다음이 없어서 None으로 표시한 것입니다.

 

각 노드를 보면 data와 next, prev 총 3가지 속성이 있는 것을 알 수 있습니다. 그래서 Node class에는 3개의 인스턴스 변수가 들어가게 됩니다. 코드로 알아보면 다음과 같습니다.

class Node:
    def __init__(self, data):
        self.data = data
        self.next = None  # 다음 노드의 reference
        self.prev = None  # 이전 노드의 reference

 

이중연결리스트의 인스턴스 변수를 정하겠습니다. head 노드, tail 노드, 노드의 개수 총 3개로 정하겠습니다.

class Node:
    def __init__(self, data):
        self.data = data
        self.next = None  # 다음 노드의 reference
        self.prev = None  # 이전 노드의 reference


class DoublyLinkedList:
    def __init__(self):
        self.head = None
        self.tail = None
        self.count = 0

 

오늘 작성해볼 코드는 추가 연산과 탐색 연산입니다.

추가 연산은 연결리스트 맨 뒤에 계속 추가하는 것입니다. 그림으로 알아보면 다음과 같습니다.

연결리스트에 추가 할 때 두 가지의 경우를 알아볼 수 있습니다.

1. 연결리스트에 아무것도 없는 경우

2. 연결리스트에 한 개 이상의 노드가 있는 경우

 

1번의 경우 연결리스트에 아무것도 없음을 파악할 수 있어야 합니다. count 인스턴스 변수를 통해서 해결이 가능합니다.

나머지 2번의 경우는 일반적인 경우이므로 tail 뒤에 이어붙여줍니다.

class DoublyLinkedList:
    def __init__(self):
        self.head = None
        self.tail = None
        self.count = 0

    def append(self, val):
        """연결리스트에 추가하는 연산"""
        new_node = Node(val)  # 일딴 노드를 만들어야 하니까 새로 생성해준다.

        if self.count == 0:  # 0이라는 말은 연결리스트가 비어있다는 의미!
            self.head = new_node
            self.tail = new_node
        else:  # 그 외에는 일반적인 경우
            self.tail.next = new_node
            new_node.prev = self.tail
            self.tail = new_node
        self.count += 1  # 노드가 늘어났으니까 1개 더해준다.

연결리스트는 실제로 그림을 그리면서 공부하면 도움이 될 것입니다.

 

이번에는 탐색 연산입니다. 리스트의 인덱스 번호처럼 내가 원하는 부분을 이야기하면 그 부분에 대해서 알려주는 것입니다.

class DoublyLinkedList:
    def __init__(self):
        self.head = None
        self.tail = None
        self.count = 0

    def append(self, val):
        """연결리스트에 추가하는 연산"""
        new_node = Node(val)  # 일딴 노드를 만들어야 하니까 새로 생성해준다.

        if self.count == 0:  # 0이라는 말은 연결리스트가 비어있다는 의미!
            self.head = new_node
            self.tail = new_node
        else:  # 그 외에는 일반적인 경우
            self.tail.next = new_node
            new_node.prev = self.tail
            self.tail = new_node
        self.count += 1  # 노드가 늘어났으니까 1개 더해준다.

    def find(self, index):
        """연결리스트의 원하는 노드를 찾아준다."""
        i = self.head
        for _ in range(index):
            i = i.next
        return i.data

 

추가로 이중연결리스트를 한 번에 볼 수 있도록 __str__ 메소드도 만들어주겠습니다.

class DoublyLinkedList:
    def __init__(self):
        self.head = None
        self.tail = None
        self.count = 0

    def append(self, val):
        """연결리스트에 추가하는 연산"""
        new_node = Node(val)  # 일딴 노드를 만들어야 하니까 새로 생성해준다.

        if self.count == 0:  # 0이라는 말은 연결리스트가 비어있다는 의미!
            self.head = new_node
            self.tail = new_node
        else:  # 그 외에는 일반적인 경우
            self.tail.next = new_node
            new_node.prev = self.tail
            self.tail = new_node
        self.count += 1  # 노드가 늘어났으니까 1개 더해준다.

    def find(self, index):
        """연결리스트의 원하는 노드를 찾아준다."""
        i = self.head
        for _ in range(index):
            i = i.next
        return i.data

    def __str__(self):
        i = self.head
        result = str(i.data)
        for _ in range(self.count - 1):
            i = i.next
            result += " <-> " + str(i.data)
        return result

이렇게 간단히 추가 연산과 탐색 연산을 만들어보았습니다.

 

각각의 시간복잡도를 알아보면

추가 연산의 경우 tail뒤에 계속 추가하므로 O(1)이라 할 수 있고

탐색의 경우 맨 앞을 탐색할 경우 O(1), 일반적인 경우  O(n)의 시간이 걸린다고 할 수 있습니다.

 

끝으로 전체 코드입니다.

class Node:
    def __init__(self, data):
        self.data = data
        self.next = None  # 다음 노드의 reference
        self.prev = None  # 이전 노드의 reference


class DoublyLinkedList:
    def __init__(self):
        self.head = None
        self.tail = None
        self.count = 0

    def append(self, val):
        """연결리스트에 추가하는 연산"""
        new_node = Node(val)  # 일딴 노드를 만들어야 하니까 새로 생성해준다.

        if self.count == 0:  # 0이라는 말은 연결리스트가 비어있다는 의미!
            self.head = new_node
            self.tail = new_node
        else:  # 그 외에는 일반적인 경우
            self.tail.next = new_node
            new_node.prev = self.tail
            self.tail = new_node
        self.count += 1  # 노드가 늘어났으니까 1개 더해준다.

    def find(self, index):
        """연결리스트의 원하는 노드를 찾아준다."""
        i = self.head
        for _ in range(index):
            i = i.next
        return i.data

    def __str__(self):
        i = self.head
        result = str(i.data)
        for _ in range(self.count - 1):
            i = i.next
            result += " <-> " + str(i.data)
        return result


test = DoublyLinkedList()

test.append(1)
test.append(2)
test.append(3)
test.append(4)

print(test.find(3))

print(test)
반응형

댓글