自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

數(shù)據(jù)結(jié)構(gòu):跳躍鏈表

開發(fā) 前端
開發(fā)時(shí)經(jīng)常使用的平衡數(shù)據(jù)結(jié)構(gòu)有B數(shù)、紅黑數(shù),AVL數(shù)。但是如果讓你實(shí)現(xiàn)其中一種,很難,實(shí)現(xiàn)起來費(fèi)時(shí)間。而跳躍鏈表一種基于鏈表數(shù)組實(shí)現(xiàn)的快速查找數(shù)據(jù)結(jié)構(gòu),目前開源軟件 Redis 和 LevelDB 都有用到它。

[[415029]]

本文轉(zhuǎn)載自微信公眾號「潛行前行」,作者cscw。轉(zhuǎn)載本文請聯(lián)系潛行前行公眾號。

什么是跳躍鏈表

開發(fā)時(shí)經(jīng)常使用的平衡數(shù)據(jù)結(jié)構(gòu)有B數(shù)、紅黑數(shù),AVL數(shù)。但是如果讓你實(shí)現(xiàn)其中一種,很難,實(shí)現(xiàn)起來費(fèi)時(shí)間。而跳躍鏈表一種基于鏈表數(shù)組實(shí)現(xiàn)的快速查找數(shù)據(jù)結(jié)構(gòu),目前開源軟件 Redis 和 LevelDB 都有用到它。它的效率和紅黑樹以及 AVL 樹不相上下

跳躍鏈表結(jié)構(gòu)

結(jié)構(gòu)

  1. public class SkipList<T> { 
  2.     //跳躍表的頭尾 
  3.     private SkipListNode<T> head; 
  4.     //跳躍表含的元素長度 
  5.     private int length; 
  6.     //跳表的層數(shù) 的歷史最大層數(shù) 
  7.     public int maxLevel; 
  8.     public SecureRandom random; 
  9.     private static final int MAX_LEVEL = 31; 
  10.     public SkipList() { 
  11.         //初始化頭尾節(jié)點(diǎn)及兩者的關(guān)系 
  12.         head = new SkipListNode<>(SkipListNode.HEAD_SCORE, null, MAX_LEVEL); 
  13.         //初始化大小,層,隨機(jī) 
  14.         length = 0; 
  15.         maxLevel = 0; // 層數(shù)從零開始計(jì)算 
  16.         random = new SecureRandom(); 
  17.     } 
  18.     ... 
  • header:指向跳躍表的頭節(jié)點(diǎn)
  • maxLevel:記錄目前跳躍表,層數(shù)最大節(jié)點(diǎn)的層數(shù)
  • length:鏈表存在的元素長度

節(jié)點(diǎn)

跳躍鏈表節(jié)點(diǎn)的組成:前節(jié)點(diǎn)、后節(jié)點(diǎn)、分值(map的key值)、及存儲對象 value

  1. public class SkipListNode<T> { 
  2.     //在跳表中排序的 分?jǐn)?shù)值 
  3.     public double score; 
  4.     public T value; 
  5.     public int level
  6.     // 前后節(jié)點(diǎn) 
  7.     public SkipListNode<T> next,pre; 
  8.     //上下節(jié)點(diǎn)形成的層 
  9.     public SkipListNode<T>[] levelNode; 
  10.     private SkipListNode(double score, int level){ 
  11.         this.score = score; 
  12.         this.level = level
  13.     } 
  14.     public SkipListNode(double score, T value, int level) { 
  15.         this.score = score; 
  16.         this.value = value; 
  17.         this.level = level
  18.         this.levelNode = new SkipListNode[level+1]; 
  19.         //初始化 SkipListNode 及 每一層的 node 
  20.         for (int i = level; i > 0; --i) { 
  21.             levelNode[i] = new SkipListNode<T>(score, level); 
  22.             levelNode[i].levelNode = levelNode; 
  23.         } 
  24.         this.levelNode[0] = this; 
  25.     } 
  26.     @Override 
  27.     public String toString() {  return "Node[score=" + score + ", value=" + value + "]"; } 

跳表是用空間來換時(shí)間

在我實(shí)現(xiàn)的跳躍鏈表節(jié)點(diǎn),包括一個(gè) levelNode 成員屬性。它就是節(jié)點(diǎn)層。跳躍鏈表能實(shí)現(xiàn)快速訪問的關(guān)鍵點(diǎn)就是它

平時(shí)訪問一個(gè)數(shù)組,我們是順序遍歷的,而跳躍鏈表效率比數(shù)組鏈表高,是因?yàn)樗褂霉?jié)點(diǎn)層存儲多級索引,形成一個(gè)稀疏索引,所以需要的更多的內(nèi)存空間

