手写一个简易跳表(Java版)

跳表概述

跳表是由William Pugh于1990年发明,他在论文中给出了跳表的描述:

Skip lists are a data structure that can be used in place of balanced trees. Skip lists use probabilistic balancing rather than strictly enforced balancing and as a result the algorithms for insertion and deletion in skip lists are much simpler and significantly faster than equivalent algorithms for balanced trees.

跳表是平衡树的一种替代的数据结构,但是和红黑树不相同的是,跳表对于树的平衡的实现是基于一种随机化的算法的,这样也就是说跳表的插入和删除的工作是比较简单的。

与单向链表不同,每一个结点不单单只包含指向下一个结点的指针,可能包含很多个指向后续结点的指针,这样就可以跳过一些不必要的结点,从而加快查找、删除等操作。对于一个链表内每一个结点包含多少个指向后续元素的指针,后续节点个数是通过一个随机函数生成器得到,这样子就构成了一个跳跃表。

随机生成的跳跃表如下图所示:

这基本上就是跳表的核心思想,其实也是一种通过空间来换取时间的一个算法,通过在每个节点中增加了向前的指针,从而提升查找的效率。目前开源软件 Redis 和 LevelDB 都有用到它。

其增删改查的时间复杂度为O(log n).

跳表的Java简易实现

节点

如果一个节点存在k个向前的指针的话,那么称该节点是k层的节点。

一个跳表的MaxLevel为跳表中所有节点中最大的层数。

public static class SkipListNode {
	public Integer value;
	public ArrayList<SkipListNode> nextNodes;

	public SkipListNode(Integer value) {
		this.value = value;
		nextNodes = new ArrayList<SkipListNode>();
	}
}

跳表数据类型

public static class SkipList {
	private SkipListNode head;
	private int maxLevel;
	private int size;
	private static final double PROBABILITY = 0.5;
}

PROBABILITY表示生成下一层的概率

初始化

public static class SkipList {
	private SkipListNode head;
	private int maxLevel;
	private int size;
	private static final double PROBABILITY = 0.5;
	
    public SkipList() {
		size = 0;
		maxLevel = 0;
		head = new SkipListNode(null);
		head.nextNodes.add(null);
	}
    
	public SkipListNode getHead() {
		return head;
	}
}

查找操作

以查找19为例,先找到6,再找到25,19比25小,在6和25之间;再找到9,再找到17,再找到25,19比25小,在17和25节点之间,17再下一个即为19。

public static class SkipList {
	private SkipListNode head;
	private int maxLevel;
	private int size;
	private static final double PROBABILITY = 0.5;
    
    // Returns the skiplist node with greatest value <= e
	private SkipListNode find(Integer e) {
		return find(e, head, maxLevel);
	}

	// Returns the skiplist node with greatest value <= e
	// Starts at node start and level
	private SkipListNode find(Integer e, SkipListNode current, int level) {
		do {
			current = findNext(e, current, level);
		} while (level-- > 0);
		return current;
	}

	// Returns the node at a given level with highest value less than e
    // 关键方法:找到给定level中value小于e的最大节点
	private SkipListNode findNext(Integer e, SkipListNode current, int level) {
		SkipListNode next = current.nextNodes.get(level);
		while (next != null) {
			Integer value = next.value;
			if (lessThan(e, value)) { // e < value
				break;
			}
			current = next;
			next = current.nextNodes.get(level);
		}
		return current;
	}
    
    private boolean lessThan(Integer a, Integer b) {
		return a.compareTo(b) < 0;
	}

	private boolean equalTo(Integer a, Integer b) {
		return a.compareTo(b) == 0;
	}
}

插入操作

由于跳表数据结构整体上是有序的,所以在插入时,需要首先查找到合适的位置,然后就是修改指针(和链表中操作类似),然后更新跳表的level变量

public static class SkipList {
	private SkipListNode head;
	private int maxLevel;
	private int size;
	private static final double PROBABILITY = 0.5;
    
    public void add(Integer newValue) {
		if (!contains(newValue)) {
			size++;
			int level = 0;
			while (Math.random() < PROBABILITY) {
				level++;
			}
			while (level > maxLevel) {
				head.nextNodes.add(null);
				maxLevel++;
			}
			SkipListNode newNode = new SkipListNode(newValue);
			SkipListNode current = head;
			do {
				current = findNext(newValue, current, level);
				newNode.nextNodes.add(0, current.nextNodes.get(level));
				current.nextNodes.set(level, newNode);
			} while (level-- > 0);
		}
	}
    
