Skip List

duffyishereยท2023๋…„ 12์›” 2์ผ
post-thumbnail

๐Ÿฃ ๋“ค์–ด๊ฐ€๊ธฐ ์•ž์„œ

- ์ •๋ ฌ๋œ ๋ฆฌ์ŠคํŠธ๋Š” ์–ด๋–ป๊ฒŒ ๊ทธ ์ˆœ์„œ๋ฅผ ์œ ์ง€ํ•˜๋Š”์ง€ ์„ค๋ช…ํ•˜์‹ค ์ˆ˜ ์žˆ๋‚˜์š”?
- ๊ทธ๋ ‡๋‹ค๋ฉด, ์ •๋ ฌ๋œ ๋ฆฌ์ŠคํŠธ์—์„œ ํŠน์ • ๋ฒ”์œ„๋ฅผ ๊ฒ€์ƒ‰ํ•˜๋Š” ๊ณผ์ •์„ ์„ค๋ช…ํ•˜์‹ค ์ˆ˜ ์žˆ๋‚˜์š”?

๋งŒ์•ฝ, ์„ค๋ช…์ด ํž˜๋“ค๋‹ค๋ฉด ์ด๋ฒˆ ๊ธ€์„ ์ฝ์œผ์‹œ๋ฉด ๋„์›€์ด ๋  ๊ฒ๋‹ˆ๋‹ค.

์ด ๊ธ€์—์„œ๋Š” ์ •๋ ฌ๋œ ๋ฆฌ์ŠคํŠธ์—์„œ ๋น ๋ฅธ ๊ฒ€์ƒ‰์„ ์œ„ํ•œ ์ž๋ฃŒ๊ตฌ์กฐ Skip List๋ฅผ ์†Œ๊ฐœํ•ฉ๋‹ˆ๋‹ค.

๐ŸŒŸ Skip List

์•„๋ž˜์™€ ๊ฐ™์€ ์ •๋ ฌ๋œ Linked List๊ฐ€ ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด ๋ด…์‹œ๋‹ค.

์œ„ ๋ฆฌ์ŠคํŠธ์—์„œ '10'์„ ์ฐพ๋Š”๋‹ค๊ณ  ํ•  ๋–„ ๊ฑธ๋ฆฌ๋Š” ์‹œ๊ฐ„์€ 10์ž…๋‹ˆ๋‹ค.
๊ทธ๋Ÿผ ๋น ๋ฅธ ์ด๋™์„ ํ•  ์ˆ˜ ์žˆ๋Š” ๊ณ ์†๋„๋กœ(๋ ˆ๋ฒจ)๋ฅผ ์œ„์— ๋งŒ๋“ค์–ด ๋ด…์‹œ๋‹ค.

์ด์ œ '10'์„ ์ฐพ์„ ๋•Œ ๊ฑธ๋ฆฌ๋Š” ์‹œ๊ฐ„์€ 5์ž…๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ €์žฅํ•  ์ •๋ณด๊ฐ€ 4๋งŒํผ ๋Š˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ถ”๊ฐ€์ ์ธ ๊ณต๊ฐ„์„ ํ• ๋‹นํ•จ์œผ๋กœ์จ ๋น ๋ฅธ ๊ฒ€์ƒ‰์„ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๋Ÿฌํ•œ ๊ฐœ๋…์„ ํ™œ์šฉํ•˜์—ฌ, ๋™์ „ ๋’ค์ง‘๊ธฐ(1/2)๋ฅผ ํ†ตํ•ด ๋ ˆ๋ฒจ์„ ๋Š˜๋ฆด์ง€ ๊ฒฐ์ •ํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด ๋ด…์‹œ๋‹ค.

์œ„์™€ ๊ฐ™์€ ๊ทธ๋ฆผ์ด ๋‚˜์˜ค๊ฒŒ ๋  ๊ฑฐ๊ณ , ์ด๋Š” Skip List๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Skip List์—์„œ๋Š” ์œ„ ๊ทธ๋ฆผ๊ณผ ๊ฐ™์€ ๊ณผ์ •์œผ๋Ÿฌ ๊ฐ’์„ ์ฐพ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ด๋•Œ ์‹œ๊ฐ„ ๋ณต์žก๋„๋Š” ํ‰๊ท ์ ์œผ๋กœ O(log n)์ด๋ฉฐ ๊ณต๊ฐ„๋ณต์žก๋„๋Š” ๋ฌด์ž‘์œ„ Skip List์—์„œ ๋†’์ด๊ฐ€ log n์ด ๋˜๊ณ , ๊ฐ ๋ ˆ๋ฒจ์—์„œ์˜ ๋…ธ๋“œ ์ˆ˜๊ฐ€ ํ‰๊ท ์ ์œผ๋กœ ์ƒ์ˆ˜๋ฐฐ๋กœ ์ฆ๊ฐ€ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ „์ฒด ๊ณต๊ฐ„ ๋ณต์žก๋„๋Š” O(n log n)์ด ๋ฉ๋‹ˆ๋‹ค.

