SkipList

Redis中使用跳錶做爲有序集合鍵(zset)的底層實現之一。當有序集合元素較多時,或有序集合中元素爲較長字符串時,都會使用跳錶做爲底層結構。java

Redis在兩個地方用到了跳錶,一種是有序集合鍵中,另外一種是集羣節點中作內部數據結構。node

 

跳錶

下面的結構是就是跳錶:數據結構

 其中 -1 表示 INT_MIN, 鏈表的最小值,1 表示 INT_MAX,鏈表的最大值。app

 

跳錶具備以下性質:less

(1) 由不少層結構組成dom

(2) 每一層都是一個有序的鏈表ide

(3) 最底層(Level 1)的鏈表包含全部元素 越接近頂層的鏈表,含有的節點則越少  (通常是作爲索引層)函數

(4) 若是一個元素出如今 Level i 的鏈表中,則它在 Level i 之下的鏈表也都會出現。測試

(5) 每一個節點包含4個指針,一個指向同一鏈表中的下一個元素,一個指向下面一層的元素。一個指向上面一層的元素,一個指向同一鏈表中的上一個元素ui

 

 

package com.high_structure;

import java.util.Random;

public class MySkipList<K extends Comparable<K>, V> {
	// 該類應該的有的屬性 size head節點
	private int size;
	private Node<K, V> head;

	// 由於咱們會經過隨機數機率的方式去生成是否生成索引層
	private final Random random = new Random();
	//
	private final double DEFAULT_PROBABILITY = 0.5;

	public MySkipList() {
		super();
		this.size = 0;
		this.head = new Node<K, V>(null, null, 0);
	}

	// 提供幾個基本方法 方便後期去操做鏈表 1 判斷是否爲空 2水平插入到某個元素的後面 3 垂直插入(上下關聯)

	public boolean isEmpty() {
		return size == 0;
	}

	/**
	 * 吧b插入到a的後面<br>
	 * a->c->d <br>
	 * b
	 * 
	 * @param a
	 * @param b
	 */
	public void horizontalLink(Node<K, V> a, Node<K, V> b) {
		b.setPre(a);
		b.setNext(a.getNext());
		if (a.getNext() != null) // 創建從右到左的連接關係
		{
			// 在這裏a.getnext == b。getnext的
			a.getNext().setPre(b);
		}
		a.setNext(b);
	}

	/**
	 * 吧b插入到a下面
	 * 
	 * @param a
	 * @param b
	 */
	public void vertLink(Node<K, V> a, Node<K, V> b) {
		a.setDown(b);
		b.setUp(a);
	}

	public static void main(String[] args) {
		MySkipList<Integer, String> skipList = new MySkipList<>();
		skipList.add(151, "1");
		skipList.add(12, "12");

		skipList.add(121, "121");
		skipList.add(112, "112");
		skipList.add(11, "11");
		skipList.remove(12);
		skipList.remove(121);
		skipList.remove(15);
		System.out.println(skipList);
	}

	/**
	 * 判斷key1是否小於key2
	 * 
	 * @param key1
	 * @param key2
	 * @return
	 */
	public boolean lessthanOrEquals(K key1, K key2) {
		return key1.compareTo(key2) <= 0;
	}

	public Node<K, V> get(K key) {
		// 省略key的判斷
		Node<K, V> node = searchNode(key);
		if (node.getKey().equals(key)) {
			return node;
		}
		return null;
	}

	/**
	 * 經過指定的key查找元素
	 * 
	 * @param key
	 * @return
	 */
	public Node<K, V> searchNode(K key) {
		// 該node表示最後返回的節點
		Node<K, V> node = head;
		// 該節點表示遍歷會用到的節點
		Node<K, V> next = null;
		Node<K, V> down = null;
		// 先在從head開始查找

		while (true) {
			next = node.getNext();
			// 先遍歷第一層節點
			while (next != null && lessthanOrEquals(next.getKey(), key))
			// 若是不爲空 而且遍歷的節點<=當前節點
			{
				node = next;
				next = next.getNext();
			}
			if (node.getKey() != null && node.getKey().compareTo(key) == 0) {
				break;
			}

			// 若是不相等 進入下一層級
			down = node.getDown();
			if (down == null) // 最後一層
			{
				break;
			} else {
				node = down;
			}
		}
		return node;
	}

