【Java】跳躍表的實現以及用例測試
跳躍表
思想不再贅述,直接貼程式碼:
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());
}
}
相關文章
- 【Redis】跳躍表原理分析與基本程式碼實現(java)RedisJava
- redis 跳躍表Redis
- 5分鐘瞭解Redis的內部實現跳躍表(skiplist)Redis
- 測試——水杯的測試用例
- 測試用例最佳實踐
- Redis資料結構—跳躍表 skiplist 實現原始碼分析Redis資料結構原始碼
- Cookie和Session的區別以及設計測試用例CookieSession
- 線上求助,有沒有大佬能講解一波redis 跳躍列表以及跳躍表的curd和元素排名Redis
- 測試用例的方法
- 測試面試-測試用例面試
- 測試用例
- 手工測試用例與自動化測試用例的區別
- 【黑盒測試】測試用例的常用方法
- 測試用例和測試方法
- 邂逅 CODING ,墨刀測試團隊實現「質」的飛躍
- 測試用例—教室
- 【5】測試用例
- 資料結構(一)--- 跳躍表資料結構
- Redis資料結構—跳躍表Redis資料結構
- 如何寫好測試用例以及go單元測試工具testify簡單介紹Go
- 軟體測試用例設計方法-判定表法
- 針對 “測試用例最佳實踐” 的說明
- 【功能測試】兩部電梯的測試用例
- APP測試設計測試用例的要點APP
- 萬能測試用例及測試用例編寫方法(待更新)
- 【pytest官方文件】解讀Skipping test functions,跳過測試用例詳解Function
- 自動化測試實戰技巧:「用例失敗重試機制」實現方案分享
- 使用Java JUnit框架裡的@SuiteClasses註解管理測試用例Java框架UI
- 試著跳一下?講講遊戲中的“跳躍”遊戲
- 介面測試平臺-66: 多介面用例實現之 小用例:新增+刪除+關閉+排序排序
- postman寫測試用例Postman
- 黑盒測試用例二
- 面經-測試用例
- Redis為什麼要使用跳躍表Redis
- Redis原始碼解析之跳躍表(一)Redis原始碼
- Redis原始碼解析之跳躍表(三)Redis原始碼
- 探索性測試的分類與測試用例
- 非平臺化的測試框架怎麼實現用例更新不影響正在執行的測試任務的?框架