C++跳跃表个人理解

一、概念

跳跃表是一种基于有序链表的扩展,简称跳表,其就是使用关键节点作为索引的一种数据结构。

可以通过以下方式更快查找到有序链表的某一节点:

利用类似索引的思想,提取出链表中的部分关键节点。比如,给定一个长度为7的有序链表,节点值依次是1->2->3->5->6->7->8,那么我们可以取出所有值为奇数节点的作为关键点。

此时如果要插入一个值是4的新节点,不再需要和原节点8,7,6,5,3逐一比较,只需要比较关键节点8,5,3.

确定了新节点在关键节点中的位置(3和5之间),就可以回到原链表,迅速定位到对应的位置,然后进行插入。

 

 多层关键节点多层索引

对于跳跃表来说,当然不能只会有一层索引节点,那么可以进一步提取索引,在索引层中提取出一层新的索引。

有了二级索引之后,新的节点可以先和2级索引进行比较,确定大体范围;然后再和1级索引比较;最后回到原链表,找到并插入对应位置。

 层级极限是什么?

当节点足够多的时候,不止能提取出二级索引,还可以向高层次提取,保证每一层是上一层节点数的一半。

提取的极限:同一层只有两个节点的时候,因为一个节点没有比较的意义。

怎么从新节点当中选取一部分提到上一层?

当大量的新节点通过逐层比较,最终插入到原链表之后,上层的索引节点会变得不够用。这时候需要从新节点当中选取一部分提到上一层。

使用随机决定新节点是否提升层级,每次向上提升一层的概率是百分之50.

为什么要随机决定新节点是否层级提升?

(1)跳跃表删除和添加节点是不可预测的,很难用一种有效地算法来保证跳跃表的索引部分始终均匀。

(2)采用随机法虽然不能保证索引绝对均匀分布,却可以让大体趋于均匀。

二、时间复杂度分析

跳跃表将新节点(删除节点)和各层索引节点逐一进行比较,确定原链表的插入或者删除位置。时间复杂度为O(logn)。

跳跃表的插入和删除操作需要的时间复杂度和原始链表一样,都为O(1)。

三、跳跃表的特征

一个完整的跳跃表,应该具有以下特征:

1、一个跳表应该有几个层(level)组成。

2、跳表的第一层包含所有的元素。

3、每一层都是一个有序的链表。

4、如果元素x出现在第i层,则所有比i小的层都包含x。

5、每个节点包含key及其对应的value和一个指向同一层链表的下个节点的指针数组(next[i])。

四、跳跃表和红黑树性能比较

目前,经常使用的平衡数据结构有:B树、红黑树、AVL树等。跳跃表是平衡树一种替代的数据结构,但是和红黑树不相同的是,跳跃表对于平衡的实现是基于一种随机化的算法,跳跃表的插入和删除工作是比较简单的。

1、查询性能:

跳跃表和红黑树的查询时间复杂度都为O(logn)。

2、插入和删除性能:

跳跃表在进行插入和删除操作时,相对来说,更容易调整结构,也就是可能需要在索引层中增加或者删除相应节点。

红黑树在进行插入和删除操作时,就需要进行旋转或者颜色调度操作,相对复杂一些。

3、空间复杂度:

跳跃表的空间复杂度相对高一些,需要额外的空间来存储多层指针。平均情况下O(n),最糟糕的情况下O(nlogn)。

红黑树空间开销相对跳跃表来说会低。O(n)。

4、应用场景:

如果要在高并发的场景下,频繁进行插入和删除节点,那么跳跃表可能更合适一些。

如果对空间性能要求较高,那么可以去使用红黑树。

五、代码实现

#include<iostream>
#include<time.h>
#include<assert.h>
#include<stdlib.h>
using namespace std;
#define MAX_L 5//跳跃表最大层数
 
