今天略微有些晚了,我就暫時不講解什麼是跳錶了,先上我實現跳錶的Java代碼以及測試結果,空了再補上
我自己用Java實現的跳錶類:SkipList.java
import java.util.ArrayList;
import java.util.List;
/**
* @Author: LiYang
* @Date: 2020/3/6 23:57
* @Description: 跳錶的實現(增加、刪除和查找,查找是O(logN)的時間複雜度)
*/
public class SkipList {
/**
* 跳錶的結點類
*/
private static class Node {
//跳錶結點的數據
private Integer data;
//跳錶結點的左結點
private Node left;
//跳錶結點的右結點
private Node right;
//跳錶結點的下級結點
private Node down;
/**
* 無參構造函數
*/
public Node() {
}
/**
* 全參數構造函數
* @param data 結點值
* @param left 左結點
* @param right 右結點
* @param down 下級結點
*/
public Node(Integer data, Node left, Node right, Node down) {
this.data = data;
this.left = left;
this.right = right;
this.down = down;
}
/**
* 重寫toString爲展示數據
* @return
*/
@Override
public String toString() {
//如果有數據
if (this.data != null) {
//就展示數據
return String.valueOf(this.data);
//如果沒有數據
} else {
//就直接返回null表示
return "null";
}
}
/**
* 打印跳錶結點自身和右邊的鏈表數據
*/
public void printRightList() {
//把本結點賦值爲當前結點
Node current = this;
//遍歷當前結點的右結點
while (current.getRight() != null){
//打印結點數據,並用 - 隔開
System.out.print(current.getRight().getData() + " - ");
//獲取下一個右結點
current = current.getRight();
}
}
/**
* 下面是 Getter 和 Setter 方法
*/
public Integer getData() {
return data;
}
public void setData(Integer data) {
this.data = data;
}
public Node getLeft() {
return left;
}
public void setLeft(Node left) {
this.left = left;
}
public Node getRight() {
return right;
}
public void setRight(Node right) {
this.right = right;
}
public Node getDown() {
return down;
}
public void setDown(Node down) {
this.down = down;
}
}
/**
* 跳錶的最大層數
* 如果想要完全發揮Integer的logN查詢,可以
* 將這個值設置爲32
*/
private static int SKIP_LIST_MAX_LEVEL = 16;
/**
* 跳錶每一層的頭結點列表
*/
private List<Node> headList;
//當SkipList類實例創建的時候,初始化headList
{
//將頭結點列表,聲明爲固定容量(跳錶最大層數)的列表
headList = new ArrayList<>(SKIP_LIST_MAX_LEVEL);
//每一層都加入空數據的頭結點
for (int i = 0; i < SKIP_LIST_MAX_LEVEL; i++) {
headList.add(new Node());
}
//上一層將下一層設置爲下結點
for (int i = 1; i < SKIP_LIST_MAX_LEVEL; i++) {
headList.get(i).setDown(headList.get(i - 1));
}
}
/**
* 獲得新加節點的層數
* @return 隨機層數
*/
private int getLevel() {
//最底層下標爲0,這一層包含所有數據
int level = 0;
//每當中標二分之一的概率
while (Math.random() < 0.5) {
//層數累加(看能連續中多少個二分之一的概率,也就是多少層)
level ++;
}
//返回當前數據的最高層數,如果超過了最大層,就返回最大層
return level >= SKIP_LIST_MAX_LEVEL? SKIP_LIST_MAX_LEVEL - 1 : level;
}
/**
* 跳錶的元素添加操作
* @param data 需要添加的數據
*/
public boolean add(int data) {
//先獲取需要插入的層數
int level = getLevel();
//獲取最頂層的頭結點
Node topLevelHead = headList.get(level);
//新結點(位於上部)
Node upNode = null;
//新結點(位於下部)
Node downNode = null;
//從頂層的頭結點,一直往右下角方向找
while (true) {
//插入數據的新實例結點
Node newNode = new Node();
//設置結點數據爲插入的數據
newNode.setData(data);
//標記爲下部新結點
downNode = newNode;
//從本層頭結點一直往右找,直到找到空,或者右邊結點數據比插入數據大的
while (topLevelHead.getRight() != null && topLevelHead.getRight().getData() < data) {
//獲得那個比插入數據剛剛小一點的節點
topLevelHead = topLevelHead.getRight();
}
//剛剛小一點的節點的右邊的數據(剛剛大於或等於插入數據)
Node right = topLevelHead.getRight();
//如果右邊的數據存在,且是等於插入數據
if (right != null && right.getData() == data) {
//表示數據已存在,不重複加入,返回操作失敗
return false;
}
//如果topLevelHead剛好比插入數據小,且
//right剛好比插入數據大
//剛剛小的,設置插入結點爲右結點
topLevelHead.setRight(newNode);
//插入結點,設置剛剛小的爲左節點
newNode.setLeft(topLevelHead);
//如果有剛剛大的右結點
if (right != null) {
//插入節點,設置剛剛大的爲右結點
newNode.setRight(right);
//剛剛大的,設置插入節點爲左節點
right.setLeft(newNode);
}
//如果標記的上部結點存在
if (upNode != null) {
//將新結點,也就是下部結點,設置爲上部結點的下結點
upNode.setDown(downNode);
}
//如果還有下一層
if (topLevelHead.getDown() != null) {
//將剛剛小的結點,用下一層的等值結點替代
//然後重複這個過程,往右找到合適的地方插入新結點
topLevelHead = topLevelHead.getDown();
//更新位於上部的標記結點
upNode = newNode;
//如果已經是最底層了(最底層的結點,
//都沒有下結點 ),結束
} else {
//跳出循環
break;
}
}
//如果沒有找到等值的結點,就會插入
//並走到這裏,返回插入成功
return true;
}
/**
* 跳錶的元素刪除操作
* @param data 待刪除的元素
*/
public boolean delete(int data) {
//先找到最頂的相同結點
Node topNode = find(data);
//如果遍歷完鏈表所有層都沒找到
if (topNode == null) {
//返回刪除失敗
return false;
}
//定義當前頭結點,初始化爲最頂層找到的待刪除數據的等值結點
Node currentTopNode = topNode;
//當還沒找到最底層
while (currentTopNode != null) {
//如果找到當前層的刪除數據等值結點無右節點
if (currentTopNode.getRight() == null) {
//獲取等值結點的左節點
Node left = currentTopNode.getLeft();
//等值結點左節點設置右節點爲空
//也就相當於刪除了這個等值結點
left.setRight(null);
//如果找到當前層的刪除數據等值結點有右節點
} else {
//找到等值結點的左節點
Node left = currentTopNode.getLeft();
//找到等值結點的右節點
Node right = currentTopNode.getRight();
//然後讓等值結點兩邊的結點牽手,跨過等值結點牽手
//中間的等值結點就被刪除了
//左節點設置右節點爲右節點
left.setRight(right);
//右節點設置左節點爲左節點
right.setLeft(left);
}
//繼續往下一層找等值結點,重複上述刪除的過程
currentTopNode = currentTopNode.getDown();
}
//如果走到這裏,就證明找到了刪除元素,並刪除成功了
return true;
}
/**
* 跳錶元素的查找
* @param data 要查找的數據
* @return
*/
public Node find(int data) {
//找到頂層的頭結點
Node topLevelHead = headList.get(SKIP_LIST_MAX_LEVEL - 1);
//當前節點初始化爲頂層頭結點
Node current = topLevelHead;
//頂部節點
Node topNode = null;
//尋找最頂部相同元素
while (true) {
//如果到了最底層都沒找到
if (current == null) {
//那就不存在,返回空
return null;
}
//一直找比查詢值剛剛小的節點
while (current.getRight() != null && current.getRight().getData() < data) {
//將剛剛小的節點,作爲當前節點
current = current.getRight();
}
//如果是頭結點,或者找到了末尾
if (current.getRight() == null) {
//當前節點垂直到下一層,接着往右找
current = current.getDown();
//如果剛剛小的結點的右節點的值不等於查詢的值
} else if (current.getRight().getData() != data) {
//當前節點垂直到下一層,接着往右找
current = current.getDown();
//最後就是等於
} else {
//也就是找到了最頂的等價結點
topNode = current.getRight();
//結束查找
break;
}
}
//返回最頂的結點(所有層結點都是一樣的)
return topNode;
}
/**
* 打印跳錶
*/
public void printSkipList() {
//從底層到頂層打印
for (int i = 0; i < SKIP_LIST_MAX_LEVEL; i++) {
System.out.print("第" + i + "層:");
headList.get(i).printRightList();
System.out.println();
}
}
/**
* 跳錶的測試方法
* @param args
*/
public static void main(String[] args) {
//新建跳錶的實例
SkipList skipList = new SkipList();
//亂序加入一些數據
skipList.add(3);
skipList.add(7);
skipList.add(5);
skipList.add(4);
skipList.add(2);
skipList.add(6);
skipList.add(8);
skipList.add(9);
skipList.add(0);
skipList.add(1);
//打印加入數據後的跳錶
skipList.printSkipList();
System.out.println("==================================");
//刪除一些存在的值,和不存在的值
System.out.println("刪除3:" + skipList.delete(3));
System.out.println("刪除5:" + skipList.delete(5));
System.out.println("刪除7:" + skipList.delete(7));
System.out.println("刪除15:" + skipList.delete(15));
System.out.println("==================================");
//打印刪除後的跳錶
skipList.printSkipList();
System.out.println("==================================");
//查找一些存在的值,和不存在的值
System.out.println("尋找6:" + skipList.find(6));
System.out.println("尋找8:" + skipList.find(8));
System.out.println("尋找3:" + skipList.find(3));
}
}
運行SkipList類的main方法,觀察控制檯的輸出,測試通過:
第0層:0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 -
第1層:2 - 3 - 6 - 7 - 8 - 9 -
第2層:3 - 6 - 7 - 9 -
第3層:3 - 6 - 7 -
第4層:
第5層:
第6層:
第7層:
第8層:
第9層:
第10層:
第11層:
第12層:
第13層:
第14層:
第15層:
==================================
刪除3:true
刪除5:true
刪除7:true
刪除15:false
==================================
第0層:0 - 1 - 2 - 4 - 6 - 8 - 9 -
第1層:2 - 6 - 8 - 9 -
第2層:6 - 9 -
第3層:6 -
第4層:
第5層:
第6層:
第7層:
第8層:
第9層:
第10層:
第11層:
第12層:
第13層:
第14層:
第15層:
==================================
尋找6:6
尋找8:8
尋找3:null