스킵리스트- Skip List
스킵리스트란Log시간에 검색/추가/제거를 수행할 수 있는 정렬된 자료구조이진검색트리를 대체할 수 있음스킵 포인터를 사용해서 빠르게 탐색 가능 - 9호선 급행처럼구현이 간단함!레퍼런스 없이 Red-Black Tree를 1시간 안에 구현할 수 있나요?
스킵리스트는 젊다!1960198019701990Treap[Aragon &Seidel]R Tree[Guttman]  AVL Tree[Avdelson-Velskii &Landis]RB TreeB Tree[Bayer]Splay Tree[Sleator& Tarjan]Skip List
아이디어 : 뛰어넘기빠른 검색을 위해 한 칸씩 뛰어넘으면서 찾으면 어떨까?메모리 : ½ N 만큼 더 사용검색 : 최대 N/2 + 1 노드를 검사
아이디어 : 또 뛰어넘기뛰어넘기 아이디어를 또 적용하면?메모리 : ¾ N만큼 더 사용속도 : 최대 N/4 + 3 노드를 검사
아이디어 : 계속 뛰어넘기2i번째 노드는2i 개의 포인터를 가지도록 만들면?메모리 : N 만큼 더 사용속도 : O(log N) 개의 노드만을 검사여기까진 이진검색트리랑 다를 게 없음
동적 데이터구조로서의 문제점노드를 추가하거나 삭제하면 패턴이 무너지게 됨다시 패턴을 만드는 데에는 O(N)이란 긴 시간이 필요함
스킵리스트: 확률적 자료구조크기의 확률분포를 유지하기!노드 중 ½은 포인터 1개를 가짐노드 중 ¼은 포인터 2개를 가짐노드 중 1/2i은 포인터 i개를 가짐규칙적인 패턴을 유지할 필요가 없어짐하지만 여전히 높은 확률로 Log N시간에 동작함struct Node<T> {    T key;    Node<T> *forward[];};
스킵리스트노드 크기의 분포는 앞서 보았던 리스트와 동일다만 패턴이 다르게 나타남
매개변수 pi개의 포인터를 가진 노드가r 개라면pr개의 노드는i+1개의 포인터를 가짐앞의 예제에서는 p = ½ p = ½일 때 가장 속도가 빠르다!적당한 속도와 메모리 효율을 위해 p = ¼ 를 권장포인터의 최대 개수는 log1𝑝𝑁개가 적당대부분의 구현에서는 최대 개수를 20개 정도로 고정함 
노드 검색하기
노드 삽입하기노드의 크기를 확률적으로 정함!크기가 1이 될 확률은 (1-p)크기가 2가 될 확률은 p(1-p)크기가 3이 될 확률은 p2(1-p)… p에 따른 노드 크기의 분포
노드 삽입하기앞쪽 포인터를 갱신하게 위해 BackLook배열을 유지하면서 탐색
노드 제거하기같은 방식으로 BackLook배열을 유지하면서 탐색제거하면서 앞뒤의 포인터를 이어줌어차피 랜덤이므로 노드 크기의 분포 유지엔 영향이 없음
최악의 경우 O(N) 이 되지만모든 노드의 크기가 같아지면 보통 리스트와 동일하게 됨.하지만 길이 100짜리 리스트가 그렇게 될 확률은?동전을 100번 던졌을 때 전부 앞면만 나올 확률로또1등먹을 확률!검색시간이 기대값(Log N)의 3배 이상 걸릴 확률은 엄청나게 낮다!
메모리 사용량 비교리스트 : 노드마다1개의 포인터 필요이진검색트리: 노드마다2개의 포인터 필요스킵리스트: 노드마다11−𝑝개의 포인터 필요𝐸𝑋=𝑛=1∞𝑛𝑃(𝑋=𝑛)=𝑛=1∞𝑛𝑝𝑛−1(1−𝑝)=11−𝑝 
메모리 사용량 비교p의 값에 따라 메모리 사용량을 타협할 수 있음더 적은 메모리 만으로도 Log 시간에 검색 가능노드당포인터 사용이진트리리스트p
멀티스레드에서RB트리의 문제점리밸런싱은 예측할 수 없지만 무겁다 – 재수없으면 망함Thread Stall락락락락(리밸런싱)락락락
스킵리스트는 병렬프로그래밍에 유리함락이 길어질 확률은 매우 빠르게 작아지기 때문에 불규칙한 락 때문에 모든 스레드가 멈춰버릴 확률이 적다.락을 포인터/노드별로로컬하게 걸 수 있어서 충돌의 위험이 더욱 작다.하지만 Persistent하게 만들기는 트리보다 어렵다.
언제 스킵리스트를 쓸까?멀티스레드에서 자주 수정할 컨테이너가 필요할 때순차적으로 탐색할 필요가 많은 경우 예 : 타임라인상의 이벤트 관리정렬할 필요가 없을 땐 해쉬테이블을 쓰는 것이 좋음cache miss/fragmentation 등으로 인해 B-Tree보다 느리게 동작하는 경우도 있으므로 신중한 결정이 필요함
스킵리스트의 변형들Indexed Skip List엣지마다 길이를 저장해서 인덱스 접근이 가능Deterministic Skip List최악의 경우에도Log시간이지만 패턴 유지를 위한 비용이 필요함1-2 skip list바로 아래 레벨의 노드2개 이상을 지나가지 않도록 유지 2-3 tree 에 대응1-2-3 skip list
스킵리스트 구현들Redis (BSD)스킵리스트를 이용한 key-value 저장 서버 (ANSI C)JAVA 1.6의 ConcurrentSkipListMap, Set스킵리스트를 이용해 구현한 멀티스레드를 지원하는 map과 setSkipdb (BSD)B-Tree대신 스킵리스트를 사용해서 구현한 버클리DB 방식의 DBQt의 Qmap (GPL, LGPL)Qt Framework중 GUI와 관계없는 기능을 모은 QtCore모듈에 있음STL map과 유사한 방식으로 사용 가능
ReferencePugh, William (June 1990). "Skip lists: a probabilistic alternative to balanced trees".Pugh, William (July 1989). “A Skip List Cookbook” Herlihy et. al. “A Probably Correct Concurrent Skip List”Fraser and Harris (2004). “Concurrent Programming Without Locks”Wikipedia : Skip List

