这个题很多人用线段树过了。那个做法我看了,是很好的,可惜是个离线。而有的时候我们需要在线。比方说,把题面改改,倘若再给你个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; }
