数据结构之哈希表之跳表

上次写的博客总结了数组实现哈希表,这次总结一下链表实现哈希表。
哈希表的基础知识在上次的数组实现哈希表里讲过了,这是链接:数组哈希
跳表结构如下:
在这里插入图片描述
那么我们来写它需要的结构体以及所需函数:先是数据类型的结构体:

struct datatype {
	int first;
	char second[20];
};

这里跟数组哈希是一样的,一个是需要取余的键值,一个是存放的数据
既然是链表结构,那么该有的链表节点我们得写一下,这个看我的数据结构专栏的帅哥都很熟悉,或者经常写链表的靓仔都很熟悉,所以直接上了:

struct node {
	struct datatype* data;
	struct node* next;
};
struct node* createnode(struct datatype* data) {
	struct node* newnode = (struct node*)malloc(sizeof(struct node));
	newnode->data = data;
	newnode->next = NULL;
	return newnode;
}

那么我们刚才看了跳表的基本结构,我们抽象出来跳表的基本单元也就是第一列节点的情况:
在这里插入图片描述
然后我们把基础单元封装成结构体,并写出它的创建的函数:

struct skiplistnode {
	struct datatype* data;
	struct node* firstnode;
	struct skiplistnode* next;
};
struct skiplistnode* createskiplistnode(struct datatype* data) {
	struct skiplistnode* newnode = (struct skiplistnode*)malloc(sizeof(struct skiplistnode));
	newnode->data = data;
	newnode->firstnode = NULL;//横向的点
	newnode->next = NULL;//竖向的节点
	return newnode;
}

接下来是抽象一下跳表结构,也就是若干个跳表节点和自己的大小:

struct skiplist {
	struct skiplistnode* headnode;
	int sizeskip;
	int div;//取余法
};
//创建一个跳表:
struct skiplist* createskiplist(int div) {
	struct skiplist* list = (struct skiplist*)malloc(sizeof(struct skiplist));
	list->headnode = NULL;
	list->sizeskip = 0;
	list->div = div;
	return list;
}

好了跳表结构我们完成了初始化的工作,接下来是在表中记录元素的函数,这里运用到了有序链表,和有序插入的知识,逻辑上稍微有点复杂,我在注释上标清楚我每一步在干嘛,有问题的也可以在评论提问我,大家注意画图理解:

//这个函数也就是找到合适的位置插入
void insertnodebysort(struct skiplist* list, struct datatype* data) {//注意我所有的data传入的都是一级指针,这样占用空间少也运行速度快
	int datahashpos = data->first % list->div;//取余法第一步
	struct skiplistnode* newskipnode = createskiplistnode(data);//创建个跳表节点
	if (list->headnode == NULL) {//这里是表中无元素,直接弄成表头节点就行了
		list->headnode = newskipnode;
		list->sizeskip++;
	}
	else {//表中有元素的情况
		struct skiplistnode* pmove = list->headnode;
		struct skiplistnode* pmovefront = NULL;//有序链表的插入,运用两个指针查找位置
		if (pmove->data->first % list->div > datahashpos) {//直接比它大,说明能直接插入,也就是新插入元素作为表头
			//插入的节点当表头
			newskipnode->next = list->headnode;
			list->headnode = newskipnode;
			list->sizeskip++;
		}
		else {//其他情况,也就是需要往后找
			while ((pmove->data->first) % (list->div) < datahashpos) {
				pmovefront = pmove;
				pmove = pmovefront->next;
				//到达表位处理,也就是找到位置
				if (pmove == NULL) {
					break;
				}
			}
			//新节点的插入考虑hash冲突,以及关键词相同的时候的处理
			if (pmove != NULL && (pmove->data->first % list->div) == datahashpos) {//遍历表,找不到位置
				//如果存在位置,讨论。键相同,采用覆盖的方式
				if (pmove->data->first == data->first) {//键相同
					strcpy(pmove->data->second, data->second);
				}
				else {//不相同,冲突处理
					struct node* newnode = createnode(data);
					struct node* ppmove = pmove->firstnode;
					if (ppmove == NULL) {
						newnode->next = pmove->firstnode;
						pmove->firstnode = newnode;
						list->sizeskip++;
					}
					else {
						while (ppmove->data->first != data->first) {
							ppmove = ppmove->next;
							if (ppmove == NULL) {
								break;
							}
						}
						if (ppmove == NULL) {
							newnode->next = pmove->firstnode;
							pmove->firstnode = newnode;
							list->sizeskip++;
						}
						else {
							strcpy(ppmove->data->second, data->second);
						}
					}
				}
			}
			else {//考虑不冲突的情况
				pmovefront->next = newskipnode;
				newskipnode->next = pmove;
				list->sizeskip++;
			}
		}
	}
}

好了,插入元素,我们完成了,那么这个数据结构其实已经完成了,接下来是验证我们做的对不对:
一个打印函数:
竖向打印,横向有冲突的在后面插入的,要做判断,如果有横向走到NULL结束,打印

void printlist(struct skiplist* list) {
	struct skiplistnode* pmove = list->headnode;//竖向打印的指针
	while (pmove) {
		printf("(%d,%s)\t", pmove->data->first, pmove->data->second);//竖向的打印
		struct node* ppmove = pmove->firstnode;//检测横向的指针
		while (ppmove) {
			printf("(%d,%s)\t", ppmove->data->first, ppmove->data->second);//横向的打印
			ppmove = ppmove->next;
		}
		printf("\n");//每次横向打印完毕转行一下,方便我们看结果
		pmove = pmove->next;//记得移动指针
	}
	printf("\n");
}

主函数:

int main() {
	struct skiplist* list = createskiplist(10);
	struct datatype array[7] = {
		1,"love",4,"my",11,"baby",8,"hate",9,"unhappy",6,"aaa",7,"原谅我"
	};
	for (int i = 0; i < 7; i++) {
		insertnodebysort(list, &array[i]);//其中检验了,空表情况,以及正常,还有冲突现象
	}
	printlist(list);
	struct datatype a = { 20,"yyx" };//检验直接表头的情况
	insertnodebysort(list, &a);
	printlist(list);
	return 0;
}

好了到这,我们的跳表结构已经写完了。
但在这最后这里,我想补充的是其实每一列竖向都是可以连通的,如图:
在这里插入图片描述
这样的结构的的确确存在着,有兴趣的小伙伴其实可以实现一下这样的结构。
再看一下,如果我们以头节点作为中心,顺时针旋转45度,有没有发现啥?
对,这是个极不平衡的二叉树!
看我下面的图
在这里插入图片描述
好了,这样就是我在这里总结的全部了。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值