增長了向前指針的鏈表叫做跳錶。跳錶全稱叫作跳躍表。跳錶是一個隨機化的數據結構,實質就是一種能夠進行二分查找的有序鏈表。跳錶在原有的有序鏈表上面增長了多級索引,經過索引來實現快速查找。跳錶不只能提升搜索性能,同時也能夠提升插入和刪除操做的性能。(摘自百度百科)java
//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;
跳躍表是有序集合的底層實現之一(zset)。node
Redis的跳躍表實現由zskiplist和zskiplistNode兩個結構組成,其中zskiplist用於保存跳躍表信息(好比表頭節點、表尾節點、長度),而zskiplistNode則用於表示跳躍表節點。數據結構
每一個跳躍表節點的層高都是1至32之間的隨機數。app
在同一個跳躍表中,多個節點能夠包含相同的分值,但每一個節點的成員對象必須是惟一的。dom
跳躍表中的節點按照分值大小進行排序,當分值相同時,節點按照成員對象的大小進行排序。ide
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()); }