數據結構-跳錶skiplist

概述

常用的數據結構有線性的數組和非線性結構的鏈表,而非線性數據結構中典型的有平衡二叉樹(AVL),紅黑樹,B樹,LSM樹,skiplist 樹,bitmap,hash表等,其中我們經常在各類程序語言中見到的集合容器往往是基於紅黑樹,skiplist,hash表實現的,而數據庫級常用的數據結構往往基於B樹(包括B+,B*)樹,LSM樹等重量級的數據結構。

這裏詳細介紹一下跳錶這個特殊的數據結構,它天然有序的高效的鬆散型數據結構,插入和查找性能與紅黑樹不相上下,而且維護更加簡單粗暴。

跳錶的特點

1.是分層結構,每一層都是有序,雙向連接的
2.每個節點都是一個固定的概率往上跳出
3.第0層是最完整的數據
在這裏插入圖片描述
太懶,不想畫圖,圖片示意來源網上

關鍵邏輯

  1. 隨機函數。
private int randomLevel() {
        int x = randomSeed;
        x ^= x << 13;
        x ^= x >>> 17;
        randomSeed = x ^= x << 5;
        if ((x & 0x8001) != 0) // test highest and lowest bits
            return 0;
        int level = 1;
        while (((x >>>= 1) & 1) != 0) ++level;
        return level;
    }

算法源自jdk 的ConcurrentSkipListMap,比起math.random 更直接,也可直接使用Random
用來返回用來獲取向上跳出的層數
2. 定位
複雜度是常數級別
過程如圖:
在這裏插入圖片描述
代碼示例如下:

  /**
     * 在最下面一層,找到要插入的位置前面的那個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;
        }
    }
  1. 插入和刪除可以參考下面的邏輯

  public Integer insert(int key, int value) {
    	SkipListEntry p, q;
        int i = 0;
 
        // 查找適合插入的位子
        p = findEntry(key);
 
        // 如果跳躍表中存在含有key值的節點,則進行value的修改操作即可完成
        if(p.key ==key) {
            Integer oldValue = p.value;
            p.value = value;
            return oldValue;
        }
 
     // 如果跳躍表中不存在含有key值的節點,則進行新增操作
        q = new SkipListEntry(key, value); 
        /* --------------------------------------------------------------
        Insert q into the lowest level after SkipListEntry p:
                         p   put q here           p        q
                         |     |                  |        |
	 	                 V     V                  V        V        V
        Lower level:    [ ] <------> [ ]    ==>  [ ] <--> [ ] <--> [ ]
        --------------------------------------------------------------- */
     q.left = p;
     q.right = p.right;
     p.right.left = q;
     p.right = q;
 
       //本層操作完畢,看更高層操作
     //拋硬幣隨機決定是否上層插入
     while ( r.nextDouble() < 0.5 /* Coin toss */ )
     {
    	 if ( i >= h )   // We reached the top level !!!
         {
            //Create a new empty TOP layer
    		 addEmptyLevel();
         }
    	 /* ------------------------------------
         Find first element with an UP-link
         ------------------------------------ */
	      while ( p.up == null )
	      {
	         p = p.left;
	      }
      /* --------------------------------
	   Make p point to this UP element
	   -------------------------------- */
          p = p.up;
 
	/* ---------------------------------------------------
          Add one more (k,*) to the column
	   Schema for making the linkage:
               p <--> e(k,*) <--> p.right
                         ^
		          |
		          v
		          q
	   ---------------------------------------------------- */
  	SkipListEntry e;
  	 // 這裏需要注意的是除底層節點之外的節點對象是不需要value值的
  	e = new SkipListEntry(key, null); 
  	/* ---------------------------------------
	   Initialize links of e
	   --------------------------------------- */
	e.left = p;
	e.right = p.right;
	e.down = q;
	/* ---------------------------------------
	   Change the neighboring links..  
	   --------------------------------------- */
	p.right.left = e;
	p.right = e;
	q.up = e;
    
	//把q執行新插入的節點:
	 q = e; 
	 // level增加
	 i = i + 1;  	 
     }
    n = n+1; //更新鏈表長度        
        return null;
    }
    private void addEmptyLevel() {
 
        SkipListEntry p1, p2;
 
        p1 = new SkipListEntry(Integer.MIN_VALUE, null);
        p2 = new SkipListEntry(Integer.MAX_VALUE, null);
 
        p1.right = p2;
        p1.down = head;
 
        p2.left = p1;
        p2.down = tail;
 
        head.up = p1;
        tail.up = p2;
 
        head = p1;
        tail = p2;
 
        h = h + 1;
    }

public Integer remove(int key) {

    SkipListEntry p, q;

    p = findEntry(key);

    if(!p.key.equals(key)) {
        return null;
    }

    Integer oldValue = p.value;
    while(p != null) {
        q = p.up;
        p.left.right = p.right;
        p.right.left = p.left;
        p = q;
    }

    return oldValue;

}

參考原文鏈接:https://blog.csdn.net/bohu83/article/details/83654524

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章