๐Ÿงต Skip List ๊ตฌํ˜„

์ด์ œ Skip List์˜ ์ž‘๋™ ๋ฐฉ์‹์„ ์‚ดํŽด๋ณด์•˜์œผ๋‹ˆ, ๊ตฌํ˜„๋œ ์ฝ”๋“œ๋ฅผ ๋ณด๋ฉฐ ์ „์ฒด์ ์ธ ๋™์ž‘์˜ ํ๋ฆ„์„ ์‚ดํŽด๋ด…๋‹ˆ๋‹ค.

์ „์ฒด์ ์ธ ์ฝ”๋“œ๋Š” ์—ฌ๊ธฐ์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ”— Node class

class Node<K extends Comparable<K>, V> {  
    private int level;  
  
    private K key;  
    private V value;  
  
    private Node<K, V> left, right, up, down;  
  
    public Node(K key, V value, int level) {  
        this.key = key;  
        this.value = value;  
        this.level = level;  
    }
    
    /* Getter, Setter... */
}

์šฐ์„  ๊ฐ’์„ ์ €์žฅํ•  Node๋ฅผ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค. Node์—๋Š” ์•ž๊ณผ ๋’ค ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์œ„, ์•„๋ž˜์˜ ์ฃผ์†Œ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ”Ž Skip List์—์„œ์˜ ์กฐํšŒ

Skip List๋Š” head์• ์„œ ์šฐ์ธก์œผ๋กœ ํƒ์ƒ‰์„ ํ•˜๋ฉฐ, ์ฐพ๋Š” ๊ฐ’์ด ์šฐ์ธก ๊ฐ’๋ณด๋‹ค ์ž‘์œผ๋ฉด ํ•œ๋‹จ๊ณ„ ์•„๋ž˜๋กœ ๋‚ด๋ ค ๊ฐ‘๋‹ˆ๋‹ค.

private Node<K, V> findNode(K key) {  
	// head์—์„œ ํƒ์ƒ‰ ์‹œ์ž‘
    Node<K, V> node = head;  
    Node<K, V> next;  
    Node<K, V> down;  
    K nodeKey;  

	// ๋ ˆ๋ฒจ ๋ณ„ ํƒ์ƒ‰
    while (true) {  
        next = node.getNext();  

		// ๋‹ค์Œ ๋…ธ๋“œ์˜ ํ‚ค ๊ฐ’์ด ์ฃผ์–ด์ง„ ํ‚ค๋ณด๋‹ค ํด ๋•Œ๊นŒ์ง€
        while(next != null && lessThanOrEqual(key, next.getKey())) {  
            node = next;  
            next = node.getNext();  
        }  
  
        nodeKey = node.getKey();  
        // ํ˜„์žฌ ๋…ธ๋“œ์˜ ํ‚ค ๊ฐ’์ด ์ฃผ์–ด์ง„ ํ‚ค์™€ ๋™์ผํ•œ ๊ฒฝ์šฐ ๋ฐ˜๋ณต๋ฌธ์„ ์ข…๋ฃŒ
        if (nodeKey != null && equalTo(key, nodeKey)) {  
            break;  
        }  
  
        down = node.getDown();  
        // ์•„๋ž˜ ๋…ธ๋“œ๊ฐ€ ๋” ์žˆ๋Š” ๊ฒฝ์šฐ ๋…ธ๋“œ๋ฅผ ์•„๋ž˜ ๋…ธ๋“œ๋กœ ์—…๋ฐ์ดํŠธ
        if (down != null) {  
            node = down;  
        } else {  
            break;  
        }  
    }  
  
    return node;  
}