    public boolean contains(Integer value) {
		SkipListNode node = find(value);
		return node != null && node.value != null && equalTo(node.value, value);
	}
}

删除操作

删除操作类似于插入操作,包含如下3步:查找到需要删除的结点、删除结点、调整指针。

public static class SkipList {
	private SkipListNode head;
	private int maxLevel;
	private int size;
	private static final double PROBABILITY = 0.5;
    
    public void delete(Integer deleteValue) {
		if (contains(deleteValue)) {
			SkipListNode deleteNode = find(deleteValue);
			size--;
			int level = maxLevel;
			SkipListNode current = head;
			do {
				current = findNext(deleteNode.value, current, level);
				if (deleteNode.nextNodes.size() > level) {
					current.nextNodes.set(level, deleteNode.nextNodes.get(level));
				}
			} while (level-- > 0);
		}
	}
}

遍历

遍历第0层的节点即可。

public static class SkipList {
	private SkipListNode head;
	private int maxLevel;
	private int size;
	private static final double PROBABILITY = 0.5;
    
    // Inner Class
    public static class SkipListIterator implements Iterator<Integer> {
		SkipList list;
		SkipListNode current;

		public SkipListIterator(SkipList list) {
			this.list = list;
			this.current = list.getHead();
		}

		public boolean hasNext() {
			return current.nextNodes.get(0) != null;
		}

		public Integer next() {
			current = current.nextNodes.get(0);
			return current.value;
		}
	}
    
    public Iterator<Integer> iterator() {
		return new SkipListIterator(this);
	}
}

全部代码

import java.util.ArrayList;
import java.util.Iterator;

public class SkipList_Simple {

	public static class SkipListNode {
		public Integer value;
		public ArrayList<SkipListNode> nextNodes;

		public SkipListNode(Integer value) {
			this.value = value;
			nextNodes = new ArrayList<SkipListNode>();
		}
	}

	public static class SkipListIterator implements Iterator<Integer> {
		SkipList list;
		SkipListNode current;

		public SkipListIterator(SkipList list) {
			this.list = list;
			this.current = list.getHead();
		}

		public boolean hasNext() {
			return current.nextNodes.get(0) != null;
		}

		public Integer next() {
			current = current.nextNodes.get(0);
			return current.value;
		}
	}

	public static class SkipList {
		private SkipListNode head;
		private int maxLevel;
		private int size;
		private static final double PROBABILITY = 0.5;

		public SkipList() {
			size = 0;
			maxLevel = 0;
			head = new SkipListNode(null);
			head.nextNodes.add(null);
		}

		public SkipListNode getHead() {
			return head;
		}

		public void add(Integer newValue) {
			if (!contains(newValue)) {
				size++;
				int level = 0;
				while (Math.random() < PROBABILITY) {
					level++;
				}
				while (level > maxLevel) {
					head.nextNodes.add(null);
					maxLevel++;
				}
				SkipListNode newNode = new SkipListNode(newValue);
				SkipListNode current = head;
				do {
					current = findNext(newValue, current, level);
					newNode.nextNodes.add(0, current.nextNodes.get(level));
					current.nextNodes.set(level, newNode);
				} while (level-- > 0);
			}
		}

		public void delete(Integer deleteValue) {
			if (contains(deleteValue)) {
				SkipListNode deleteNode = find(deleteValue);
				size--;
				int level = maxLevel;
				SkipListNode current = head;
				do {
					current = findNext(deleteNode.value, current, level);
					if (deleteNode.nextNodes.size() > level) {
						current.nextNodes.set(level, deleteNode.nextNodes.get(level));
					}
				} while (level-- > 0);
			}
		}

		// Returns the skiplist node with greatest value <= e
		private SkipListNode find(Integer e) {
			return find(e, head, maxLevel);
		}

		// Returns the skiplist node with greatest value <= e
		// Starts at node start and level
		private SkipListNode find(Integer e, SkipListNode current, int level) {
			do {
				current = findNext(e, current, level);
			} while (level-- > 0);
			return current;
		}

