跳表分析以及实现

题目:

实现链表的快速插入、查询、删除等操作

分析:

我们知道,如果只是通过简单的单链表,我们对单链表的插入、删除、查询操作都是O(n)的,这里需要用到一种特殊的数据结构——跳表,能够实现高速插入、删除、查找,平均时间复杂度能够达到O(logn)(具体分析,有很多人写了专门的文章,由于跳表是基于概率的,所以时间复杂度不好分析)。

跳表的核心思想,比如查找,让查找尽可能的快,所以我们第一次尝试查询时,查询跳跃步长尽可能大,如果失败,我们再减小步长,直到最坏情况下,我们步长为1,这就和普通单链表查询一样了。


如上数据结构,我们的跳表level最大是4层,假如我们要查找元素6,我们首先从level 4(level从上往下递减)开始,level 4指向NULL,所以我们减小level,到level 3,level 3指针直接指向元素4,继续从元素4的level 3往后跳,这样就跳到了元素6,正好6就是我们要查找的元素。

从上面可以看出来,原来需要6次的查找,现在只需要3次就行。我们利用了额外的空间换取了时间,但是注意,每个元素的level层数是由一个随机函数生成的,所以跳表是基于概率的,时间复杂度分析不好做,有谁能给个好的分析方法,还望给个链接。

代码实现:

C语言的版本可以参考这个博客http://yilee.info/skip-list.html,作者分析也做得非常好

C++版本可以参考:http://www.cnblogs.com/liuhao/archive/2012/07/26/2610218.html,但是我运行的时候存在一些问题,我修改了一下,贴出来:

// ConsoleApplication1.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"


#include "skipList.h"
#include <stdlib.h>
#include <stdio.h>
#include <time.h>

#define SKIP_LIST_TEST_MAIN

skiplistNode *slCreateNode(int level, double score) {
	skiplistNode * sn = (skiplistNode *)malloc(sizeof(*sn) + level*sizeof(struct skiplistNode));
	sn->score = score;
	return sn;
}

skiplist *slCreate(void) {
	int j;
	skiplist *sl;

	sl = (skiplist *)malloc(sizeof(*sl));
	sl->level = 1;
	sl->length = 0;
	sl->header = slCreateNode(SKIPLIST_MAXLEVEL, 0);
	for(j = 0; j < SKIPLIST_MAXLEVEL; j++) {
		sl->header->level[j].forward = NULL;
	}
	sl->header->backward = NULL;
	sl->tail = NULL;
	return sl;
}

void slFreeNode(skiplistNode *sn) {
	free(sn);
}

void slFree(skiplist *sl) {
	skiplistNode *node = sl->header->level[0].forward, *next;

	free(sl->header);
	while(node) {
		next = node->level[0].forward;
		slFreeNode(node);
		node = next;
	}
	free(sl);
}

int slRandomLevel(void) {
	int level = 1;
	//while((rand()&0xFFFF) < (0.5 * 0xFFFF)) 
	while(rand() % 2)	//change the value to adjust the level number
		level += 1;
	return (level < SKIPLIST_MAXLEVEL) ? level : SKIPLIST_MAXLEVEL;
}

skiplistNode *slInsert(skiplist *sl, double score) {
	skiplistNode *update[SKIPLIST_MAXLEVEL];
	skiplistNode *node;

	node = sl->header;
	int i, level;
	for ( i = sl->level-1; i >= 0; i--) {
		while(node->level[i].forward && node->level[i].forward->score < score) {
			node = node->level[i].forward;
		}
		update[i] = node;
	}
	level = slRandomLevel();
	if (level > sl->level) {
		for (i = sl->level; i< level ;i++) {
			update[i] = sl->header;
		}
		sl->level = level;
	}
	node = slCreateNode(level, score);
	for (i = 0; i < level; i++) {
		node->level[i].forward = update[i]->level[i].forward;
		update[i]->level[i].forward = node;
	}

	node->backward = (update[0] == sl->header? NULL : update[0]);
	if (node->level[0].forward)
		node->level[0].forward->backward = node;
	else
		sl->tail = node;
	sl->length++;
	return node;
}

void slDeleteNode(skiplist *sl, skiplistNode *x, skiplistNode **update){
	int i;
	for (i = 0; i < sl->level; i++) {
		if (update[i]->level[i].forward == x) {
			update[i]->level[i].forward = x->level[i].forward;
		}
	}
	if (x->level[0].forward) {
		x->level[0].forward->backward = x->backward;
	} else {
		sl->tail = x->backward;
	}
	while (sl->level > 1 && sl->header->level[sl->level-1].forward == NULL) 
		sl->level--;
	sl->length--;
}

int slDelete(skiplist *sl, double score) {
	skiplistNode *update[SKIPLIST_MAXLEVEL], *node;
	int i;

	node = sl->header;
	for(i = sl->level-1; i >= 0; i--) {
		while (node->level[i].forward && node->level[i].forward->score < score) {
			node = node->level[i].forward;
		}
		update[i] = node;
	}
	node = node->level[0].forward;
	if (node && score == node->score) {
		slDeleteNode(sl, node, update);
		slFreeNode(node);
		return 1;
	} else {
		return 0;
	}
	return 0;
}

int slSearch(skiplist *sl, double score) {
	skiplistNode *node;
	int i;

	node = sl->header;
	for (i = sl->level-1; i >= 0 ;i--) {
		while(node->level[i].forward && node->level[i].forward->score < score) {
			node = node->level[i].forward;
		}
	}
	node = node->level[0].forward;
	if (node && score == node->score) {
		printf("Found %d\n",(int)node->score);
		return 1;
	} else {
		printf("Not found %d\n", (int)score);
		return 0;
	}
}

void slPrint(skiplist *sl) {
	skiplistNode *node;
	int i;
	for (i = 0; i < SKIPLIST_MAXLEVEL; i++) {
		printf("LEVEL[%d]: ", i);
		node = sl->header->level[i].forward;
		while(node) {
			printf("%d -> ", (int)(node->score));
			node = node->level[i].forward;
		}
		printf("NULL\n");
	}
}

#ifdef SKIP_LIST_TEST_MAIN
int main() {
	srand((unsigned)time(0));
	int count = 20, i;

	printf("### Function Test ###\n");

	printf("=== Init Skip List ===\n");
	skiplist * sl = slCreate();
	for ( i = 0; i < count; i++) {
		slInsert(sl,i);
	}
	printf("=== Print Skip List ===\n");
	slPrint(sl);

	printf("=== Search Skip List ===\n");
	for (i = 0; i < count; i++) {
		int value = rand()%(count+10);
		slSearch(sl, value);
	}
	printf("=== Delete Skip List ===\n");
	for (i = 0; i < count+10; i+=2) {
		printf("Delete[%d]: %s\n", i, slDelete(sl, i)?"SUCCESS":"NOT FOUND");
	}
	slPrint(sl);

	slFree(sl);
	sl = NULL;
	system("pause");

	return 0;
}
#endif


总结:

skip list非常实用,在Redis等很多项目中都用到,相比红黑树、平衡树,实现更加简单,而且效率也非常高。


修订:

这个博客写的很好,而且分析时间和空间复杂度。http://www.cnblogs.com/flyfy1/archive/2011/02/24/1963347.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值