普通链表有一个严重的缺陷:查找某对象时需要遍历整个链表,直到找到了该元素或者遍历完了整个链表也没有找到,时间复杂度很高。
为了解决该问题,可以使用跳跃链表。
跳跃链表的特点:
跳跃链表中的元素按照从小到达或从大到小的规则排列,该顺序在向链表加入元素时维护,所以,只能指定向链表插入某个值,不能指定插入位置
跳跃链表使用了二分查找的思想, 查找某个元素的复杂度是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));
}
*/
}