跳表(跳跃表)(SkipList)的java实现

小咪咪 2023-02-10 03:46 158阅读 0赞

跳表的详细数据结构解释见如下blog:

跳跃表Skip List的原理和实现(Java)

参照上述博文的实现如下:

  1. package com.foraixh.datastructure;
  2. import java.util.Random;
  3. /**
  4. * @author myvina@qq.com
  5. * @date 2020/5/19 19:28
  6. * @usage 跳跃表的简单实现
  7. */
  8. public class SkipList<V> {
  9. /**
  10. * 跳跃表的头指针
  11. */
  12. private Entry<V> head;
  13. /**
  14. * 跳跃表的尾指针
  15. */
  16. private Entry<V> tail;
  17. /**
  18. * 存储的数据量
  19. */
  20. private int size;
  21. /**
  22. * 跳跃表的高度
  23. */
  24. private int height;
  25. /**
  26. * 决定是否能进入到上一层
  27. */
  28. private Random coinToss;
  29. private final Entry<V> MIN_ENTRY = new Entry<>(Integer.MIN_VALUE, null);
  30. private final Entry<V> MAX_ENTRY = new Entry<>(Integer.MAX_VALUE, null);
  31. public SkipList() {
  32. size = 0;
  33. height = 0;
  34. coinToss = new Random();
  35. head = MIN_ENTRY;
  36. tail = MAX_ENTRY;
  37. MIN_ENTRY.right = MAX_ENTRY;
  38. MAX_ENTRY.left = MIN_ENTRY;
  39. }
  40. public static void main(String[] args) {
  41. SkipList<String> skipList = new SkipList<>();
  42. for (int i = 0; i < 10; i++) {
  43. skipList.put(i, "值:" + i);
  44. }
  45. skipList.show();
  46. }
  47. private void show() {
  48. Entry<V> p = head;
  49. while (p != null) {
  50. Entry<V> q = p;
  51. System.out.print(p);
  52. while (p.right != null) {
  53. System.out.print("->" + p.right);
  54. p = p.right;
  55. }
  56. System.out.println();
  57. p = q.down;
  58. }
  59. }
  60. private Entry<V> findEntry(Integer key) {
  61. Entry<V> p = head;
  62. while (true) {
  63. // 从左向右查找,直到右节点的key值大于要查找的key值
  64. while (!p.right.getScore().equals(MAX_ENTRY.getScore()) && p.right.getScore().compareTo(key) <= 0) {
  65. p = p.right;
  66. }
  67. // 如果有更低层的节点,则向低层移动
  68. if (p.down != null) {
  69. p = p.down;
  70. } else {
  71. break;
  72. }
  73. }
  74. // 返回p 注意这里p的key值是小于等于传入key的值的(p.key <= key)
  75. return p;
  76. }
  77. public V get(Integer key) {
  78. Entry<V> p = findEntry(key);
  79. if (p.getScore().equals(key)) {
  80. return p.value;
  81. }
  82. return null;
  83. }
  84. public void put(Integer score, V value) {
  85. Entry<V> q = new Entry<>(score, value);
  86. // 第一步:查找适合插入的节点
  87. Entry<V> p = findEntry(score);
  88. // 第二部:如果跳跃表中存在含有key值的节点,则进行value的修改操作即可完成
  89. if (p.getScore().equals(score)) {
  90. p.setValue(value);
  91. return;
  92. }
  93. // 第三步:如果跳跃表中不存在含有key值的节点,则进行新增操作
  94. insertEntryToRight(p, q);
  95. // 第四步:再使用随机数决定是否要向更高level攀升
  96. // 插入的新节点一定是在最底层
  97. int level = 0;
  98. // 如果新增的节点q的左右节点已经是边界了,则不允许新增一层
  99. while ((Integer.MIN_VALUE != q.left.getScore() || Integer.MAX_VALUE != q.right.getScore()) && coinToss.nextBoolean()) {
  100. // 如果新元素的级别已经达到跳跃表的最大高度,则新建空白层
  101. if (level >= height) {
  102. addEmptyLevel();
  103. }
  104. while (p.up == null) {
  105. p = p.left;
  106. }
  107. p = p.up;
  108. // 新增和q指针指向的节点含有相同key值的节点对象
  109. // 这里需要注意的是除底层节点之外的节点对象是不需要value值的
  110. Entry<V> z = new Entry<>(score, null);
  111. insertEntryToRight(p, z);
  112. z.down = q;
  113. q.up = z;
  114. q = z;
  115. level++;
  116. }
  117. size++;
  118. }
  119. public V remove(Integer key) {
  120. Entry<V> p = findEntry(key);
  121. if (!p.getScore().equals(key)) {
  122. return null;
  123. }
  124. V oldValue = p.getValue();
  125. Entry<V> q;
  126. while (p != null) {
  127. q = p.up;
  128. p.left.right = p.right;
  129. p.right.left = p.left;
  130. p = q;
  131. }
  132. return oldValue;
  133. }
  134. private void addEmptyLevel() {
  135. Entry<V> p = new Entry<>(Integer.MIN_VALUE, null);
  136. Entry<V> q = new Entry<>(Integer.MAX_VALUE, null);
  137. p.right = q;
  138. p.down = head;
  139. q.left = p;
  140. q.down = tail;
  141. head.up = p;
  142. tail.up = q;
  143. head = p;
  144. tail = q;
  145. height++;
  146. }
  147. /**
  148. * 在节点p的右边插入节点q
  149. */
  150. private void insertEntryToRight(Entry<V> p, Entry<V> q) {
  151. q.left = p;
  152. q.right = p.right;
  153. p.right.left = q;
  154. p.right = q;
  155. }
  156. public static class Entry<V> {
  157. /**
  158. * 根据score进行排序
  159. */
  160. private Integer score;
  161. private V value;
  162. /**
  163. * 前后左右的指针
  164. */
  165. Entry<V> up;
  166. Entry<V> down;
  167. Entry<V> left;
  168. Entry<V> right;
  169. public Entry() {
  170. }
  171. public Entry(Integer score, V value) {
  172. this.score = score;
  173. this.value = value;
  174. }
  175. public Integer getScore() {
  176. return score;
  177. }
  178. public void setScore(Integer score) {
  179. this.score = score;
  180. }
  181. public V getValue() {
  182. return value;
  183. }
  184. public void setValue(V value) {
  185. this.value = value;
  186. }
  187. @Override
  188. public String toString() {
  189. return "Entry{" +
  190. "score=" + score +
  191. ", value=" + value +
  192. '}';
  193. }
  194. }
  195. }

发表评论

表情:
评论列表 (有 0 条评论,158人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Skiplist

    跳跃表(skiplist)是一种随机化的数据结构,是一种可以与平衡树媲美的层次化链表结构——查找、删除、添加等时间复杂度都是O(log n),许多知名的开源软件中的数据结构均采

    相关 SkipList

    > 1.聊一聊跳表作者的其人其事 > > 2. 言归正传,跳表简介 > > 3. 跳表数据存储模型 > > 4. 跳表的代码实现分析 > > 5. 论文,代码下载及参考

    相关 SkipList原理

    为什么选择跳表        目前经常使用的平衡数据结构有:B树,红黑树,AVL树,Splay Tree, Treep等。想象一下,给你一张草稿纸,一只笔,一个编辑器,你