【poj2828】无键跳表

这个题很多人用线段树过了。那个做法我看了,是很好的,可惜是个离线。而有的时候我们需要在线。比方说,把题面改改,倘若再给你个n倍时间,让你每插入一个人就把整个表输出一遍,你再倒过来还能做吗?那就很难了对吧。

所以我觉得这个题吧,其实正解是个跳表。

1.

首先我们不考虑跳表,考虑二叉搜索树。

如果这个题用二叉搜索树,怎么做呢?

考虑一下啊。

首先很容易知道每个人名字和点的键值key是没关系的对吧。

那么人的名字就只能作为value了对吧。

那我们需要个key。

那么,key要保证什么条件呢?

很明显每次插进去一个点q,插在b点的位置上,而a是b点前的点。

那么插入前:

a->b

插入后:

a->q->b

对吧。

那么就要求q.key>a.key , q.key<b.key

能够想到的一个很简单的办法就是q.key = (a.key+b.key)/2

那么问题解决了:

我们首先给队首加个虚点key=0,给队尾加个虚点key = DOUBLE_MAX;

然后每次查询找到要插入的位置(二叉搜索树以key的大小二叉)上的点b,然后找他的前面的点a,然后你就知道了q的key,value赋进去,然后插进去就行了。

所以本题就这么结束了。。。才没有!

我们稍稍动动脑筋想想就知道,double最大值才1e300多点,你这边倒好一下子弄了个2e5的插入,那么我们需要(最糟糕的情况)分割成 2^(2e5) 段,很明显每一段的间隔是小数点后很多很多位,这种情况下我们比较double的大小真的可以做到吗?很明显不行对不对!

好了,问题来了,现在我们来解决它。

用key判断不是精度不够吗?我们不用key不就好了!

——解决个毛啊,二叉搜索树没有key还搜个屁。。。

这就是为什么这个题要用跳表。

因为,跳表是可以没有key的。

= =当然了有key的跳表会更加好写就是了。

2.

Q:什么是跳表

A:网上很多教程了,都挺不错的,再写就浪费资源了。

Q:跳表有什么用

A:二叉搜索树能做的都能做。

Q:那么比二叉搜索树好在哪?

A:好在它不用key的大小来指定节点的先后关系,而是多条重叠的链表一样,指针指定先后顺序的。这样就可以搞不方便建key的题。

而且,多线程的时候似乎比二叉搜索树性能好。(只是听过有这么个说法,然而我并没有验证过)

Q:那么比二叉搜索树差在哪?

A:如果可以建key:需要花费n*logn的空间。

如果不可以建key:需要花费2*n*logn的空间。

空间复杂度不优。


3.如何从有key的跳表转化为无key的跳表。

有key跳表你们随便去找个人博客看看就行了,那个讲的人很多的,

http://www.cppblog.com/mysileng/archive/2013/04/06/199159.html

我的跳表是用这份改的,并没有刻意抹痕迹所以大家还是可以知道哪里对应哪里的

有key跳表的节点:

struct node {
	int key;
	int value;
	node *next[MAX_LEVEL];
};

其中,next指向的就是每层的下一个节点

无key跳表的节点:

struct node {
	int value;
	node *next[MAX_LEVEL];
	int seg[MAX_LEVEL];
};
可以注意到我们去掉了一个key,而新增了一个seg数组。

seg[i]记录的是this到next[i]之间的距离值

这个题只有一个插入操作对吧。

大体的思路是,插入的时候,除了update数组以外,我们还要记录一个updateseg数组,用来记录当前update[i]的key。

这个key并不是记录在节点里的,而是从表头往后,一个一个seg[i]加上去的。

就是说,每次遍历到这个点的时候,用路径上的一个个seg来加出来当前的key,然后存进数组。

然后根据两个数组判断一下,做个插入操作就行了。

思路很简单。

具体可以看我代码= =

代码写的很丑见谅啊。

#include<stdio.h>
#include<string.h>
#include<math.h>
#include<iostream>
#include<cstdlib>

//#include<algorithm>
//#include<map>
//#include<deque>
//#include<vector>
//#include<set>
//#include<bits/stdc++.h>
//#include<tr1/unordered_map>

using namespace std;

#define MAXN 200008
#define MAX_LEVEL 17

//节点
struct node {
	int value;
	node *next[MAX_LEVEL];
	int seg[MAX_LEVEL];

	node() {
		value = 0;
		for (int i = 0; i < MAX_LEVEL; i++) {
			next[i] = NULL;
			seg[i] = 0;
		}
	}
};

node nodes[MAXN];
int nodecnt;