跳躍鏈表有多快

如果一個(gè)鏈表有 n 個(gè)結(jié)點(diǎn),每兩個(gè)結(jié)點(diǎn)抽取出一個(gè)結(jié)點(diǎn)建立索引的話,那么第一層索引的結(jié)點(diǎn)數(shù)大約就是 n/2,第二層索引的結(jié)點(diǎn)數(shù)大約為 n/4,以此類推第 m 層索引的節(jié)點(diǎn)數(shù)大約為 n/(2^m)

訪問數(shù)據(jù)時(shí)可以從 m 層索引查詢定位到 m-1 層索引數(shù)據(jù)。而 m-1 大約是 m 層的1/2。也就是說最優(yōu)的時(shí)間復(fù)雜度為O(log/N)

最差情況。在實(shí)際實(shí)現(xiàn)中,每一層索引是無法每次以數(shù)據(jù)數(shù)量對折一次實(shí)現(xiàn)一層索引。因此折中的方式,每一層的索引是隨機(jī)用全量數(shù)據(jù)建一條。也就是說最差情況時(shí)間復(fù)雜度為O(N),但最優(yōu)時(shí)間復(fù)雜度不變

查詢

查詢一開始是遍歷最高層 maxLevel 的索引 m。按照以下步驟查詢出等于 score 或者最接近 score 的左節(jié)點(diǎn)

1:如果同層索引的 next 節(jié)點(diǎn)分值小于查詢分值,則跳到 next 節(jié)點(diǎn)。cur = next

2:如果 next 為空?;蛘遪ext節(jié)點(diǎn)分值大于查詢分值。則跳到下一層 m-1 索引,循環(huán) 2

循環(huán) 1、2 步驟直到訪問到節(jié)點(diǎn)分值和查詢分值一致,或者索引層為零

  1. // SkipList 
  2.     private SkipListNode<T> findNearestNode(double score) { 
  3.         int curLevel = maxLevel; 
  4.         SkipListNode<T> cur = head.levelNode[curLevel]; 
  5.         SkipListNode<T> next = cur.next
  6.         // 和當(dāng)前節(jié)點(diǎn)分?jǐn)?shù)相同 或者 next 為 null 
  7.         while (score != cur.score && curLevel > 0) { 
  8.             // 1 向右 next 遍歷 
  9.             if (next != null && score >= next.levelNode[0].score) { 
  10.                 cur = next
  11.             } 
  12.             next = cur.levelNode[curLevel].next
  13.             // 2 向下遍歷,層數(shù)減1 
  14.             while ((next == null || score < next.levelNode[0].score) && curLevel > 0) { 
  15.                 next = cur.levelNode[--curLevel].next; 
  16.             } 
  17.         } 
  18.         // 最底層的 node。 
  19.         return cur.levelNode[0]; 
  20.     } 
  21.     public SkipListNode<T> get(double score) { 
  22.         //返回跳表最底層中,最接近這個(gè) score 的node 
  23.         SkipListNode<T> p = findNearestNode(score); 
  24.         //score 相同,返回這個(gè)node 
  25.         return p.score == score ? p : null
  26.     } 

插入

如果分值存在則替換 value

如果分值對應(yīng)節(jié)點(diǎn)不存在,則隨機(jī)一個(gè)索引層數(shù) level (取值 0~31)。然后依靠節(jié)點(diǎn)屬性 levelNode 加入 0 到 level 層的索引

  1. //SkipList 
  2.     public T put(double score, T value) { 
  3.         //首先得到跳表最底層中,最接近這個(gè)key的node 
  4.         SkipListNode<T> p = findNearestNode(score); 
  5.         if (p.score == score) { 
  6.             // 在跳表中,只有最底層的node才有真正的value,只需修改最底層的value就行 
  7.             T old = p.value; 
  8.             p.value = value; 
  9.             return old; 
  10.         } 
  11.         // nowNode 為新建的最底層的node。索引層數(shù) 0 到 31 
  12.         int nodeLevel = (int) Math.round(random.nextDouble() * 32); 
  13.         SkipListNode<T> nowNode = new SkipListNode<T>(score, value, nodeLevel); 
  14.         //初始化每一層,并連接每一層前后節(jié)點(diǎn) 
  15.         int level = 0; 
  16.         while (nodeLevel >= p.level) { 
  17.             for (; level <= p.levellevel++) { 
  18.                 insertNodeHorizontally(p.levelNode[level], nowNode.levelNode[level]); 
  19.             } 
  20.             p = p.pre; 
  21.         } 
  22.         // 此時(shí) p 的層數(shù)大于 nowNode 的層數(shù)才進(jìn)入循環(huán) 
  23.         for (; level <= nodeLevel; level++) { 
  24.             insertNodeHorizontally(p.levelNode[level], nowNode.levelNode[level]); 
  25.         } 
  26.         this.length ++ ; 
  27.         if (this.maxLevel < nodeLevel) { 
  28.             maxLevel = nodeLevel; 
  29.         } 
  30.         return value; 
  31.     } 
  32.  
  33.     private void insertNodeHorizontally(SkipListNode<T> pre, SkipListNode<T> now) { 
  34.         //先考慮now 
  35.         now.next = pre.next
  36.         now.pre = pre; 
  37.         //再考慮pre的next節(jié)點(diǎn) 
  38.         if (pre.next != null) { 
  39.             pre.next.pre = now; 
  40.         } 
  41.         //最后考慮pre 
  42.         pre.next = now; 
  43.     } 

