All Articles

Linked List

Linked List(연결리스트)가 뭐야?

노드를 저장할 때 그 다음 순서의 자료가 있는 위치를 데이터에 포함시키는 방식으로 자료를 저장하는 구조이다.

링크드리스트 vs 배열

둘은 얼추 비슷한 느낌이지만 분명 다르다.
추가/삭제가 많다면 링크드리스트, 탐색/정렬이 많다면 배열을 사용하는 게 유리하다.

둘 다 선형 데이터 구조지만, 배열은 물리적으로도 연속된 메모리를 사용하고 링크드리스트는 다음 노드의 위치를 저장함으로써 흩어진 메모리를 연결해서 쓴다는 게 둘 사이의 차이점을 만드는 주요한 요인이다.

  • 링크드리스트

    • 장점: 데이터의 추가/삽입 및 삭제가 용이하다. 즉, 새로운 노드를 끼워넣기 쉽다. 길이를 동적으로 조절 가능
    • 단점: 인덱스없이 연결관계만 있기 때문에 특정 노드를 불러내기 어렵다. (일반적으로) O(N)이 걸리며 순차적으로 탐색하게 되기 때문이다.(B+tree자료구조 등은 예외) 거꾸로 탐색하기도 어렵다. 정렬은 O(NlogN)이 걸린다.
  • 배열

    • 장점: 자료마다 인덱스가 있어서 특정 자료를 불러내기 편하다.
    • 단점: 연속된 메모리 공간을 할당받아야 하다보니 크기를 크게 키우기가 어렵다. 또한, 안 쓰는 공간까지 전부 예약해두고 있어야 하므로 공간 낭비가 생긴다. 데이터 추가/삽입 과정에서 데이터를 옮기는 연산작업이 생긴다.

링크드리스트의 종류

  • 단순 연결 리스트 (Singly Linked List)
  • 이중 연결 리스트 (Doubly Linked List)
  • 원형 연결 리스트 (Circular linked list)
  • 청크 리스트 (Chunked List)

Python으로 단순연결리스트 구현하기

구현방식

노드를 아래와 같이 구현한다.

class Node:
  def __init__(self, data):
    self.data = data
    self.next = None

그리고 다음 노드는 아래와 같은 방식으로 참조시킨다.

head = Node(5)
next_node = Node(12)
head.next = next_node

단순연결 리스트의 클래스는 이렇게 만들었다. 첫 번째 노드 삽입, 중간 노드 삽입, 마지막 노드 삽입, 노드 삭제 메서드를 구현할 때, 모두 이 클래스 안에 작성했다.

class SingleLinkedList:
  def __init__(self, data):
    new_node = Node(data)
    self.head = new_node
    self.list_size = 1

첫번째 노드 삽입

def insertFirst(self, data):
  new_node = Node(data)       # 새로운 노드 생성
  temp_node = self.head       # 기존 헤드를 잠시 보관
  self.head = new_node        # 헤드를 새로운 노드로 변경
  self.head.next = temp_node  # 새로 생성된 헤드의 링크를 기존 헤드의 링크로 변경
  self.list_size += 1

노드 선택

def selectNode(self, num):
  if self.list_size < num:
    return # 오버플로우
  node = self.head
  count = 0
  while count < num:
    node = node.next
    count += 1
  return node

중간 노드 삽입

def insertMiddle(self, num, data):
  if self.head.next == None:
    # 헤더가 만들어진 직후에 메서드를 사용하는 경우
    self.insertLast(data)
    return
  node = self.selectNode(num)
  new_node = Node(data)
  temp_next = node.next
  node.next = new_node
  new_node.next = temp_next
  self.list_size += 1

마지막 노드 삽입

def insertLast(self, data):
  node = self.head
  while True:
    if node.next == None: # 다음 링크가 없으면
      break
    node = node.next
  
  new_node = Node(data)
  node.next = new_node      # 마지막 노드로 링크
  self.list_size += 1

노드 삭제

def deleteNode(self, num):
  if self.list_size < 1:
    return # 언더플로우
  elif self.list_size < num:
    return # 오버플로우

  if num == 0:
    self.deleteHead()
    return
  node = self.selectNode(num - 1) # 이전 노드의 링크를 다다음 노드와 연결하기 위해
                                  # 이전 노드를 선택하였다
  node.next = node.next.next
  del_node = node.next
  del del_node

헤드 노드 삭제

def deleteHead(self):
  node = self.head
  self.head = node.next
  del node

출력해보기

if __name__ == "__main__":
  m_list = SingleLinkedList(1)
  m_list.insertLast(5)
  m_list.insertLast(6)
  print('LinkedList :', m_list)
  print('LinkedList Size() :', m_list.size())
  print('LinkedList SelectNode(1) :', m_list.selectNode(1))

  m_list.insertMiddle(1, 15)
  print('LinkedList Insert Middle(1, 15) :', m_list)
  
  m_list.insertFirst(100)
  print('LinkedList Insert First(100) : ', m_list)
  print('LinkedList SelectNode(0) :', m_list.selectNode(0))

  m_list.deleteNode(0)
  print('LinkedList Delete Node(0) : ', m_list)
  m_list.deleteNode(1)
  print('LinkedList Delete Node(1) : ', m_list)

출력 결과

LinkedList : [ 1, 5, 6 ]
LinkedList Size() : 3
LinkedList SelectNode(1) : 5
LinkedList Insert Middle(1, 15) : [ 1, 5, 15, 6 ]
LinkedList Insert First(100) :  [ 100, 1, 5, 15, 6 ]
LinkedList SelectNode(0) : 100
LinkedList Delete Node(0) :  [ 1, 5, 15, 6 ]
LinkedList Delete Node(1) :  [ 1, 15, 6 ]

[참고자료]
https://lsjsj92.tistory.com/461?category=828186
https://baejino.com/programing/python/datastructure-1-linked-list