Home  >  Article  >  Java  >  Example sharing of Java implementation of skip list

Example sharing of Java implementation of skip list

黄舟
黄舟Original
2017-09-19 11:44:201428browse

This article mainly introduces the concept and implementation principle of jump table in Java programming, and briefly describes its structure. It has certain reference value and friends in need can learn about it.

The skip linked list is a randomized data structure based on parallel linked lists. Its efficiency is comparable to a binary search tree (requiring O(log n) average time for most operations) and is friendly to concurrent algorithms. .

Basically, a jump list adds additional forward links to an ordered linked list. The addition is performed in a randomized manner, so searches in the list can quickly skip parts of the list. (hence the name). All operations were performed with logarithmically randomized times.

Implementation principle:

The structure of the jump table is: if there are 10 nodes in the bottom layer, then there are theoretically 5 nodes in the upper layer of the bottom layer, and then there are 5 nodes in the upper layer. There are theoretically 2 or 3 nodes on one layer, and 1 node on the next layer. So it can be seen from here that the number of nodes in each layer is 1/2 of the elements in the next layer, and so on. From here we can see that as long as we ensure that the number of elements in the upper layer is 1/2 of the number of elements in the next layer when inserting, our jump list can become an ideal jump list. So how can we ensure that the number of upper elements is 1/2 of the number of lower elements when we insert? It can be solved simply by tossing a coin. Suppose element The probability of 1/2 is to be inserted into the next lowest level, so let’s flip a coin. Heads will be inserted, and tails will not be inserted. Compared with the second lowest level, we still have a 1/2 probability of insertion, so continue tossing the coin. Well, by analogy, the probability of prime X being inserted into the nth layer is (1/2) n times. In this way, we can insert an element into the jump list.

The first time I learned about the data structure of jump tables was about a year ago (I may have been despised by countless compatriots when I said this), but I didn’t know how to implement it. What impressed me most at that time was an article about Skip List - Implementation (Java), because the implementation method of Skip List in this article is the easiest to understand. It was through this article that I first learned about skip lists. Further understanding, so I really want to thank the owner of this article here. A year later, I found that my understanding of watch jumping was vague again, so this article was the first thing that came to my mind. I read this article again today and implemented the deletion method not given in it. And added generics, but generics only use generics for value, and still use the String type in the original text for Key. So it's still relatively simple and limited. The reason why I post it is to improve my understanding of Skip List and to serve as a reminder. The link to the original article is as mentioned before. I actually don’t know who the author of the original article is, so I just want to silently say thank you. Of course, if the original author feels that I have done anything offensive or infringing, I will delete the post immediately.

For the definition and introduction of skip tables, readers can refer to the original text. The original code is given directly here. The only difference between the original code here and the original text is that I have given here the deletion method that is not given in the original text (the original text actually refers to an English article, and the English article gives I didn’t discover the deletion method until later, but compared with the English article, the code is slightly redundant. The deletion method posted here is the deletion method I implemented myself). It may be poorly implemented, so I also urge everyone to criticize and correct me! ! !

1 The encapsulation class SkipListEntry.java


public class SkipListEntry<v>
{
 public String key;
 public V value;
 public int pos; // 主要为了打印 链表用
 public SkipListEntry<v deep="6"> up, down, left, right; // 上下左右 四个指针
 public static String negInf = new String("-oo"); // 负无穷
 public static String posInf = new String("+oo"); // 正无穷
 public SkipListEntry(String k, V v)
 {
  key = k;
  value = v;
  up = down = left = right = null;
 }
 public V getValue()
 {
  return value;
 }
 public String getKey()
 {
  return key;
 }
 public V setValue(V val)
 {
  V oldValue = value;
  value = val;
  return oldValue;
 }
 @SuppressWarnings("unchecked")
 public boolean equals(Object o)
 {
  SkipListEntry<v> entry;
  try
  {
   entry = (SkipListEntry<v>) o; // 检测类型
  } catch (ClassCastException ex)
  {
   return false;
  }
  return (entry.getKey() == key) && (entry.getValue().equals(value));
 }
 public String toString()
 {
  return "(" + key + "," + value + ")";
 }
}

