【Java】跳躍表的實現以及用例測試

庫北勒神發表於2021-01-04

跳躍表
思想不再贅述,直接貼程式碼:
SkipListNode.java

package com.kuber.algorithmtest.skiplist;

public class SkipListNode <T>{
    private int key;
    private T value;
    public SkipListNode<T> up, down, left, right; // 上下左右 四個指標

    public static final int HEAD_KEY = Integer.MIN_VALUE; // 負無窮
    public static final int  TAIL_KEY = Integer.MAX_VALUE; // 正無窮

    public SkipListNode(int k, T v) {
        key = k;
        value = v;
    }
    public int getKey() {
        return key;
    }
    public void setKey(int key) {
        this.key = key;
    }
    public T getValue() {
        return value;
    }
    public void setValue(T value) {
        this.value = value;
    }
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null) {
            return false;
        }
        if (!(o instanceof SkipListNode<?>)) {
            return false;
        }
        SkipListNode<T> ent;
        try {
            ent = (SkipListNode<T>) o; // 檢測型別
        } catch (ClassCastException ex) {
            return false;
        }
        return (ent.getKey() == key) && (ent.getValue() == value);
    }
    @Override
    public String toString() {
        return "key=value:" + key + "=" + value;
    }
}

SkipList.java:

package com.kuber.algorithmtest.skiplist;

import java.util.Random;


public class SkipList <T>{

    private SkipListNode<T> head,tail;
    private int nodes; // 節點總數
    private int listLevel; // 最大層數
    private Random random; // 隨機數,用於投擲硬幣決定是否要加層高
    private static final double PROBABILITY = 0.25; // 向上提升一個的概率(此處採用redis中的預設值)
    private static final int MAXLEVEL = 32; // 最大層高(此處採用redis中的預設值)

    public SkipList() {
        random = new Random();
        clear();
    }

    /**
     * @Description: 清空跳躍表
     * @Param: []
     * @return: void
     * @Author: kuber
     */
    public void clear(){
        head = new SkipListNode<T>(SkipListNode.HEAD_KEY, null);
        tail = new SkipListNode<T>(SkipListNode.TAIL_KEY, null);
        horizontalLink(head, tail);
        listLevel = 0;
        nodes = 0;
    }

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

    public int size() {
        return nodes;
    }

    /**
     * @Description: 找到要插入的位置前面的那個key 的最底層節點
     * @Param: [key]
     * @return: com.jet.SkipListNode<T>
     * @Author: kuber
     */
    private SkipListNode<T> findNode(int key){
        SkipListNode<T> p = head;
        while(true){
            while (p.right.getKey() != SkipListNode.TAIL_KEY && p.right.getKey() <= key) {
                p = p.right;
            }
            if (p.down != null) {
                p = p.down;
            } else {
                break;
            }

        }
        return p;
    }

    /**
     * @Description: 查詢是否存在key,存在則返回該節點,否則返回null
     * @Param: [key]
     * @return: com.wailian.SkipListNode<T>
     * @Author: kuber
     */
    public SkipListNode<T> search(int key){
        SkipListNode<T> p = findNode(key);
        if (key == p.getKey()) {
            return p;
        } else {
            return null;
        }
    }

    /**
     * @Description: 向跳躍表中新增key-value
     * @Param: [k, v]
     * @return: void
     * @Author: kuber
     */
    public void put(int k,T v){
        SkipListNode<T> p = findNode(k);
        // 如果key值相同,替換原來的value即可結束
        if (k == p.getKey()) {
            p.setValue(v);
            return;
        }
        SkipListNode<T> q = new SkipListNode<>(k, v);
        backLink(p, q);
        int currentLevel = 0; // 當前所在的層級是0
        // 拋硬幣
        while (random.nextDouble() < PROBABILITY && currentLevel < MAXLEVEL) {
            // 如果超出了高度,需要重新建一個頂層
            if (currentLevel >= listLevel) {
                listLevel++;
                SkipListNode<T> p1 = new SkipListNode<>(SkipListNode.HEAD_KEY, null);
                SkipListNode<T> p2 = new SkipListNode<>(SkipListNode.TAIL_KEY, null);
                horizontalLink(p1, p2);
                verticalLink(p1, head);
                verticalLink(p2, tail);
                head = p1;
                tail = p2;
            }
            // 將p移動到上一層
            while (p.up == null) {
                p = p.left;
            }
            p = p.up;

            SkipListNode<T> e = new SkipListNode<>(k, null); // 只儲存key就ok
            backLink(p, e); // 將e插入到p的後面
            verticalLink(e, q); // 將e和q上下連線
            q = e;
            currentLevel++;
        }
        nodes++; // 層數遞增
    }

    /**
     * @Description: node1後面插入node2
     * @Param: [node1, node2]
     * @return: void
     * @Author: kuber
     */
    private void backLink(SkipListNode<T> node1,SkipListNode<T> node2){
        node2.left = node1;
        node2.right = node1.right;
        node1.right.left = node2;
        node1.right = node2;
    }

    /**
     * @Description: 水平雙向連線
     * @Param: [node1, node2]
     * @return: void
     * @Author: kuber
     */
    private void horizontalLink(SkipListNode<T> node1,SkipListNode<T> node2){
        node1.right = node2;
        node2.left = node1;
    }

    /**
     * @Description: 垂直雙向連線
     * @Param: [node1, node2]
     * @return: void
     * @Author: kuber
     */
    private void verticalLink(SkipListNode<T> node1, SkipListNode<T> node2){
        node1.down = node2;
        node2.up = node1;
    }

    @Override
    public String toString() {
        if (isEmpty()) {
            return "跳躍表為空!!!";
        }
        StringBuilder builder = new StringBuilder();
        SkipListNode<T> p=head;
        while (p.down != null) {
            p = p.down;
        }

        while (p.left != null) {
            p = p.left;
        }
        if (p.right!= null) {
            p = p.right;
        }
        while (p.right != null) {
            builder.append(p).append("\n");
            p = p.right;
        }

        return builder.toString();
    }

}

junit單元測試:

package com.kuber.algorithmtest.skiplist;

import org.junit.Test;

public class SkipListTest {

    @Test
    public void test1(){
        SkipList<String> list = new SkipList<>();
        System.out.println(list);
        System.out.println("新增資料:");
        list.put(6, "!!!");
        list.put(1, "Ni Wenhan");
        list.put(2, "make");
        list.put(3, "SkipList");
        list.put(1, "kuber");
        list.put(4, "come true");
        list.put(5, "successfully");
        System.out.println("遍歷表:");
        System.out.println(list);
        System.out.println("表長為:"+list.size());
    }
}

在這裡插入圖片描述

相關文章