		// Returns the node at a given level with highest value less than e
		private SkipListNode findNext(Integer e, SkipListNode current, int level) {
			SkipListNode next = current.nextNodes.get(level);
			while (next != null) {
				Integer value = next.value;
				if (lessThan(e, value)) { // e < value
					break;
				}
				current = next;
				next = current.nextNodes.get(level);
			}
			return current;
		}

		public int size() {
			return size;
		}

		public boolean contains(Integer value) {
			SkipListNode node = find(value);
			return node != null && node.value != null && equalTo(node.value, value);
		}

		public Iterator<Integer> iterator() {
			return new SkipListIterator(this);
		}

		/******************************************************************************
		 * Utility Functions *
		 ******************************************************************************/

		private boolean lessThan(Integer a, Integer b) {
			return a.compareTo(b) < 0;
		}

		private boolean equalTo(Integer a, Integer b) {
			return a.compareTo(b) == 0;
		}
	}

	public static void main(String[] args) {

	}
}

ConcurrentSkipListMap

ConcurrentSkipListMap是JDK1.6以后提供的一个并发的SkipList。

从上面的构造我们可以看到,Index节点包含了Node节点,除此之外,Index还有两个指针,一个指向同一个layer的下一个节点right,一个指向下一层layer的节点down。采用自旋+CAS的方式进行增删改查。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
(Skip List)是一种随机化的数据结构,它基于并联的链,用于维护一个有序的元素序列。支持快速的插入、删除和查找操作,时间复杂度为 O(log n),与平衡树相当,但实现起来比平衡树简单。 下面是用 Java 实现的代码: ```java import java.util.Random; public class SkipList { private static final int MAX_LEVEL = 16; // 的最大层数 private int levelCount = 1; // 的当前层数 private Node head = new Node(); // 的头结点 private Random r = new Random(); // 随机数生成器 // 查找操作 public Node find(int value) { Node p = head; // 从的顶层开始查找 for (int i = levelCount - 1; i >= 0; i--) { while (p.forwards[i] != null && p.forwards[i].data < value) { p = p.forwards[i]; } } // 如果找到了目标值,则返回该节点,否则返回 null if (p.forwards[0] != null && p.forwards[0].data == value) { return p.forwards[0]; } else { return null; } } // 插入操作 public void insert(int value) { int level = randomLevel(); Node newNode = new Node(); newNode.data = value; newNode.maxLevel = level; Node[] update = new Node[level]; for (int i = 0; i < level; i++) { update[i] = head; } Node p = head; for (int i = level - 1; i >= 0; i--) { while (p.forwards[i] != null && p.forwards[i].data < value) { p = p.forwards[i]; } update[i] = p; } for (int i = 0; i < level; i++) { newNode.forwards[i] = update[i].forwards[i]; update[i].forwards[i] = newNode; } if (levelCount < level) { levelCount = level; } } // 删除操作 public void delete(int value) { Node[] update = new Node[levelCount]; Node p = head; for (int i = levelCount - 1; i >= 0; i--) { while (p.forwards[i] != null && p.forwards[i].data < value) { p = p.forwards[i]; } update[i] = p; } if (p.forwards[0] != null && p.forwards[0].data == value) { for (int i = levelCount - 1; i >= 0; i--) { if (update[i].forwards[i] != null && update[i].forwards[i].data == value) { update[i].forwards[i] = update[i].forwards[i].forwards[i]; } } } } // 随机生成层数 private int randomLevel() { int level = 1; while (r.nextDouble() < 0.5 && level < MAX_LEVEL) { level++; } return level; } // 内部节点类 class Node { private int data = -1; private Node[] forwards = new Node[MAX_LEVEL]; private int maxLevel = 0; @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append("{ data: "); builder.append(data); builder.append("; levels: "); builder.append(maxLevel); builder.append(" }"); return builder.toString(); } } // 打印中的所有节点 public void printAll() { Node p = head; while (p.forwards[0] != null) { System.out.println(p.forwards[0] + " "); p = p.forwards[0]; } System.out.println(); } } ``` 在中,每个节点包含了一个数据域和一个数组,数组中存储了该节点在不同层级上的后继节点。在插入节点时,我们随机生成该节点的层数,并将其插入到每一层中适当的位置。在删除节点时,我们需要先查找到该节点的前驱节点,然后将其从每一层中删除即可。 这是一个简单的实现,你可以根据自己的需求进行修改和扩展。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值