@@ -241,41 +241,40 @@ private int levelCount = 1;
241241private Node h = new Node ();
242242
243243public void add(int value) {
244-
245- // 随机生成高度
246- int level = randomLevel();
244+ int level = randomLevel(); // 新节点的随机高度
247245
248246 Node newNode = new Node ();
249247 newNode. data = value;
250248 newNode. maxLevel = level;
251249
252- // 创建一个node数组,用于记录小于当前value的最大值
253- Node [] maxOfMinArr = new Node [level];
254- // 默认情况下指向头节点
250+ // 用于记录每层前驱节点的数组
251+ Node [] update = new Node [level];
255252 for (int i = 0 ; i < level; i++ ) {
256- maxOfMinArr [i] = h;
253+ update [i] = h;
257254 }
258255
259- // 基于上述结果拿到当前节点的后继节点
260256 Node p = h;
261- for (int i = level - 1 ; i >= 0 ; i-- ) {
257+ // 关键修正:从跳表的当前最高层开始查找
258+ for (int i = levelCount - 1 ; i >= 0 ; i-- ) {
262259 while (p. forwards[i] != null && p. forwards[i]. data < value) {
263260 p = p. forwards[i];
264261 }
265- maxOfMinArr[i] = p;
262+ // 只记录需要更新的层的前驱节点
263+ if (i < level) {
264+ update[i] = p;
265+ }
266266 }
267267
268- // 更新前驱节点的后继节点为当前节点newNode
268+ // 插入新节点
269269 for (int i = 0 ; i < level; i++ ) {
270- newNode. forwards[i] = maxOfMinArr [i]. forwards[i];
271- maxOfMinArr [i]. forwards[i] = newNode;
270+ newNode. forwards[i] = update [i]. forwards[i];
271+ update [i]. forwards[i] = newNode;
272272 }
273273
274- // 如果当前newNode高度大于跳表最高高度则更新levelCount
274+ // 更新跳表的总高度
275275 if (levelCount < level) {
276276 levelCount = level;
277277 }
278-
279278}
280279```
281280
@@ -380,7 +379,7 @@ public class SkipList {
380379 /**
381380 * 每个节点添加一层索引高度的概率为二分之一
382381 */
383- private static final float PROB = 0.5 f ;
382+ private static final float PROB = 0.5f ;
384383
385384 /**
386385 * 默认情况下的高度为1,即只有自己一个节点
@@ -392,9 +391,11 @@ public class SkipList {
392391 */
393392 private Node h = new Node ();
394393
395- public SkipList () {}
394+ public SkipList () {
395+ }
396396
397397 public class Node {
398+
398399 private int data = - 1 ;
399400 /**
400401 *
@@ -404,58 +405,55 @@ public class SkipList {
404405
405406 @Override
406407 public String toString () {
407- return " Node{" +
408- " data=" + data +
409- " , maxLevel=" + maxLevel +
410- ' }' ;
408+ return " Node{"
409+ + " data=" + data
410+ + " , maxLevel=" + maxLevel
411+ + ' }' ;
411412 }
412413 }
413414
414415 public void add (int value ) {
415-
416- // 随机生成高度
417- int level = randomLevel();
416+ int level = randomLevel(); // 新节点的随机高度
418417
419418 Node newNode = new Node ();
420419 newNode. data = value;
421420 newNode. maxLevel = level;
422421
423- // 创建一个node数组,用于记录小于当前value的最大值
424- Node [] maxOfMinArr = new Node [level];
425- // 默认情况下指向头节点
422+ // 用于记录每层前驱节点的数组
423+ Node [] update = new Node [level];
426424 for (int i = 0 ; i < level; i++ ) {
427- maxOfMinArr [i] = h;
425+ update [i] = h;
428426 }
429427
430- // 基于上述结果拿到当前节点的后继节点
431428 Node p = h;
432- for (int i = level - 1 ; i >= 0 ; i-- ) {
429+ // 关键修正:从跳表的当前最高层开始查找
430+ for (int i = levelCount - 1 ; i >= 0 ; i-- ) {
433431 while (p. forwards[i] != null && p. forwards[i]. data < value) {
434432 p = p. forwards[i];
435433 }
436- maxOfMinArr[i] = p;
434+ // 只记录需要更新的层的前驱节点
435+ if (i < level) {
436+ update[i] = p;
437+ }
437438 }
438439
439- // 更新前驱节点的后继节点为当前节点newNode
440+ // 插入新节点
440441 for (int i = 0 ; i < level; i++ ) {
441- newNode. forwards[i] = maxOfMinArr [i]. forwards[i];
442- maxOfMinArr [i]. forwards[i] = newNode;
442+ newNode. forwards[i] = update [i]. forwards[i];
443+ update [i]. forwards[i] = newNode;
443444 }
444445
445- // 如果当前newNode高度大于跳表最高高度则更新levelCount
446+ // 更新跳表的总高度
446447 if (levelCount < level) {
447448 levelCount = level;
448449 }
449-
450450 }
451451
452452 /**
453453 * 理论来讲,一级索引中元素个数应该占原始数据的 50%,二级索引中元素个数占 25%,三级索引12.5% ,一直到最顶层。
454- * 因为这里每一层的晋升概率是 50%。对于每一个新插入的节点,都需要调用 randomLevel 生成一个合理的层数。
455- * 该 randomLevel 方法会随机生成 1~MAX_LEVEL 之间的数,且 :
456- * 50%的概率返回 1
457- * 25%的概率返回 2
458- * 12.5%的概率返回 3 ...
454+ * 因为这里每一层的晋升概率是 50%。对于每一个新插入的节点,都需要调用 randomLevel 生成一个合理的层数。 该 randomLevel
455+ * 方法会随机生成 1~MAX_LEVEL 之间的数,且 : 50%的概率返回 1 25%的概率返回 2 12.5%的概率返回 3 ...
456+ *
459457 * @return
460458 */
461459 private int randomLevel () {
@@ -523,11 +521,11 @@ public class SkipList {
523521 }
524522
525523 }
526-
527524}
525+
528526```
529527
530- 对应测试代码和输出结果如下 :
528+ 测试代码 :
531529
532530``` java
533531public static void main(String [] args) {
@@ -550,61 +548,6 @@ public static void main(String[] args) {
550548 }
551549```
552550
553- 输出结果:
554-
555- ``` bash
556- ********** 输出添加结果**********
557- Node{data=0, maxLevel=2}
558- Node{data=1, maxLevel=3}
559- Node{data=2, maxLevel=1}
560- Node{data=3, maxLevel=1}
561- Node{data=4, maxLevel=2}
562- Node{data=5, maxLevel=2}
563- Node{data=6, maxLevel=2}
564- Node{data=7, maxLevel=2}
565- Node{data=8, maxLevel=4}
566- Node{data=9, maxLevel=1}
567- Node{data=10, maxLevel=1}
568- Node{data=11, maxLevel=1}
569- Node{data=12, maxLevel=1}
570- Node{data=13, maxLevel=1}
571- Node{data=14, maxLevel=1}
572- Node{data=15, maxLevel=3}
573- Node{data=16, maxLevel=4}
574- Node{data=17, maxLevel=2}
575- Node{data=18, maxLevel=1}
576- Node{data=19, maxLevel=1}
577- Node{data=20, maxLevel=1}
578- Node{data=21, maxLevel=3}
579- Node{data=22, maxLevel=1}
580- Node{data=23, maxLevel=1}
581- ********** 查询结果:Node{data=22, maxLevel=1} **********
582- ********** 删除结果**********
583- Node{data=0, maxLevel=2}
584- Node{data=1, maxLevel=3}
585- Node{data=2, maxLevel=1}
586- Node{data=3, maxLevel=1}
587- Node{data=4, maxLevel=2}
588- Node{data=5, maxLevel=2}
589- Node{data=6, maxLevel=2}
590- Node{data=7, maxLevel=2}
591- Node{data=8, maxLevel=4}
592- Node{data=9, maxLevel=1}
593- Node{data=10, maxLevel=1}
594- Node{data=11, maxLevel=1}
595- Node{data=12, maxLevel=1}
596- Node{data=13, maxLevel=1}
597- Node{data=14, maxLevel=1}
598- Node{data=15, maxLevel=3}
599- Node{data=16, maxLevel=4}
600- Node{data=17, maxLevel=2}
601- Node{data=18, maxLevel=1}
602- Node{data=19, maxLevel=1}
603- Node{data=20, maxLevel=1}
604- Node{data=21, maxLevel=3}
605- Node{data=23, maxLevel=1}
606- ```
607-
608551** Redis 跳表的特点** :
609552
6105531 . 采用** 双向链表** ,不同于上面的示例,存在一个回退指针。主要用于简化操作,例如删除某个元素时,还需要找到该元素的前驱节点,使用回退指针会非常方便。
0 commit comments