跳表c++实现

本文介绍了跳表(Skip List)这一高效的数据结构,它的插入、查询、删除操作平均时间复杂度为O(logn),空间效率优于红黑树。文章详细讲解了跳表的节点结构、随机生成节点层级的算法,并提供了C++实现的跳表类,包括插入、搜索和删除功能。在实现过程中,作者遇到了二级指针使用和初始化错误等问题,并给出了修正方案。
摘要由CSDN通过智能技术生成

原理:https://blog.csdn.net/u013011841/article/details/39158585

一.跳表结构

一种插入、查询、删除时间效率为O(log n)空间效率为O(n)的数据结构,效率堪比红黑树,但是实现起来更加简易,内存空间使用更小,便于调试。
在这里插入图片描述

1.节点

//节点
class node {
	int val;
	int level;//节点的高度(层次)
	node* forword[MAXLEVEL];//指向node*的数组
};

2.skiplist(管理整个表的一个数据结构)

class skiplist {
private:
	node* head;//指向跳表的头结点
	int max_length;//计算跳表的最大节点个数
}

随机生成level算法

//随机生成level算法
int randomLevel() {
	int k = 1;

	while (rand() % 2) {
		k++;
	}
	k = (k < MAXLEVEL) ? k : MAXLEVEL;
	return k;
}

二、具体实现

class node {
public:
	node(int val,int level) {
		this->val = val;
		this->level = level;
		//初始化节点指针
		//forword = (node**)malloc(sizeof(node*) * level);
		for (int i = 0; i < MAXLEVEL; i++) {
			forword[i] = NULL;
		}
	}
	~node() {	
	}

	int val;
	int level;
	node* forword[MAXLEVEL];//指向node*的数组
};
class skiplist {
public:
	
	skiplist() {
		//head = NULL;
		max_length = 0;
		//初始化head为一个5层的val(0)的node
		node* p = new node(0, 5);
		head = p;
	}
	//注:所有方法都是跳过第一个节点(5层的val(0)),
	void insert(int val) {
		int level = randomLevel();
		max_length++;
		//如果目前还没有节点
		if (head == NULL) {
			//第一个节点的高度为max
			node* p = new node(val,MAXLEVEL);
			head = p;
			max_length++;
		}
		else {
			//指向第一个节点
			node* cur = head;
			//p是新加入的节点
			node* p = new node(val, level);
			
			//保存每一层插入的最近前面节点
			//node** last=(node**)malloc(MAXLEVEL*sizeof(node*));
			node* last[MAXLEVEL];
			for (int i = MAXLEVEL-1; i >= 0; i--) {
				//在第i层找到最后一个比val小的节点
				while (cur->forword[i] != NULL && cur->forword[i]->val < val) {
					cur = cur->forword[i];
				
				}
				last[i] = cur;
			}
			//找到了
			if(cur->forword[0]==NULL){
				//插入到最后
				for (int i = p->level-1; i >= 0; i--) {
					last[i]->forword[i] = p;
				}
			}
			else if (cur->forword[0]->val > val) {
				//插入
				for (int i = p->level-1; i >= 0; i--) {
					//p的后面指针=p前面的指针的后续指针
					p->forword[i] = last[i]->forword[i];
					//改变p前面的指针的后续指针
					last[i]->forword[i] = p;
				}
				
			}
			
		}
	}
	//从最高level开始找,若最高level找不到则level-- 直到level==0若还是找不到 则真找不到(cout<<"not find")。
	void search(int val) {
		node* cur = head;
		for (int i = MAXLEVEL - 1; i >= 0; i--) {
			while (cur->forword[i]!=NULL&&cur->forword[i]->val<val) {
				cur = cur->forword[i];
				
			}

			if (cur->forword[i]!=NULL&&cur->forword[i]->val == val) {
						cout << "i search it " << endl;
						return;
			}
			//else cout << "not find" << endl;
			
		}
		cout << "not find" << endl;
	}
	//方法与search一样,先找到要删除的值,若找到了改变前面的指针
	void delete_val(int val) {
		node* cur = head;
		node* ask=NULL;//要找到的节点
		node* last[MAXLEVEL];
		for (int i = MAXLEVEL - 1; i >= 0; i--) {
			while (cur->forword[i] != NULL && cur->forword[i]->val < val) {
				cur = cur->forword[i];
			}
			//保留每个level的前面的指针
			last[i] = cur;

			if (cur->forword[i] != NULL && cur->forword[i]->val == val) {
				//找到了但是得保存前面的指针所以继续往下扫描
				//保存找到的节点
				ask = cur->forword[i];
			}
			//else cout << "not find" << endl;

		}
		for (int i = ask->level - 1; i >= 0; i--) {
			//删除的节点前面的指针替换成删除的节点所指
			last[i]->forword[i] = ask->forword[i];

		}
		//释放内存
		delete(ask);
	}
	//测试打印
	void print() {
		
		for (int i = MAXLEVEL-1; i >= 0; i--) {
			node* cur = head;
			/*cout << cur->val;*/
			while (cur->forword[i]) {
				
				cur = cur->forword[i];
				cout << cur->val;
			}
			cout << endl;
		}
		//cout << head->get_val() << endl;
		//cout << max_length;
	}

