스킵리스트란

  • Logt시간에 검색, 추가, 제거를 수행할 수 있는 정렬된 자료구조
  • 이진검색트리 대체 가능
  • 스킵 포인터를 이용해 빠르게 탐색

스킵리스트

스킵리스트 아이디어

Skip List는 LinkedLis에서 조금 더 빠르게 데이터를 검색할 수 있기 위해 고안한 자료구조이다.

한 칸씩 skip

enter image description here

  • 빠르게 검색하기 위해, 한 칸씩 skip하며 검색을 한다면?
    • 메모리 : 1/2N 만큼 더 사용
    • 검색 : 최대 1/2 + 1 노드 검사

한 번 더 skip

enter image description here

  • 한 칸이 아니라, 4칸에 한 번씩(한번 더 skip)을 진행하게 된다면?
    • 메모리 : (1/2 + 1/4) N = 3/4N 만큼 더 사용
    • 검색 : 최대 N/4 + 3 노드를 검사

계속 적용한다면..?

enter image description here

  • 2^i번째 노드에 2^i개의 포인터를 가지도록 만든다면?
    • 메모리 : N만큼 더 사용
    • 속도 : O(logN) 개의 노드를 검사
  • 이는 이진검색트리와 동일하다.

동적 데이터 구조로서의 한계

  • 중간에 노드 추가/삭제시 패턴이 무너지게되 처음부터 포인터를 다시 만들어야함
  • 최대 O(N)의 시간이 소요될 수 있음

해결 방법 :

enter image description here

  • 포인터 크기의 확률분포를 유지
  • 규칙적인 패턴을 유지할 필요가 없어지기에 앞선 문제를 해결할 수 있다.

연결리스트와의 차이점

  • 연결리스트는 하나의 next 포인터를 가진다.
  • skiplist는 다수의 next포인터를 가지며, 이를 Forward 포인터라고 한다.
  • 최상의 경우 O(logn)의 효율을 가지지만, 최악의 경우 O(n)의 복잡도를 가진다.

노드 검색

enter image description here

  • 가장 상위 리스트부터 검색을 시작한다.
  • 현재 위치의 다음 element가 찾으려는 값보다 크면 아래로 내려가고, 아니면 포인터가 가르키는 다음 노드로 이동한다.

노드 삽입

  • 노드 삽입시 제일 중요한 건, 노드의 크기(포인터를 몇개 둘 것인지)이다.
  • 노드의 크기는 확률적으로 정한다.
  • 예를 들어 15를 insert한다고 가정해보자.
    • 15보다 작은 수까지 검색한다.
    • 검색이 종료된 위치를 Predecessor Key라하며, 다음 위치에 15를 합입한다.
    • 삽입할 때 확률로 노드의 크기를 정해 삽입한다. enter image description here

노드 삭제

  • 가장 상위의 포인터부터 순차적으로 제거해준다. enter image description here

    스킵리스트은 언제 자주 쓰일까?

  • 멀테쓰레드 환경에서 자주 수정할 컨테이너가 필요할 때
  • 순차적으로 탐색할 필요가 많은 경우
  • 정렬이 필요 없으면 HashTable을 쓰는 것이 더 효율적이다.
  • Redit, ConcurrentSkipListMap/Set에서 이 원리를 기반으로 적용하였다.

정리

  • 스킵리스트는 결국 정렬을 유지하며 빠르게 삽입/삭제/검색을 할 수 있ㄷ록 지원하는 자료구조이다.
  • tree와 비슷하게 리밸런싱시 락이 발생할 수 있지만, 트리에 비해서는 확률이 훨씬 적다.

참고자료 : Skip List 스킵리스트(Skip List)


oksusutea's blog

꾸준히 기록하려고 만든 블로그