2 Specific implementation of Skip List (including adding, deleting, modifying, and checking)


import java.util.Random;
/**
 * 跳表的一种简单实现。key只能为字符串类型,value可以为任意对象类型
 * @param <v>
 * @author xxx 2017年2月14日 下午9:42:06
 * @version v1.0
 */
public class SkipList<v>
{
 public SkipListEntry<v> head; // 顶层的第一个元素
 public SkipListEntry<v> tail; // 顶层的最后一个元素
 public int size; // 跳跃表中的元素个数
 public int height; // 跳跃表的高度
 public Random flag; // 投掷硬币
 /**
  * 默认构造函数
  * @author xxx 2017年2月14日 下午9:32:22
  * @since v1.0
  */
 public SkipList() 
 {
  head = new SkipListEntry<v>(SkipListEntry.negInf, null);
  tail = new SkipListEntry<v>(SkipListEntry.posInf, null);
  head.right = tail;
  tail.left = head;
  size = 0;
  height = 0;
  flag = new Random();
 }
 /**
  * 返回元素的个数
  * @return
  * @author xxx 2017年2月14日 下午9:35:22
  * @since v1.0
  */
 public int size()
 {
  return size;
 }
  /**
  * 判断跳表中的元素个数是否为零
  * @return
  * @author xxx 2017年2月14日 下午9:35:52
  * @since v1.0
  */
 public boolean isEmpty()
 {
  return (size == 0);
 }
 /**
  * 从最顶层的第一个元素,也即head元素开始查找,直到找到第0层、要插入的位置前面的那个key
  * @param k
  * @return
  * @author xxx 2017年2月14日 下午9:42:12
  * @since v1.0
  */
 private SkipListEntry<v> findEntry(String k)
 {
  SkipListEntry<v> p = head;
  while (true)
  {
   /*
    * 一直向右找,例: k=34。 10 ---> 20 ---> 30 ---> 40 ^ | p 会在30处停止
    */
   while (p.right.key != SkipListEntry.posInf && p.right.key.compareTo(k) <= 0)
   {
    p = p.right;
   }
   // 如果还有下一层,就到下一层继续查找
   if (p.down != null)
   {
    p = p.down;
   } else
   {
    break; // 到了最下面一层 就停止查找
   }
  }
  return p; // p.key <= k
 }
 /** 返回和key绑定的值 */
 public V get(String k)
 {
  SkipListEntry<v> p = findEntry(k);
 
  if (k.equals(p.getKey()))
  {
   return p.value;
  } else
  {
   return null;
  }
 }
 /**
  * 往跳表中插入一个键值对,如果键已经存在,则覆盖相应的值并返回旧值
  * @param k
  * @param v
  * @return
  * @author xxx 2017年2月14日 下午9:48:54
  * @since v1.0
  */
 public V put(String k, V v)
 {
  System.out.println("-----插入[" + k + "]之前的跳跃表是:-----");
  printHorizontal();
 
  SkipListEntry<v> p, q;
 
  p = findEntry(k);
 
  if (k.equals(p.getKey()))
  {
   V old = p.value;
   p.value = v;
   return old;
  }
  q = new SkipListEntry<v>(k, v);
  q.left = p;
  q.right = p.right;
  p.right.left = q;
  p.right = q;
  int currentLevel = 0; // 当前层 currentLevel = 0
  // 随机值小于0.5,则插入的键值对对应的键需要在上一层建立关联,同时有可能增加跳表的高度
  while (flag.nextDouble() < 0.5)
  {
   // 如果超出了高度,需要重新建一个顶层
   if (currentLevel >= height)
   {
    SkipListEntry<v> p1, p2;
    height = height + 1;
    p1 = new SkipListEntry<v>(SkipListEntry.negInf, null);
    p2 = new SkipListEntry<v>(SkipListEntry.posInf, null);
    p1.right = p2;
    p1.down = head;
    p2.left = p1;
    p2.down = tail;
    head.up = p1;
    tail.up = p2;
    head = p1;
    tail = p2;
   }
   while (p.up == null)
   {
    p = p.left;
   }
   p = p.up;
 
   SkipListEntry<v> e;
   /*
    * 注意,本实现中只有第0层的链表持有键对应的值,1 ~ height 层中的SkipListEntry对象
    * 仅仅持有键的引用,值为空,以便节省空间。
    */
   e = new SkipListEntry<v>(k, null);
   e.left = p;
   e.right = p.right;
   e.down = q;
   p.right.left = e;
   p.right = e;
   q.up = e;
 
   q = e; // q 进行下一层迭代
   currentLevel = currentLevel + 1; // 当前层 +1
  }
  // 插入一个键值对后总数加1
  size = size + 1;
 
  System.out.println("-----插入[" + k + "]之后的跳跃表是:-----");
  printHorizontal();
  return null;
 }
 /**
  * 根据键删除键值对
  * @param key
  * @return
  * @author xxx 2017年2月14日 下午10:08:17
  * @since v1.0
  */
 public void remove(String key)
 {
  SkipListEntry<v> p = findEntry(key);
 
  if(!p.getKey().equals(key)) {
   return;
  }
  //删除元素后重新关联,同时使被删除的对象游离,便于垃圾回收
  p.left.right = p.right;
  p.right.left = p.left;
  p.right = null;
  p.left = null;
  //自底向上,使所有键等于key的SkipListEntry对象左右两个方向的引用置空
  while(p.up != null) {
   p = p.up;
   p.left.right = p.right;
   p.right.left = p.left;
   p.right = null;
   p.left = null;
  }
  //自顶向下,使所有键等于key的SkipListEntry对象上下两个方向的引用置空
  while(p.down != null) {
   SkipListEntry<v> temp = p.down;
   p.down = null;
   temp.up = null;
   p = temp;
  }
  /*
   * 删除元素后,如果顶层的链表只有head和tail两个元素,则删除顶层。
   * 删除顶层以后最新的顶层如果依然只有head和tail两个元素,则也要被删除,以此类推。
   */
  while(head.right.key == tail.key && height > 0) {
   SkipListEntry<v> p1, p2;
   p1 = head.down;
   p2 = tail.down;
   head.right = null;
   head.down = null;
   tail.left = null;
   tail.down = null;
   p1.up = null;
   p2.up = null;
   head = p1;
   tail = p2;
   height = height - 1;
  }
  //成功移除一个元素,大小减1
  size = size - 1;
  System.out.println("-----删除[" + key + "]后的跳跃表是:-----");
  printHorizontal();
 }
 /**
  * 打印出跳表的图示结构(水平方向)
  * @author xxx 2017年2月14日 下午10:35:36
  * @since v1.0
  */
 public void printHorizontal()
 {
  String s = "";
  int i;
  SkipListEntry<v> p;
  p = head;
  while (p.down != null)
  {
   p = p.down;
  }
  i = 0;
  while (p != null)
  {
   p.pos = i++;
   p = p.right;
  }
  p = head;
  while (p != null)
  {
   s = getOneRow(p);
   System.out.println(s);
   p = p.down;
  }
 }
 private String getOneRow(SkipListEntry<v> p)
 {
  String s;
  int a, b, i;
  a = 0;
  s = "" + p.key;
  p = p.right;
  while (p != null)
  {
   SkipListEntry<v> q;
   q = p;
   while (q.down != null)
    q = q.down;
   b = q.pos;
   s = s + " <-";
   for (i = a + 1; i < b; i++)
    s = s + "--------";
   s = s + "> " + p.key;
   a = b;
   p = p.right;
  }
  return s;
 }
 /**
  * 打印出跳表的图示结构(垂直方向)
  * @author xxx 2017年2月14日 下午10:35:36
  * @since v1.0
  */
 public void printVertical()
 {
  String s = "";
  SkipListEntry<v> p;
  p = head;
  while (p.down != null)
   p = p.down;
  while (p != null)
  {
   s = getOneColumn(p);
   System.out.println(s);
   p = p.right;
  }
 }
 private String getOneColumn(SkipListEntry<v> p)
 {
  String s = "";
  while (p != null)
  {
   s = s + " " + p.key;
   p = p.up;
  }
  return (s);
 }
}

