原來這就是傳說中的跳躍表,不過如此(附:跳躍表的Java實現)

 1. 什麼是跳躍表?

增長了向前指針的鏈表叫做跳錶。跳錶全稱叫作跳躍表。跳錶是一個隨機化的數據結構,實質就是一種能夠進行二分查找的有序鏈表。跳錶在原有的有序鏈表上面增長了多級索引,經過索引來實現快速查找。跳錶不只能提升搜索性能,同時也能夠提升插入和刪除操做的性能。(摘自百度百科)java

watermark,size_14,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=

 

2. Redis中跳躍表的數據結構

watermark,size_14,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=

 

//zskiplistNode:跳躍表的一個節點
typedef struct zskiplistNode {
    // 層:每一個節點都包含不少層,每一層指向的都是同一個對象
    struct zskiplistLevel {
        // 前進指針
        struct zskiplistNode *forward;
        // 跨度:指當前這一層兩個節點的距離,如上圖o1和o3節點的L3層,中間跨過了o2節點,因此跨度就爲2
        unsigned int span;
    } level[];
    // 後退指針
    struct zskiplistNode *backward;
    // 分值:用於排序,跳躍表中的全部節點都按分值大小進行排序
    double score;
    // 成員對象:即真正要往鏈表中存放的對象
    robj *obj;
} zskiplistNode;

//zskiplist:跳躍錶鏈表
typedef struct zskiplist {
    // 表頭節點和表尾節點
    structz skiplistNode *header, *tail;
    // 表中節點的數量
    unsigned long length;
    // 表中層數最大的節點的層數
    int level;
} zskiplist;

3. 總結

跳躍表是有序集合的底層實現之一(zset)。node

  • Redis的跳躍表實現由zskiplist和zskiplistNode兩個結構組成,其中zskiplist用於保存跳躍表信息(好比表頭節點、表尾節點、長度),而zskiplistNode則用於表示跳躍表節點。數據結構

  • 每一個跳躍表節點的層高都是1至32之間的隨機數。app

  • 在同一個跳躍表中,多個節點能夠包含相同的分值,但每一個節點的成員對象必須是惟一的。dom

  • 跳躍表中的節點按照分值大小進行排序,當分值相同時,節點按照成員對象的大小進行排序。ide

4. 附:跳躍表的Java實現

SkipList.java性能

import java.util.Random;

/**
 * @author DengWeiPing
 * @version 1.0
 * @date 2021/5/16 10:00
 */
public class SkipList<T> {
    private SkipListNode<T> head, tail;
    private int nodes;//節點總數
    private int listLevel;//層數
    private Random random;// 用於投擲硬幣
    private static final double PROBABILITY = 0.5;//向上提高一個的機率

    public SkipList() {
        // TODO Auto-generated constructor stub
        random = new Random();
        clear();
    }

    /**
     * 清空跳躍表
     */
    public void clear() {
        head = new SkipListNode<T>(SkipListNode.HEAD_KEY, null);
        tail = new SkipListNode<T>(SkipListNode.TAIL_KEY, null);
        horizontalLink(head, tail);
        listLevel = 0;
        nodes = 0;
    }

    public boolean isEmpty() {
        return nodes == 0;
    }

    public int size() {
        return nodes;
    }

    /**
     * 在最下面一層,找到要插入的位置前面的那個key
     */
    private SkipListNode<T> findNode(int key) {
        SkipListNode<T> p = head;
        while (true) {
            while (p.right.key != SkipListNode.TAIL_KEY && p.right.key <= key) {
                p = p.right;
            }
            if (p.down != null) {
                p = p.down;
            } else {
                break;
            }

        }
        return p;
    }

    /**
     * 查找是否存在key,存在則返回該節點,不然返回null
     */
    public SkipListNode<T> search(int key) {
        SkipListNode<T> p = findNode(key);
        if (key == p.getKey()) {
            return p;
        } else {
            return null;
        }
    }

