算法与数据结构【C++】:跳跃链表

         普通链表有一个严重的缺陷:查找某对象时需要遍历整个链表,直到找到了该元素或者遍历完了整个链表也没有找到,时间复杂度很高。

         为了解决该问题,可以使用跳跃链表。

跳跃链表的特点:

跳跃链表中的元素按照从小到达或从大到小的规则排列,该顺序在向链表加入元素时维护,所以,只能指定向链表插入某个值,不能指定插入位置
跳跃链表使用了二分查找的思想, 查找某个元素的复杂度是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));
	}
	*/
}











        

        

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值