Skip List

  • 1.
  • 2.
    스킵리스트란Log시간에 검색/추가/제거를 수행할수 있는 정렬된 자료구조이진검색트리를 대체할 수 있음스킵 포인터를 사용해서 빠르게 탐색 가능 - 9호선 급행처럼구현이 간단함!레퍼런스 없이 Red-Black Tree를 1시간 안에 구현할 수 있나요?
  • 3.
    스킵리스트는 젊다!1960198019701990Treap[Aragon &Seidel]RTree[Guttman] AVL Tree[Avdelson-Velskii &Landis]RB TreeB Tree[Bayer]Splay Tree[Sleator& Tarjan]Skip List
  • 4.
    아이디어 : 뛰어넘기빠른검색을 위해 한 칸씩 뛰어넘으면서 찾으면 어떨까?메모리 : ½ N 만큼 더 사용검색 : 최대 N/2 + 1 노드를 검사
  • 5.
    아이디어 : 또뛰어넘기뛰어넘기 아이디어를 또 적용하면?메모리 : ¾ N만큼 더 사용속도 : 최대 N/4 + 3 노드를 검사
  • 6.
    아이디어 : 계속뛰어넘기2i번째 노드는2i 개의 포인터를 가지도록 만들면?메모리 : N 만큼 더 사용속도 : O(log N) 개의 노드만을 검사여기까진 이진검색트리랑 다를 게 없음
  • 7.
    동적 데이터구조로서의 문제점노드를추가하거나 삭제하면 패턴이 무너지게 됨다시 패턴을 만드는 데에는 O(N)이란 긴 시간이 필요함
  • 8.
    스킵리스트: 확률적 자료구조크기의확률분포를 유지하기!노드 중 ½은 포인터 1개를 가짐노드 중 ¼은 포인터 2개를 가짐노드 중 1/2i은 포인터 i개를 가짐규칙적인 패턴을 유지할 필요가 없어짐하지만 여전히 높은 확률로 Log N시간에 동작함struct Node<T> { T key; Node<T> *forward[];};
  • 9.
    스킵리스트노드 크기의 분포는앞서 보았던 리스트와 동일다만 패턴이 다르게 나타남
  • 10.
    매개변수 pi개의 포인터를가진 노드가r 개라면pr개의 노드는i+1개의 포인터를 가짐앞의 예제에서는 p = ½ p = ½일 때 가장 속도가 빠르다!적당한 속도와 메모리 효율을 위해 p = ¼ 를 권장포인터의 최대 개수는 log1𝑝𝑁개가 적당대부분의 구현에서는 최대 개수를 20개 정도로 고정함 
  • 11.
  • 12.
    노드 삽입하기노드의 크기를확률적으로 정함!크기가 1이 될 확률은 (1-p)크기가 2가 될 확률은 p(1-p)크기가 3이 될 확률은 p2(1-p)… p에 따른 노드 크기의 분포
  • 13.
    노드 삽입하기앞쪽 포인터를갱신하게 위해 BackLook배열을 유지하면서 탐색
  • 14.
    노드 제거하기같은 방식으로BackLook배열을 유지하면서 탐색제거하면서 앞뒤의 포인터를 이어줌어차피 랜덤이므로 노드 크기의 분포 유지엔 영향이 없음
  • 15.
    최악의 경우 O(N)이 되지만모든 노드의 크기가 같아지면 보통 리스트와 동일하게 됨.하지만 길이 100짜리 리스트가 그렇게 될 확률은?동전을 100번 던졌을 때 전부 앞면만 나올 확률로또1등먹을 확률!검색시간이 기대값(Log N)의 3배 이상 걸릴 확률은 엄청나게 낮다!
  • 16.
    메모리 사용량 비교리스트: 노드마다1개의 포인터 필요이진검색트리: 노드마다2개의 포인터 필요스킵리스트: 노드마다11−𝑝개의 포인터 필요𝐸𝑋=𝑛=1∞𝑛𝑃(𝑋=𝑛)=𝑛=1∞𝑛𝑝𝑛−1(1−𝑝)=11−𝑝 
  • 17.
    메모리 사용량 비교p의값에 따라 메모리 사용량을 타협할 수 있음더 적은 메모리 만으로도 Log 시간에 검색 가능노드당포인터 사용이진트리리스트p
  • 18.
    멀티스레드에서RB트리의 문제점리밸런싱은 예측할수 없지만 무겁다 – 재수없으면 망함Thread Stall락락락락(리밸런싱)락락락
  • 19.
    스킵리스트는 병렬프로그래밍에 유리함락이길어질 확률은 매우 빠르게 작아지기 때문에 불규칙한 락 때문에 모든 스레드가 멈춰버릴 확률이 적다.락을 포인터/노드별로로컬하게 걸 수 있어서 충돌의 위험이 더욱 작다.하지만 Persistent하게 만들기는 트리보다 어렵다.
  • 20.
    언제 스킵리스트를 쓸까?멀티스레드에서자주 수정할 컨테이너가 필요할 때순차적으로 탐색할 필요가 많은 경우 예 : 타임라인상의 이벤트 관리정렬할 필요가 없을 땐 해쉬테이블을 쓰는 것이 좋음cache miss/fragmentation 등으로 인해 B-Tree보다 느리게 동작하는 경우도 있으므로 신중한 결정이 필요함
  • 21.
    스킵리스트의 변형들Indexed SkipList엣지마다 길이를 저장해서 인덱스 접근이 가능Deterministic Skip List최악의 경우에도Log시간이지만 패턴 유지를 위한 비용이 필요함1-2 skip list바로 아래 레벨의 노드2개 이상을 지나가지 않도록 유지 2-3 tree 에 대응1-2-3 skip list
  • 22.
    스킵리스트 구현들Redis (BSD)스킵리스트를이용한 key-value 저장 서버 (ANSI C)JAVA 1.6의 ConcurrentSkipListMap, Set스킵리스트를 이용해 구현한 멀티스레드를 지원하는 map과 setSkipdb (BSD)B-Tree대신 스킵리스트를 사용해서 구현한 버클리DB 방식의 DBQt의 Qmap (GPL, LGPL)Qt Framework중 GUI와 관계없는 기능을 모은 QtCore모듈에 있음STL map과 유사한 방식으로 사용 가능
  • 23.
    ReferencePugh, William (June1990). "Skip lists: a probabilistic alternative to balanced trees".Pugh, William (July 1989). “A Skip List Cookbook” Herlihy et. al. “A Probably Correct Concurrent Skip List”Fraser and Harris (2004). “Concurrent Programming Without Locks”Wikipedia : Skip List