刪除

使用 get 方法找到元素,然后解除節(jié)點(diǎn)屬性 levelNode 在每一層索引的前后引用關(guān)系即可

  1. //SkipList 
  2.     public T remove(double score){ 
  3.         //在底層找到對應(yīng)這個(gè)key的節(jié)點(diǎn) 
  4.         SkipListNode<T> now = get(score); 
  5.         if (now == null) { 
  6.             return null
  7.         } 
  8.         SkipListNode<T> curNode, next
  9.         //解除節(jié)點(diǎn)屬性 levelNode 在每一層索引的前后引用關(guān)系 
  10.         for (int i = 0; i <= now.level; i++){ 
  11.             curNode = now.levelNode[i]; 
  12.             next = curNode.next
  13.             if (next != null) { 
  14.                 next.pre = curNode.pre; 
  15.             } 
  16.             curNode.pre.next = curNode.next
  17.         } 
  18.         this.length--; //更新size,返回舊值 
  19.         return now.value; 
  20.     } 

使用示例

  1. public static void main(String[] args) { 
  2.     SkipList<String> list=new SkipList<>(); 
  3.     list.printSkipList(); 
  4.     list.put(1, "csc"); 
  5.     list.printSkipList(); 
  6.     list.put(3, "lwl"); 
  7.     list.printSkipList(); 
  8.     list.put(2, "hello world!"); 
  9.     list.printSkipList(); 
  10.  
  11.     System.out.println(list.get(2)); 
  12.     System.out.println(list.get(4)); 
  13.     list.remove(2); 
  14.     list.printSkipList(); 

歡迎指正文中錯(cuò)誤

參考文章 

  • redis設(shè)計(jì)與實(shí)現(xiàn)
  • 跳表(跳躍表,skipList)總結(jié)-java版[1]
  • 數(shù)據(jù)結(jié)構(gòu)與算法——跳表[2]

 

責(zé)任編輯:武曉燕 來源: 潛行前行
相關(guān)推薦

2023-02-08 07:52:36

跳躍表數(shù)據(jù)結(jié)構(gòu)

2021-05-12 14:09:35

鏈表數(shù)據(jù)結(jié)構(gòu)線性結(jié)構(gòu)

2021-07-15 06:43:12

Python數(shù)據(jù)結(jié)構(gòu)

2017-03-01 13:58:46

Python數(shù)據(jù)結(jié)構(gòu)鏈表

2021-07-13 07:52:03

Python數(shù)據(jù)結(jié)構(gòu)

2021-10-29 11:27:52

鏈表數(shù)據(jù)結(jié)構(gòu)算法

2021-01-06 08:03:00

JavaScript數(shù)據(jù)結(jié)構(gòu)

2012-02-02 10:21:05

單鏈表nexthead

2021-01-28 07:33:34

JavaScript鏈表數(shù)據(jù)

2021-04-12 15:47:00

數(shù)據(jù)結(jié)構(gòu)算法鏈表

2021-03-10 08:42:19

Java數(shù)據(jù)結(jié)構(gòu)算法

2021-12-21 08:19:29

數(shù)據(jù)結(jié)構(gòu)算法鏈表相交

2011-03-31 15:41:51

Cacti數(shù)據(jù)表結(jié)構(gòu)

2023-10-31 08:51:25

數(shù)據(jù)結(jié)構(gòu)存儲數(shù)據(jù)

2012-04-28 14:21:47

Java數(shù)據(jù)結(jié)構(gòu)線性結(jié)構(gòu)

2021-03-11 08:53:20

Java數(shù)據(jù)結(jié)構(gòu)算法

2020-10-28 10:10:03

Java單鏈表數(shù)據(jù)結(jié)構(gòu)

2023-10-06 20:21:28

Python鏈表

2020-10-21 14:57:04

數(shù)據(jù)結(jié)構(gòu)算法圖形

2023-11-12 21:49:10

Redis數(shù)據(jù)庫
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號