	/**
	 * 添加元素 若是 key存在 就修改該node的value 若是不存在 就添加到他應該的位置
	 * 
	 * @param node
	 */
	public void add(K key, V val) {
		// 省略判斷key是否合法
		Node<K, V> node = searchNode(key);
		K searchKey = node.getKey();
		if (searchKey != null && searchKey.compareTo(key) == 0) {
			node.setValue(val);
			return;
		}
		// 若是不相等 那麼咱們就把咱們的節點插入到該節點的後面
		Node<K, V> newNode = new Node<K, V>(key, val, node.getLevel());
		horizontalLink(node, newNode);
		// 經過隨機函數的的方式看是否須要生成索引層 【若是查找的數和咱們 的head在同一層在同一級就須要生成 】
		int headLevel = head.getLevel();
		int currentLevel = node.getLevel();
		while (isNeedBuildLevel()) {
			if (currentLevel >= headLevel) { // 須要建立的心的索引層頭結點
				Node<K, V> newHead = new Node<K, V>(null, null, ++headLevel);
				vertLink(newHead, head);
				head = newHead;
			}
//			結合跳錶的圖更好理解
//			若是當前節點(node)的up爲空  咱們就跳轉到當前節點的head節點 而後 獲取up
//			若是不爲空直接獲取up 而後在up後面添加當前節點做爲索引層
			while (node.getUp() == null) {
				node = node.getPre();
			}
			node = node.getUp();
			Node<K, V> newTemp = new Node<K, V>(key, val, node.getLevel());
			horizontalLink(node, newTemp);
			vertLink(newTemp, newNode);

              //由於咱們生成索引層是隨機的 有可能在生成了一個索引層之後又會生成一個
              //好比 如今有2層
              // leav1 0--> 1
              //                  ||
              // leav0 0--> 1  假如又生成一個新的索引層 咱們實際上是須要記錄上一次索引層的位置 也就是 tmp的值 咱們把他保存在newNode 下次使用

			newNode = newTemp;
			currentLevel++;
		}
		size++;
	}

	/**
	 *
	 * 刪除的核心思路 先找到該節點 而後走到最下面 從下面開始刪除 (從上往下刪除 )
	 *
	 * @param key
	 */
	public void remove(K key) {
		// 省略判斷key是否合法
		Node<K, V> node = searchNode(key);
		// 刪除
		if (node != null && node.getKey().equals(key)) {
			while (node.getDown() != null) {
				node = node.getDown();
			}

			Node<K, V> next = null;

			Node<K, V> pre = null;
			while (node != null) {
				pre = node.getPre();
				next = node.getNext();
				if (pre != null) {
					pre.setNext(next);
				}
				if (next != null) {
					next.setPre(pre);
				}
				node = node.getUp();
			}
			// 對頂層鏈表進行調整,去除無效的頂層鏈表 某些時候可能存在某一行索引就一個單獨的頭結點 該頭結點的next沒有值
			while (head.getNext() == null && head.getDown() != null) {
				head = head.getDown();
				head.setUp(null);
			}
		}
		size--;
	}

	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();
		Node<K, V> node = head;

		// 移動到最底層
		while (node.getDown() != null)
			node = node.getDown();

		while (node.getPre() != null)
			node = node.getPre();

		// 第一個節點是頭部節點,沒有任何意義,因此須要移動到後一個節點
		if (node.getNext() != null)
			node = node.getNext();

		// 遍歷
		while (node != null) {
			sb.append(node.toString()).append("\n");
			node = node.getNext();
		}

		return sb.toString();
	}

	private boolean isNeedBuildLevel() {
		return random.nextDouble() < DEFAULT_PROBABILITY;
	}

	// 節點類型 採用內部類的方式
	static class Node<K, V> {
		private K key;
		private V value;
		// 還須要一個值來保存該節點所在的層級
		private int level;

		public int getLevel() {
			return level;
		}

		public void setLevel(int level) {
			this.level = level;
		}

		private Node<K, V> up, down, next, pre;

		public K getKey() {
			return key;
		}

		public void setKey(K key) {
			this.key = key;
		}

		public V getValue() {
			return value;
		}

		public void setValue(V value) {
			this.value = value;
		}

		public Node<K, V> getUp() {
			return up;
		}

		public void setUp(Node<K, V> up) {
			this.up = up;
		}

		public Node<K, V> getDown() {
			return down;
		}

		public void setDown(Node<K, V> down) {
			this.down = down;
		}

		public Node<K, V> getNext() {
			return next;
		}

		public void setNext(Node<K, V> next) {
			this.next = next;
		}

		public Node<K, V> getPre() {
			return pre;
		}

		public void setPre(Node<K, V> pre) {
			this.pre = pre;
		}

		public Node(K key, V value, int level) {
			super();
			this.key = key;
			this.value = value;
			this.level = level;
		}

		public Node() {

		}

		@Override
		public String toString() {
			return "Node [key=" + key + ", value=" + value + "]";
		}

	}
}

最後的測試結果以下

相關文章
相關標籤/搜索