1. ADT 작성 

Datas:
    head : 첫 노드를 가리키는 포인터
    tail : 마지막 노드를 가리키는 포인터
    it : 이터레이션을 위한 포인터
    size : 리스트의 크기 = 리스트에 들어있는 노드의 갯수
    
Operations:
    insert(e, i) : 데이터 e를 리스트의 i번째 위치에 삽입. i == -1이라면 마지막에 삽입
    delete_at(i) : i 번째 데이터를 삭제하고 삭제한 데이터를 반환
    delete(e) : 데이터 e를 찾아 삭제하고 결과를 반환
    search_at(i) : i 번째 데이터를 반환. 유효하지 않은 인덱스라면 에러 발생
    search(e) : 데이터 e가 처음으로 나타나는 인덱스를 찾아 반환

 

2. 구현

# 양방향 연결리스트 클래스
class DoublyLinkedList:
    # 노드 클래스
    class __Node:
        # 노드 생성자
        def __init__(self, e):
            # 데이터
            self.data = e

            # 다음 노드
            self.next = None

            # 이전 노드
            self.prev = None

    # 리스트 생성자
    def __init__(self):
        # head
        self.__head = None

        # tail
        self.__tail = None

        # 현재 노드 (iterator 구현을 위한 필드)
        self.__it = self.__Node(0)

        # 리스트 크기
        self.__size = 0

    # 삽입 메소드
    def insert(self, e, i=-1):
        # 삽입할 노드 생성
        new_node = self.__Node(e)

        # 인덱스가 생략됐거나 마지막 인덱스인 경우 맨 끝에 삽입
        if i < 0 or i == self.__size:
            # 리스트가 비어있다면 head = tail = new_node
            if not self.__head:
                self.__head = self.__tail = new_node

            # 리스트가 비어있지 않다면 마지막 노드 뒤에 삽입
            else:
                self.__tail.next = new_node
                new_node.prev = self.__tail
                self.__tail = new_node

        # 마지막 노드가 아닌 인덱스가 주어졌을 경우
        else:
            # 유효하지 않은 인덱스라면 IndexError 발생
            if i > self.__size:
                raise IndexError

            # 인덱스가 0이라면 첫 노드의 앞 부분에 삽입
            if i == 0:
                self.__head.prev = new_node
                new_node.next = self.__head
                self.__head = new_node

            # 그렇지 않다면 해당 인덱스의 노드 앞 부분에 삽입
            else:
                cur = self.__head
                for _ in range(i):
                    cur = cur.next
                cur.prev.next = new_node
                new_node.prev = cur.prev
                cur.prev = new_node
                new_node.next = cur

        # 리스트의 크기 1 증가
        self.__size += 1

    # 탐색 메소드 - 인덱스
    def search_at(self, i):
        # 유효하지 않은 인덱스라면 IndexError 발생
        if i < 0 or i >= self.__size:
            raise IndexError

        # head 부터 i만큼 다음 노드로 이동
        cur = self.__head
        for _ in range(i):
            cur = cur.next

        # 노드의 데이터 반환
        return cur.data

    # 탐색 메소드 - 데이터
    def search(self, e):
        cur = self.__head
        idx = 0
        # 데이터 값이 e인 노드가 나올 때 까지 탐색
        while cur and cur.data != e:
            cur = cur.next
            idx += 1

        # 데이터 값이 e인 노드가 없을 경우 ValueError 발생
        if not cur:
            raise ValueError

        # 데이터 값이 e인 노드가 처음으로 나타난 인덱스 반환
        return idx

    # i 번째 노드 삭제 메소드
    def delete_at(self, i):
        # 유효하지 않은 인덱스라면 IndexError 발생
        if i < 0 or i >= self.__size:
            raise IndexError

        # head 부터 i 만큼 다음 노드로 이동
        cur = self.__head
        for _ in range(i):
            cur = cur.next

        # 반환할 노드의 데이터를 저장
        res = cur.data

        # 앞, 뒤 노드가 서로 연결되도록 조정
        # head, tail 을 삭제한 경우 head, tail 재지정
        if cur.prev:
            cur.prev.next = cur.next
        else:
            self.__head = cur.next

        if cur.next:
            cur.next.prev = cur.prev
        else:
            self.__tail = cur.prev

        # 리스트 크기 1 감소
        self.__size -= 1

        # 삭제한 데이터 반환
        return res

    # 처음으로 나타나는 데이터 e 삭제 메소드
    def delete(self, e):
        # 데이터 삭제 시도
        try:
            # search 메소드를 호출하여 데이터의 인덱스를 구함
            idx = self.search(e)

            # delete_at 메소드를 호출하여 데이터 삭제
            self.delete_at(idx)

            # 삭제성공. True 반환
            return True

        # 존재하지 않는 데이터라면 삭제에 실패. False 반환
        except ValueError:
            return False

    # len 함수의 결과로 sef.__size 반환
    def __len__(self):
        return self.__size

    # 이터레이터 구현
    def __iter__(self):
        # 이터레이션을 위한 self.__it 객체의 next 를 self.__head 로 하고 self 를 반환
        self.__it.next = self.__head
        return self

    def __next__(self):
        # 다음 노드가 있다면 self.__it = self.__it.next 후 self.__it 의 데이터값 반환
        if self.__it.next:
            self.__it = self.__it.next
            return self.__it.data
        # 없다면 이터레이션 종료
        else:
            raise StopIteration


# 테스트
def main():
    # 연결 리스트 생성
    dlist = DoublyLinkedList()
    
    # 데이터 삽입
    dlist.insert(3)
    dlist.insert(1)
    dlist.insert(2)
    dlist.insert(4)
    dlist.insert(0)
    dlist.insert(6)
    dlist.insert(7)
    dlist.insert(9)

    # 데이터 4 삭제
    dlist.delete(4)

    # 첫 번째 데이터(3) 삭제
    dlist.delete_at(0)

    # 3 번째 데이터(6) 삭제
    dlist.delete_at(3)

    # Python 의 list 로 변환하여 출력
    print(list(dlist))
  • ADT에서 언급한대로 리스트의 기능을 구현
  • __len__(self) 를 구현하여 len 함수를 호출하면 리스트의 크기를 반환하도록 함
  • __iter__(self) 와 __next__(self) 를 구현하여 이터레이터로서 동작하도록 구현.  for 문이나 list() 에 사용 가능.

 

단방향 연결 리스트나 원형 연결 리스트의 경우 양방향 연결 리스트의 코드를 조금 수정하는 것 만으로 구현할 수

있기 때문에 따로 구현하지 않는다.

'CS > 자료구조' 카테고리의 다른 글

#5 그래프(Graph)  (0) 2021.10.01
#4 큐(Queue)  (0) 2021.09.28
#3 스택 (Stack)  (0) 2021.09.28
#1 리스트(List)  (0) 2021.09.27
#0 ADT(Abstract Data Type)  (0) 2021.09.27

+ Recent posts