๐ŸŒ• Skip List์—์„œ์˜ ์ถ”๊ฐ€

  1. findBy ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ๊ฐ€์žฅ ๊ฐ€๊นŒ์šฐ๋ฉฐ ์ž‘์€ ๊ฐ’์„ ์ฐพ์•„ ์‚ฝ์ž…ํ•ฉ๋‹ˆ๋‹ค.
  2. ๋™์ „ ๋’ค์ง‘๊ธฐ ๊ฒฐ๊ณผ์— ๋”ฐ๋ผ ํ†ตํ•ด ๋ ˆ๋ฒจ์„ ๋Š˜๋ฆฝ๋‹ˆ๋‹ค.
    • ๋งŒ์•ฝ, ์ตœ๋Œ€ ๋ ˆ๋ฒจ์„ ๊ฐฑ์‹ ํ•˜๋ฉด ํ•ด๋”๋ฅผ ๊ฐฑ์‹ ํ•ด ์ค๋‹ˆ๋‹ค.
public void add(K key, V value) {  
    Node<K, V> node = findNode(key);  
    if (node.getKey() != null && equalTo(key, node.getKey())) {  
        node.setValue(value);  
        return;  
    }  
  
    Node<K, V> prevNode = node;  
    int currentLevel = 1;  
    int headLevel = head.getLevel();  
    Node<K, V> newNode = new Node<>(key, value, currentLevel);
    insertAfter(newNode, prevNode);  
  
    while (canBuildNextLevel()) {  
	    // ์ตœ๋Œ€ ๋ ˆ๋ฒจ์„ ๋„˜๊ธด ๊ฒฝ์šฐ
        if (currentLevel >= head.getLevel()) {  
            Node<K, V> newHead = new Node<>(null, null, headLevel + 1);  
            linkVertical(newHead, head);  
            head = newHead;  
            headLevel = head.getLevel();  
        }  

		// ๋†’์ธ ๋ ˆ๋ฒจ์˜ ๋…ธ๋“œ์™€ ์—ฐ๊ฒฐ๋  ๋…ธ๋“œ ํƒ์ƒ‰
        while (prevNode.getUp() == null) {  
            prevNode = prevNode.getPrevious();  
        }  
        prevNode = prevNode.getUp();  

		// ๋…ธ๋“œ ์—ฐ๊ฒฐ
        Node<K, V> upperNode = new Node<>(key, value, prevNode.getLevel());  
        insertAfter(prevNode, upperNode);  
        linkVertical(upperNode, newNode);  
        newNode = upperNode;  
        currentLevel++;  
    }  
    size++;  
}

๐Ÿ—‘๏ธ Skip List์—์„œ์˜ ์‚ญ์ œ

  1. findBy๋ฅผ ํ†ตํ•ด ์ฃผ์–ด์ง„ ํ‚ค๋ฅผ ์ฐพ์Šต๋‹ˆ๋‹ค.
  2. ๋ฐ˜ํ™˜๋œ ๋…ธ๋“œ์˜ ์—ฐ๊ฒฐ์„ ๋Š์œผ๋ฉฐ ์•„๋ž˜ ๋…ธ๋“œ๋กœ ์ด๋™ํ•ฉ๋‹ˆ๋‹ค.
public V remove(K key) {  
    Node<K, V> node = findNode(key);  
    if (!equalTo(key, node.getKey())) {  
        return null;  
    }  
  
    V deletedValue = node.getValue();  
    while (node != null) {  
        Node<K, V> downNode = node.getDown();  
        node.disconnect();  
        node = downNode;  
    }  
  
    return deletedValue;  
}

๐Ÿ‘ป 3์ค„ ์š”์•ฝ

  1. Linked List์™€ ์œ ์‚ฌํ•œ ๊ตฌ์กฐ๋ฅผ ๊ฐ€์ง€๋ฉฐ, ๊ฐ ๋…ธ๋“œ๋Š” ์—ฌ๋Ÿฌ ๋ ˆ๋ฒจ์„ ๊ฐ€์ง์œผ๋กœ์จ ๋น ๋ฅธ ๊ฒ€์ƒ‰๊ณผ ํšจ์œจ์ ์ธ ๋ฒ”์œ„ ๊ฒ€์ƒ‰์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
  2. ๋ ˆ๋ฒจ์„ ํ™•๋ฅ ์ ์œผ๋กœ ํ• ๋‹นํ•˜์—ฌ, ๊ฐ ๋…ธ๋“œ๊ฐ€ ๋‹ค์–‘ํ•œ ๋ ˆ๋ฒจ์„ ๊ฐ€์ง€๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.
  3. ๋ชจ๋“  ์—ฐ์‚ฐ์˜ ํ‰๊ท  ์‹œ๊ฐ„ ๋ณต์žก๋„๋Š” O(log n)์ž…๋‹ˆ๋‹ค.

๐Ÿ“˜ ์ฐธ๊ณ  ๋ฌธํ—Œ

Algorithms Lab Youtube
Wikipedia

0๊ฐœ์˜ ๋Œ“๊ธ€