	int get_max_length() {
		return max_length;
	}
private:
	node* head;
	int max_length;
};

三、测试

int main() {
	skiplist t1;
	t1.insert(3);
	t1.insert(7);
	t1.insert(4);
	t1.insert(6);
	t1.insert(5);
	t1.insert(-1);
	t1.delete_val(4);
	//cout << t1.get_max_length()<<endl;
	t1.search(8);
	//cout << endl;
	//t1.insert(10);
	//t1.insert(100);
	//t1.search(6);
	//t1.delete_val(4);
	t1.print();
   system("pause");
	return 0;
}

运行结果:
注:print()函数打印的为跳表从最左节点打到最后,从上往下打。
在这里插入图片描述
跳表实现成功!

四、反思

编写的时候出现了两个bug:

  1. 二级指针的建立
    我想用二级指针node** last来保存前面的节点(node*),或者是保存每个节点的forword指针,以便减少内存。结果导致debug时间一个小时,整个程序都是乱的。

错误的初始化:

node** forword;
forword = (node**)malloc(sizeof(node*) * level);

应当改为:
其中forword为node数组,node[0]:node,node[1]:node*…
数组的大小为level

forword=new node*[level];
//forword为node*数组
  1. level下标越界
    for循环写成for(int i=level;i>=0;i++),应当为i=level-1
  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
跳表是一种数据结构,可以高效地实现有序链表和有序集合,可以用来实现排行榜。跳表是通过在原始链表添加多级索引节点来加速查找操作的。 跳表实现思路如下: 1. 创建一个带有头节点的链表,头节点的值为负无穷大,尾节点的值为正无穷大。 2. 在原始链表,插入新的节点时,根据节点的值,决定是否在当前层级上添加索引节点。添加索引节点的概率可以根据需求进行调整。 3. 使用索引节点可以将跳表分为多个层级(level),每一层级都是一个有序链表。 4. 查询操作时,从最高层级开始,从左向右逐层搜索,直到找到目标值所在的区间(比目标值大的最小节点和比目标值小的最大节点之间)。 5. 对于插入和删除操作,首先在最底层进行,然后根据概率决定是否在上层级插入或删除对应的节点。 使用跳表实现排行榜的步骤如下: 1. 创建一个跳表,每个节点存储着用户的信息,包括用户的排名、分数等。 2. 初始化排行榜时,将所有用户按照分数从大到小顺序插入跳表。 3. 当有新的用户加入或者用户的分数发生变化时,根据新的分数更新用户节点的位置。 4. 当需要查询某个用户的排名时,可以通过跳表的索引节点,快速定位到该用户所在的层级,然后在该层级按照顺序遍历找到目标节点,并返回排名。 通过以上步骤,我们可以使用跳表高效地实现排行榜功能。跳表的插入、删除和查找操作的时间复杂度都可以达到O(log n),在大数据量下具有较高的效率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值