//创建节点
node* createNode(int level, int value) {
	nodes[nodecnt] = node();
	nodes[nodecnt].value = value;
	nodecnt++;
	return nodes + (nodecnt - 1);
}

//随机产生层数
int randomLevel() {
	int k = 1;
	while (rand() % 2)
		k++;
	k = (k < MAX_LEVEL) ? k : MAX_LEVEL - 1;
	return k;
}

//跳表
struct skiplist {
	int level;
	int size;
	node *header;

	skiplist() {
		level = 0;
		size = 0;
		header = NULL;
	}

	//初始化跳表
	void init() {
		level = 0;
		size = 0;
		header = NULL;
		nodecnt = 0;
		header = createNode(0, 0);
	}

	//插入节点(把所有后面的节点后移一个)
	int insertNode(int key, int value) {

		node *update[MAX_LEVEL];
		for (int i = 0; i < MAX_LEVEL; i++) {
			update[i] = header;
		}
		int updateseg[MAX_LEVEL];
		for (int i = 0; i < MAX_LEVEL; i++) {
			updateseg[i] = 0;
		}

		node *p, *q;
		q = NULL;
		p = header;
		int k = level;
		//从最高层往下查找需要插入的位置
		//填充update

		int nowkey = 0;
		for (int i = level - 1; i >= 0; i--) {
			while (((q = p->next[i]) != NULL) && (nowkey + p->seg[i] < key)) {
				nowkey += p->seg[i];
				p = q;
			}
			updateseg[i] = nowkey;
			update[i] = p;
		}

//		if (q != NULL && q->key == key) {
//			return;
//		}

		//产生一个随机层数K
		//新建一个待插入节点q
		//一层一层插入
		k = randomLevel();
		//更新跳表的level
		if (k > level) {
			for (int i = level; i < k; i++) {
				updateseg[i] = -1;
			}
			level = k;
		}

		q = createNode(k, value);
		//逐层更新节点的指针,和普通列表插入一样
		for (int i = 0; i < k; i++) {

			node* u = update[i];
			if (updateseg[i] == -1) {
				//printf("IMHERE!!!\N");
				header->seg[i] = key;
				header->next[i] = q;
			} else {
				if (updateseg[i] + u->seg[i] == key) {
					node* a = u;
					u = u->next[i];
					q->seg[i] = 1;
					q->next[i] = u;
					a->next[i] = q;
				} else {
					if (update[i]->next[i] != NULL) {
						node* b = u->next[i];
						q->next[i] = b;
						q->seg[i] = u->seg[i] - (key - updateseg[i]) + 1;
						u->next[i] = q;
						u->seg[i] = key - updateseg[i];
						//
						//q->seg[i] = update[i]->seg[i];
					} else {
						u->next[i] = q;
						u->seg[i] = key - updateseg[i];
					}
				}
			}
//
		}
		for (int i = k; i < level; i++) {
			node* u = update[i];
			if (u->next[i] != NULL) {
				u->seg[i]++;
			}
		}
		size++;
		return 1;
	}

	void print() {
		node *p, *q;
		p = header;
		q = NULL;
		int k = level;
		int flag0 = 0;

		//for (int i = k - 1; i >= 0; i--) {
		while ((q = p->next[0]) != NULL) {
			if (flag0)
				printf(" ");
			else
				flag0 = 1;

			printf("%d", q->value);
			p = q;
		}
		//}
		printf("\n");
	}

	void test_print() {
		//从最高层开始打印
		node *p, *q = NULL;
		//从最高层开始搜
		int k = level;
		for (int i = k - 1; i >= 0; i--) {
			p = header;
			while ((q = p->next[i]) != NULL) {
				printf("%d -> ", q->value);
				p = q;
			}
			printf("\n");
		}
		printf("\n");
	}

	void ppp() {
		for (int i = 0; i < 10; i++) {
			for (int j = 0; j < MAX_LEVEL; j++) {
				printf("%d  ", nodes[i].next[j] - nodes);
			}
			printf("\n");
			for (int j = 0; j < MAX_LEVEL; j++) {
				printf("%d  ", nodes[i].seg[j]);

			}
			printf("\n");
			printf("\n");
		}
	}
};

skiplist SL;
int n;

int main() {
	while (scanf("%d", &n) != EOF) {
		//scanf("%d", &n);
		SL.init();
		int l, r;
		for (int i = 0; i < n; i++) {
			scanf("%d", &l);
			scanf("%d", &r);
			SL.insertNode(l + 1, r);
		}
		SL.print();
		//SL.ppp();
	}
	return 0;
}







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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值