//节点结构
struct Node
{
	int key;//节点的值
	Node* next[];//多层链表节点
};
//跳跃表结构
struct Skiplist
{
	int level;//跳跃表层数
	Node* head;
};
//创建节点
Node* CreateNode(int level, int key)
{
	Node* p = (Node*)malloc(sizeof(Node) + level * sizeof(Node*));
	if (nullptr == p)
	{
		return nullptr;
	}
	for (int i = 0; i < level; ++i)
	{
		p->next[i] = nullptr;
	}
	p->key = key;
	return p;
}
//创建表
Skiplist* CreateList()
{
	Skiplist* s = (Skiplist*)malloc(sizeof(Skiplist));
	if (nullptr == s)
	{
		return nullptr;
	}
	s->level = 0;
	Node* nh = CreateNode(MAX_L + 1, 0);
	if (nullptr == nh)
	{
		free(s);
		return nullptr;
	}
	s->head = nh;
	return s;
}
//生成新节点的级数
int random()
{
	int level = 0;
	while (level <= MAX_L && rand() < RAND_MAX / 2)
	{
		++level;
	}
	return level;
}
//向跳跃表中插入元素
bool InsertNode(Skiplist* s, int key)
{
	if (nullptr == s) return false;
	//第一步,查找到在每层待插入位置,更新update数组
	Node* update[MAX_L + 1] = {};
	Node* q = nullptr, * p = s->head;
	//找到每一层插入前的一个节点,更新update数组
	for (int i = s->level; i >= 0; --i)
	{
		while (p->next[i] != nullptr && p->next[i]->key < key)
		{
			p = p->next[i];
		}
		update[i] = p;
	}
	p = p->next[0];
	if (p != nullptr && p->key == key)
	{
		return true;
	}
	//第二步,随机产生一个层数
	int newlevel = random();
 
	//新产生的节点层数比原跳跃表大
	if (newlevel > s->level)
	{
		for (int i = s->level + 1; i <= newlevel; ++i)
		{
			update[i] = s->head;
		}
		s->level = newlevel;
	}
	//第三步,从高往下插入
	p = CreateNode(newlevel + 1, key);
	if (nullptr == p)
	{
		return false;
	}
	//根据update数组在每一层中插入新节点
	for (int i = newlevel; i >= 0; --i)
	{
		p->next[i] = update[i]->next[i];
		update[i]->next[i] = p;
	}
	return true;
}
//删除跳跃表中的元素
bool DeleteNode(Skiplist* s, int key)
{
	Node* update[MAX_L + 1] = {};
	Node * p = s->head;
	//找到每一层待删除元素前的一个节点,放入update数组中
	for (int i = s->level; i >= 0; --i)
	{
		while (p->next[i] != nullptr && p->next[i]->key < key)
		{
			p = p->next[i];
		}
		update[i] = p;
	}
	Node* q = p->next[0];
	assert(q != nullptr);
	//判断q是否为待删除的节点 
	if (q==nullptr && q->key != key)
	{
		return false;
	}
	for (int i = s->level; i >= 0; --i)
	{
		if (update[i]->next[i]==q)
		{
			update[i]->next[i] = q->next[i];
			if (s->head->next[i] == nullptr)
			{
				s->level--;
			}
		}
	}
	free(q);
	return true;
}
void Print(Skiplist* s)
{
	Node* p = nullptr;
	for (int i = s->level; i >= 0; --i)
	{
		p = s->head->next[i];
		cout << "level:" << i << endl;
		while (p != nullptr)
		{
			cout << "key:" << p->key << " ";
			p = p->next[i];
		}
		cout << endl;
	}
}
void FreeList(Skiplist* s)
{
	if (nullptr == s)
	{
		return;
	}
	Node* q = s->head;
	Node* next = nullptr;
	while (q != nullptr)
	{
		next = q->next[0];
		free(q);
		q = next;
	}
	free(s);
}
int main()
{
	Skiplist* mylist = CreateList();
	for (int i = 1; i < 5; ++i)
	{
		InsertNode(mylist, i);
	}
	DeleteNode(mylist, 3);
	Print(mylist);
	free(mylist);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值