    /**
     * 向跳躍表中添加key-value
     */
    public void put(int k, T v) {
        SkipListNode<T> p = findNode(k);
        //若是key值相同,替換原來的vaule便可結束
        if (k == p.getKey()) {
            p.value = v;
            return;
        }
        SkipListNode<T> q = new SkipListNode<>(k, v);
        backLink(p, q);
        int currentLevel = 0;//當前所在的層級是0
        //拋硬幣,若是小於0.5,則往上一層也插入q
        while (random.nextDouble() < PROBABILITY) {
            //若是這次還是正面,且當前要插入的層數超出了高度,須要從新建一個頂層
            if (currentLevel >= listLevel) {
                listLevel++;
                SkipListNode<T> p1 = new SkipListNode<>(SkipListNode.HEAD_KEY, null);
                SkipListNode<T> p2 = new SkipListNode<>(SkipListNode.TAIL_KEY, null);
                horizontalLink(p1, p2);
                vertiacallLink(p1, head);
                vertiacallLink(p2, tail);
                head = p1;
                tail = p2;
            }
            //找到p的上一層,若是p沒有上層,則找它左邊節點的上一層
            while (p.up == null) {
                p = p.left;
            }
            p = p.up;//此時的p是上一層的節點

            SkipListNode<T> e = new SkipListNode<T>(k, null);//只保存key就ok
            backLink(p, e);//將e插入到上一層的p的右邊
            vertiacallLink(e, q);//將e和q上下鏈接
            q = e;//此時就在上一層也插入了q,而且和其左邊節點及下面節點相鏈接
            currentLevel++;
        }
        nodes++;//節點數+1
    }

    //node1後面插入node2
    private void backLink(SkipListNode<T> node1, SkipListNode<T> node2) {
        node2.left = node1;
        node2.right = node1.right;
        node1.right.left = node2;
        node1.right = node2;
    }

    /**
     * 水平雙向鏈接
     */
    private void horizontalLink(SkipListNode<T> node1, SkipListNode<T> node2) {
        node1.right = node2;
        node2.left = node1;
    }

    /**
     * 垂直雙向鏈接
     */
    private void vertiacallLink(SkipListNode<T> node1, SkipListNode<T> node2) {
        node1.down = node2;
        node2.up = node1;
    }

    /**
     * 打印出原始數據
     */
    @Override
    public String toString() {
        // TODO Auto-generated method stub
        if (isEmpty()) {
            return "跳躍表爲空!";
        }
        StringBuilder builder = new StringBuilder();
        SkipListNode<T> p = head;
        while (p.down != null) {
            p = p.down;
        }

        while (p.left != null) {
            p = p.left;
        }
        if (p.right != null) {
            p = p.right;
        }
        while (p.right != null) {
            builder.append(p);
            builder.append("\n");
            p = p.right;
        }

        return builder.toString();
    }

}

SkipListNode.java測試

/**
 * @author DengWeiPing
 * @version 1.0
 * @date 2021/5/16 9:59
 */
public class SkipListNode<T> {
    public int key;
    public T value;
    public SkipListNode<T> up, down, left, right; // 上下左右 四個指針

    public static final int HEAD_KEY = Integer.MIN_VALUE; // 負無窮
    public static final int TAIL_KEY = Integer.MAX_VALUE; // 正無窮

    public SkipListNode(int k, T v) {
        // TODO Auto-generated constructor stub
        key = k;
        value = v;
    }

    public int getKey() {
        return key;
    }

    public void setKey(int key) {
        this.key = key;
    }

    public T getValue() {
        return value;
    }

    public void setValue(T value) {
        this.value = value;
    }

    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null) {
            return false;
        }
        if (!(o instanceof SkipListNode<?>)) {
            return false;
        }
        SkipListNode<T> ent;
        try {
            ent = (SkipListNode<T>) o; // 檢測類型
        } catch (ClassCastException ex) {
            return false;
        }
        return (ent.getKey() == key) && (ent.getValue() == value);
    }

    @Override
    public String toString() {
        // TODO Auto-generated method stub
        return "key-value:" + key + "-" + value;
    }

}

test.javaui

public static void main(String[] args) {
    // TODO Auto-generated method stub
    SkipList<String> list = new SkipList<String>();
    System.out.println(list);
    list.put(2, "ping");
    list.put(1, "deng");
    list.put(3, "wei");
    list.put(1, "d");//測試同一個key值
    list.put(4, "鄧");
    list.put(6, "衛");
    list.put(5, "平");
    System.out.println(list);
    System.out.println(list.size());
}

 

相關文章
相關標籤/搜索