普通鏈表有一個嚴重的缺陷:查找某對象時需要遍歷整個鏈表,直到找到了該元素或者遍歷完了整個鏈表也沒有找到,時間複雜度很高。
爲了解決該問題,可以使用跳躍鏈表。
跳躍鏈表的特點:
跳躍鏈表中的元素按照從小到達或從大到小的規則排列,該順序在向鏈表加入元素時維護,所以,只能指定向鏈表插入某個值,不能指定插入位置
跳躍鏈表使用了二分查找的思想, 查找某個元素的複雜度是O(logn)
跳躍鏈表最底層(對應代碼中的0層)包含了所有元素
跳躍鏈表第1層(對應代碼中的1層)包含了從0層中隨機抽取的一半的元素(理論上)
跳躍鏈表第2層(對應代碼中的2層)包含了從1層中隨機抽取的一半的元素(理論上)
以此類推
爲什麼使用隨機數?
因爲跳躍表在何處插入和刪除值未知,很難使用預定算法使得這個鏈表“平衡”(即上層索引較爲平均的分隔下層索引)。所以使用隨機數決定,要將新加入的結點存入哪些層
算法中的缺陷:
加入元素時採用隨機值決定要插入多少層;但是刪除時,必須一律從最高層開始刪除,多次添加和刪除之後,較高層的插入是根據隨機數,刪除是必須刪除,所以會發生較高層結點較少的情況,降低查找效率。
另外,算法根據隨機數進行層數分配,有一定的不可控因素。
#include<iostream>
#include<string>
#include<stdio.h>
#include <time.h>
#include <stdlib.h>
#include<cmath>
using namespace std;
//結點類
class Node{
public:
int value; //該結點的值
Node* next; //該結點的下一個結點
Node* down; //該結點對應的下一層結點
Node(int aValue, Node* aNext=0, Node* aDown=0){
value = aValue;
next = aNext;
down = aDown;
}
};
//順序表,鏈表中的元素按升序排列
class SortedList{
public:
Node* head; //表頭結點
Node* tail; //表尾結點
int length; //鏈表長度
SortedList(){
head = tail = 0;
length = 0;
}
//插入結點
//參數:要插入新節點的值;要插入的結點的前一個結點的引用
Node* insertNode(int value, Node* aheadOfInsert){
//被插入的結點
Node* InsertedNode = 0;
//如果鏈表爲空,調用向頭部插入方法
if (aheadOfInsert==0){
InsertedNode = insertToHead(value);
}
//要在鏈表尾部插入,直接更新tail
else if (aheadOfInsert == tail){
InsertedNode = tail = aheadOfInsert->next = new Node(value, 0);
}
//在鏈表中間插入
else {
InsertedNode = aheadOfInsert->next = new Node(value, aheadOfInsert->next);
}
length++;
return InsertedNode;
}
//刪除結點
//參數:要刪除的結點的前一個結點的引用
//如果要刪除第一個結點,則該參數爲空
int deleteNode(Node* aheadOfRemove){
//鏈表爲空,直接返回-1
if (head == 0){
return -1;
}
Node* removedNode = 0;
//鏈表只有一個結點,刪除該結點
if (head == tail){
removedNode = head;
head = tail = 0;
}
//參數爲空,代表要刪除第一個結點。同時更新head
else if (aheadOfRemove == 0){
removedNode = head;
head = head->next;
}
//正常刪除結點
else{
removedNode = aheadOfRemove->next;
aheadOfRemove->next = aheadOfRemove->next->next;
}
length--;
delete aheadOfRemove;
}
//判斷鏈表是否爲空
int isEmpty(){
return head == 0;
}
//打印鏈表內容及相關參數
void printSelf(){
printf("SortedList: [");
for (Node* now=head; now!=0; now=now->next)
if (now->down == 0)
printf("%d, ", now->value);
else
printf("%d( %d), ", now->value, now->down->value);
printf("]\t\t%d\n", length);
}
private:
Node* insertToHead(int value){
if (head == 0){
head = tail = new Node(value, NULL, NULL);
//cout<<"Heading into Empty List"<<endl;
}
else {
head = new Node(value, head);
}
return head;
}
};
//跳躍表類
class SkipList{
public:
int length; //跳躍表最底層長度
int layerNum; //跳躍表最大層數
SortedList* layers[100]; //跳躍表層數的引用
//爲什麼這裏使用了數組?
//因爲跳躍表的層數是log(n),100層能夠容納2^100個數據,使用數組佔用的空間可以忽略
//構造函數
SkipList(){
length = 0;
layerNum = 0;
for (int i=0; i<100; i++)
layers[i] = new SortedList();
srand( (unsigned)time( NULL ) );
}
//向跳躍表插入值
int insert(int value){
//get the necessary layerNum by number of nodes
//先獲得增加結點之後,該結點需要加入到哪些層
int newLayerNum = getLayerNumRandomly(length+1);
//更新最大層數量
if (layerNum < newLayerNum){
layerNum++;
}
//因爲先要從上向下查找到要將新結點插入到哪裏,並且需要將查找的路徑記錄下來
//這樣才能在每一層都插入該結點
//該數組用於記錄從上向下尋找的過程中,經過了哪些層的哪個結點(下標爲層數,數組內容爲經過的結點)
Node* aheadOfInsert[100];
//數組初始化爲NULL
for (int i=0; i<100; i++)
aheadOfInsert[i] = 0;
//獲取每一層需要插入結點的位置
getInsertIndex(value, aheadOfInsert);
Node* InsertedNode[100] = {0};
//cout<<"LayerNum: "<<layerNum<<endl;
//遍歷每一層,插入結點
for (int i=layerNum;i>=0;i--){
//cout<<"Insert into layer "<<i<<" value "<<value<<endl;
//if (i==1)cout<<"-=========================================="<<endl;
//cout<<"aheadOfInsert value: "<<aheadOfInsert[i]->value<<endl;
InsertedNode[i] = layers[i]->insertNode(value, aheadOfInsert[i]);
//cout<<"asdasdasdasdasdas"<<endl;
}
//更新層與層之間的指針
for (int i=layerNum; i>=1; i--){
InsertedNode[i]->down = InsertedNode[i-1];
}
length++;
//打印各個層
cout<<"================================"<<endl;
layers[0]->printSelf();
layers[1]->printSelf();
layers[2]->printSelf();
layers[3]->printSelf();
layers[4]->printSelf();
cout<<"================================"<<endl;
}
//因爲先要從上向下查找到要將新結點插入到哪裏,並且需要將查找的路徑記錄下來
//這樣才能在每一層都插入該結點
//這個函用於查找該路徑,該路徑用要插入的結點位置的前一個結點表示,存入aheadOfInsert數組
int getInsertIndex(int value, Node** aheadOfInsert){
int i;
Node* currentNode = 0;
cout<<layerNum<<endl;
//最高的層可能爲空,需要在鏈表頭插入結點,所以前一個結點爲空
for (i=layerNum; i>=0 && (layers[i]->head==0 || layers[i]->head->value>value); i--){
aheadOfInsert[i] = 0;
}
//如果所有層都爲空,直接返回
if (i==-1)
return 0;
currentNode = layers[i]->head;
//從上層到下層遍歷,尋找該路徑
for (; i>=0; i--){
Node* now;
//該循環從一層的鏈表前方向後方遍歷,循環停止的條件有兩個:
//當遇到鏈表尾:說明該值只可能在下一層找到
//當遇到比當前值還小的結點,說明要找的值,在當前結點和下一和結點之間,轉向下一層尋找
for (now=currentNode; now->next!=0 && now->next->value<value; now=now->next);
aheadOfInsert[i] = now;
currentNode = now->down;
}
}
//因爲先要從上向下查找到要刪除哪個結點,並且需要將查找的路徑記錄下來
//這樣才能在每一層都刪除該結點
//這個函用於查找該路徑,該路徑用要刪除的結點位置的前一個結點表示,存入aheadOfRemove數組
//然而,在刪除結點時,有如下三種情況:
//1、該結點不存在這一層,不需要刪除,指針爲NULL
//2、該結點是該層的第一個結點,該結點的前一個結點爲NULL,指針也爲NULL
//3、找到了該結點,指針正常
//所以,需要另一個數組表示該結點是否應該被刪除,這裏使用數組deleteOrNot
int getRemoveIndex(int value, Node** aheadOfRemove, bool* deleteOrNot){
int i;
Node* currentNode = 0;
//從上層向下層遍歷,如果該層爲空,或該層第一個值已經大於要刪除的值,說明這一層不需要刪除結點
for (i=layerNum; i>=0 && (layers[i]->head==0 || layers[i]->head->value>value); i--){
aheadOfRemove[i] = 0;
deleteOrNot[i] = false;
}
//循環退出之後,i指向需要有結點有可能被刪除的第一個層
cout<<i<<endl;
if (i==-1)
return 0;
//遍歷可能需要刪除結點的層
//如果要刪除的結點是底層鏈表的第一個元素,那麼向上找,把所有頭元素是該元素的鏈表首元素全部刪除
if (layers[0]->head->value == value){
i=0;
while(layers[i]->head!=0 && layers[i]->head->value == value){
deleteOrNot[i] = true;
aheadOfRemove[i] = 0;
i++;
}
return 0;
}
currentNode = layers[i]->head;
//否則向下沿路徑查找,尋找應該刪除的結點
for (; i>=0; i--){
Node* now;
for (now=currentNode; now->next!=0 && now->next->value<value; now=now->next);
if (now->next!=0 && now->next->value == value){
deleteOrNot[i] = true;
}
aheadOfRemove[i] = now;
currentNode = now->down;
}
}
//刪除某個元素的函數
int remove(int value){
//每一層需要被刪除的結點的前一個結點
Node* aheadOfRemove[100] = {0};
//該層的這個結點是否有效,或者說,如果aheadOfRemove【i】==null,是代表不需要刪除任何結點,還是刪除第一個結點
bool removeOrNot[100] = {false};
cout<<"Got removeIndex";
//獲取每一層需要被刪除的結點的前一個結點
getRemoveIndex(value, aheadOfRemove, removeOrNot);
cout<<"Got removeIndex";
//刪除結點
for (int i=layerNum; i>=0; i--)
if (removeOrNot[i] == true){
layers[i]->deleteNode(aheadOfRemove[i]);
}
length--;
//打印結果
cout<<"================================"<<endl;
layers[0]->printSelf();
layers[1]->printSelf();
layers[2]->printSelf();
layers[3]->printSelf();
layers[4]->printSelf();
cout<<"================================"<<endl;
}
//在跳躍表中搜索某個元素
Node* search(int value){
int i;
Node* currentNode = 0;
//從上層向下層遍歷,如果該層爲空,或該層第一個值已經大於要查找的值,說明這一層不需要查找
for (i=layerNum; i>=0 && (layers[i]->head==0 || layers[i]->head->value>value); i--);
if (i==-1)
return 0;
cout<<"i = "<<i<<endl;
currentNode = layers[i]->head;
Node* now;
//從上層向下層遍歷查找
for (; i>=0; i--){
for (now=currentNode; now->next!=0 && now->next->value<=value; now=now->next);
currentNode = now->down;
}
return now;
}
//隨機獲取某個結點應該被插入到0-k層
int getLayerNumRandomly(int supposeLength){
int k = 0;
//隨機數
int a = rand()%2;
while(a==0){
cout<<a<<" ";
k++;
a = rand()%2;
}
//根據二分的思想,查找n個結點,需要log(n)次
//跳躍表裏也就需要log(n)層,所以這裏對隨機產生的層數有最大值限制
int maxLayer = getMaxLayer(supposeLength);
if (k > maxLayer)
k = maxLayer;
return k;
}
int getMaxLayer(int supposeLength){
return ceil(log(supposeLength)/log(2));
}
};
//測試函數
int main(){
SkipList* list = new SkipList();
list->insert(33);
list->insert(1);
list->insert(11);
list->insert(21);
list->insert(-1);
list->insert(45);
list->insert(37);
Node* result = list->search(11);
cout<<"Search result: "<<result->value<<endl;
cout<<"next Search result: "<<result->next->value<<endl;
list->remove(11);
list->remove(-1);
list->remove(45);
/*
for (int i=0; i<=100; i++){
printf("%d: %d\n", i, list->getMaxLayer(i));
}
*/
}