3 Test


public class Test
{
 public static void main(String[] args)
 {
  SkipList<String> s = new SkipList<String>();
  s.put("ABC", "");
  s.put("DEF", "");
  s.put("KLM", "");
  s.put("HIJ", "");
  s.put("GHJ", "");
  s.put("AAA", "");
  s.remove("ABC");
  s.remove("DEF");
  s.remove("KLM");
  s.remove("HIJ");
  s.remove("GHJ");
  s.remove("AAA");
  s.put("ABC", "");
  s.put("DEF", "");
  s.put("KLM", "");
  s.put("HIJ", "");
  s.put("GHJ", "");
  s.put("AAA", "");
 }
}
//运行测试后结果示例如下(注意:由于跳表的特性,每次运行结果都不一样)

-----插入[ABC]之前的跳跃表是:-----
-oo <-> +oo
-----插入[ABC]之后的跳跃表是:-----
-oo <-> ABC <-> +oo
-oo <-> ABC <-> +oo
-----插入[DEF]之前的跳跃表是:-----
-oo <-> ABC <-> +oo
-oo <-> ABC <-> +oo
-----插入[DEF]之后的跳跃表是:-----
-oo <---------> DEF <-> +oo
-oo <-> ABC <-> DEF <-> +oo
-oo <-> ABC <-> DEF <-> +oo
-----插入[KLM]之前的跳跃表是:-----
-oo <---------> DEF <-> +oo
-oo <-> ABC <-> DEF <-> +oo
-oo <-> ABC <-> DEF <-> +oo
-----插入[KLM]之后的跳跃表是:-----
-oo <---------> DEF <-> KLM <-> +oo
-oo <-> ABC <-> DEF <-> KLM <-> +oo
-oo <-> ABC <-> DEF <-> KLM <-> +oo
-----插入[HIJ]之前的跳跃表是:-----
-oo <---------> DEF <-> KLM <-> +oo
-oo <-> ABC <-> DEF <-> KLM <-> +oo
-oo <-> ABC <-> DEF <-> KLM <-> +oo
-----插入[HIJ]之后的跳跃表是:-----
-oo <---------> DEF <---------> KLM <-> +oo
-oo <-> ABC <-> DEF <---------> KLM <-> +oo
-oo <-> ABC <-> DEF <-> HIJ <-> KLM <-> +oo
-----插入[GHJ]之前的跳跃表是:-----
-oo <---------> DEF <---------> KLM <-> +oo
-oo <-> ABC <-> DEF <---------> KLM <-> +oo
-oo <-> ABC <-> DEF <-> HIJ <-> KLM <-> +oo
-----插入[GHJ]之后的跳跃表是:-----
-oo <-----------------> GHJ <-----------------> +oo
-oo <-----------------> GHJ <-----------------> +oo
-oo <-----------------> GHJ <-----------------> +oo
-oo <-----------------> GHJ <-----------------> +oo
-oo <-----------------> GHJ <-----------------> +oo
-oo <-----------------> GHJ <-----------------> +oo
-oo <---------> DEF <-> GHJ <---------> KLM <-> +oo
-oo <-> ABC <-> DEF <-> GHJ <---------> KLM <-> +oo
-oo <-> ABC <-> DEF <-> GHJ <-> HIJ <-> KLM <-> +oo
-----插入[AAA]之前的跳跃表是:-----
-oo <-----------------> GHJ <-----------------> +oo
-oo <-----------------> GHJ <-----------------> +oo
-oo <-----------------> GHJ <-----------------> +oo
-oo <-----------------> GHJ <-----------------> +oo
-oo <-----------------> GHJ <-----------------> +oo
-oo <-----------------> GHJ <-----------------> +oo
-oo <---------> DEF <-> GHJ <---------> KLM <-> +oo
-oo <-> ABC <-> DEF <-> GHJ <---------> KLM <-> +oo
-oo <-> ABC <-> DEF <-> GHJ <-> HIJ <-> KLM <-> +oo
-----插入[AAA]之后的跳跃表是:-----
-oo <-------------------------> GHJ <-----------------> +oo
-oo <-------------------------> GHJ <-----------------> +oo
-oo <-------------------------> GHJ <-----------------> +oo
-oo <-------------------------> GHJ <-----------------> +oo
-oo <-------------------------> GHJ <-----------------> +oo
-oo <-> AAA <-----------------> GHJ <-----------------> +oo
-oo <-> AAA <---------> DEF <-> GHJ <---------> KLM <-> +oo
-oo <-> AAA <-> ABC <-> DEF <-> GHJ <---------> KLM <-> +oo
-oo <-> AAA <-> ABC <-> DEF <-> GHJ <-> HIJ <-> KLM <-> +oo
-----删除[ABC]后的跳跃表是:-----
-oo <-----------------> GHJ <-----------------> +oo
-oo <-----------------> GHJ <-----------------> +oo
-oo <-----------------> GHJ <-----------------> +oo
-oo <-----------------> GHJ <-----------------> +oo
-oo <-----------------> GHJ <-----------------> +oo
-oo <-> AAA <---------> GHJ <-----------------> +oo
-oo <-> AAA <-> DEF <-> GHJ <---------> KLM <-> +oo
-oo <-> AAA <-> DEF <-> GHJ <---------> KLM <-> +oo
-oo <-> AAA <-> DEF <-> GHJ <-> HIJ <-> KLM <-> +oo
-----删除[DEF]后的跳跃表是:-----
-oo <---------> GHJ <-----------------> +oo
-oo <---------> GHJ <-----------------> +oo
-oo <---------> GHJ <-----------------> +oo
-oo <---------> GHJ <-----------------> +oo
-oo <---------> GHJ <-----------------> +oo
-oo <-> AAA <-> GHJ <-----------------> +oo
-oo <-> AAA <-> GHJ <---------> KLM <-> +oo
-oo <-> AAA <-> GHJ <---------> KLM <-> +oo
-oo <-> AAA <-> GHJ <-> HIJ <-> KLM <-> +oo
-----删除[KLM]后的跳跃表是:-----
-oo <---------> GHJ <---------> +oo
-oo <---------> GHJ <---------> +oo
-oo <---------> GHJ <---------> +oo
-oo <---------> GHJ <---------> +oo
-oo <---------> GHJ <---------> +oo
-oo <-> AAA <-> GHJ <---------> +oo
-oo <-> AAA <-> GHJ <---------> +oo
-oo <-> AAA <-> GHJ <---------> +oo
-oo <-> AAA <-> GHJ <-> HIJ <-> +oo
-----删除[HIJ]后的跳跃表是:-----
-oo <---------> GHJ <-> +oo
-oo <---------> GHJ <-> +oo
-oo <---------> GHJ <-> +oo
-oo <---------> GHJ <-> +oo
-oo <---------> GHJ <-> +oo
-oo <-> AAA <-> GHJ <-> +oo
-oo <-> AAA <-> GHJ <-> +oo
-oo <-> AAA <-> GHJ <-> +oo
-oo <-> AAA <-> GHJ <-> +oo
-----删除[GHJ]后的跳跃表是:-----
-oo <-> AAA <-> +oo
-oo <-> AAA <-> +oo
-oo <-> AAA <-> +oo
-oo <-> AAA <-> +oo
-----删除[AAA]后的跳跃表是:-----
-oo <-> +oo
-----插入[ABC]之前的跳跃表是:-----
-oo <-> +oo
-----插入[ABC]之后的跳跃表是:-----
-oo <-> ABC <-> +oo
-----插入[DEF]之前的跳跃表是:-----
-oo <-> ABC <-> +oo
-----插入[DEF]之后的跳跃表是:-----
-oo <---------> DEF <-> +oo
-oo <---------> DEF <-> +oo
-oo <---------> DEF <-> +oo
-oo <---------> DEF <-> +oo
-oo <-> ABC <-> DEF <-> +oo
-----插入[KLM]之前的跳跃表是:-----
-oo <---------> DEF <-> +oo
-oo <---------> DEF <-> +oo
-oo <---------> DEF <-> +oo
-oo <---------> DEF <-> +oo
-oo <-> ABC <-> DEF <-> +oo
-----插入[KLM]之后的跳跃表是:-----
-oo <---------> DEF <---------> +oo
-oo <---------> DEF <---------> +oo
-oo <---------> DEF <---------> +oo
-oo <---------> DEF <---------> +oo
-oo <-> ABC <-> DEF <-> KLM <-> +oo
-----插入[HIJ]之前的跳跃表是:-----
-oo <---------> DEF <---------> +oo
-oo <---------> DEF <---------> +oo
-oo <---------> DEF <---------> +oo
-oo <---------> DEF <---------> +oo
-oo <-> ABC <-> DEF <-> KLM <-> +oo
-----插入[HIJ]之后的跳跃表是:-----
-oo <---------> DEF <-----------------> +oo
-oo <---------> DEF <-----------------> +oo
-oo <---------> DEF <-----------------> +oo
-oo <---------> DEF <-> HIJ <---------> +oo
-oo <-> ABC <-> DEF <-> HIJ <-> KLM <-> +oo
-----插入[GHJ]之前的跳跃表是:-----
-oo <---------> DEF <-----------------> +oo
-oo <---------> DEF <-----------------> +oo
-oo <---------> DEF <-----------------> +oo
-oo <---------> DEF <-> HIJ <---------> +oo
-oo <-> ABC <-> DEF <-> HIJ <-> KLM <-> +oo
-----插入[GHJ]之后的跳跃表是:-----
-oo <---------> DEF <-------------------------> +oo
-oo <---------> DEF <-------------------------> +oo
-oo <---------> DEF <-------------------------> +oo
-oo <---------> DEF <---------> HIJ <---------> +oo
-oo <-> ABC <-> DEF <-> GHJ <-> HIJ <-> KLM <-> +oo
-----插入[AAA]之前的跳跃表是:-----
-oo <---------> DEF <-------------------------> +oo
-oo <---------> DEF <-------------------------> +oo
-oo <---------> DEF <-------------------------> +oo
-oo <---------> DEF <---------> HIJ <---------> +oo
-oo <-> ABC <-> DEF <-> GHJ <-> HIJ <-> KLM <-> +oo
-----插入[AAA]之后的跳跃表是:-----
-oo <-----------------> DEF <-------------------------> +oo
-oo <-----------------> DEF <-------------------------> +oo
-oo <-----------------> DEF <-------------------------> +oo
-oo <-----------------> DEF <---------> HIJ <---------> +oo
-oo <-> AAA <-> ABC <-> DEF <-> GHJ <-> HIJ <-> KLM <-> +oo

Summary

The above is the detailed content of Example sharing of Java implementation of skip list. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn