poj2828 無鍵跳表

2021-07-16 18:53:16 字數 3414 閱讀 3185

所以我覺得這個題吧,其實正解是個跳表。

1.首先我們不考慮跳表,考慮二叉搜尋樹。

如果這個題用二叉搜尋樹,怎麼做呢?

考慮一下啊。

首先很容易知道每個人名字和點的鍵值key是沒關係的對吧。

那麼人的名字就只能作為value了對吧。

那我們需要個key。

那麼,key要保證什麼條件呢?

很明顯每次插進去乙個點q,插在b點的位置上,而a是b點前的點。

那麼插入前:

a->b

插入後:

a->q->b

對吧。那麼就要求q.key>a.key , q.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跳表你們隨便去找個人部落格看看就行了,那個講的人很多的,

:我的跳表是用這份改的,並沒有刻意抹痕跡所以大家還是可以知道**對應**的

有key跳表的節點:

struct node ;

其中,next指向的就是每層的下乙個節點

無key跳表的節點:

struct node ;
可以注意到我們去掉了乙個key,而新增了乙個seg陣列。

seg[i]記錄的是this到next[i]之間的距離值

這個題只有乙個插入操作對吧。

大體的思路是,插入的時候,除了update陣列以外,我們還要記錄乙個updateseg陣列,用來記錄當前update[i]的key。

這個key並不是記錄在節點裡的,而是從表頭往後,乙個乙個seg[i]加上去的。

就是說,每次遍歷到這個點的時候,用路徑上的乙個個seg來加出來當前的key,然後存進陣列。

然後根據兩個陣列判斷一下,做個插入操作就行了。

思路很簡單。

具體可以看我**= =

**寫的很醜見諒啊。

#include#include#include#include#include//#include//#include//#include//#include//#include//#include//#includeusing namespace std;

#define maxn 200008

#define max_level 17

//節點

struct node

}};node nodes[maxn];

int nodecnt;

//建立節點

node* createnode(int level, int value)

//隨機產生層數

int randomlevel()

//跳表

struct skiplist

//初始化跳表

void init()

//插入節點(把所有後面的節點後移乙個)

int insertnode(int key, int value)

int updateseg[max_level];

for (int i = 0; i < max_level; i++)

node *p, *q;

q = null;

p = header;

int k = level;

//從最高層往下查詢需要插入的位置

//填充update

int nowkey = 0;

for (int i = level - 1; i >= 0; i--)

updateseg[i] = nowkey;

update[i] = p;

}// if (q != null && q->key == key)

//產生乙個隨機層數k

//新建乙個待插入節點q

//一層一層插入

k = randomlevel();

//更新跳表的level

if (k > level)

level = k;

} q = createnode(k, value);

//逐層更新節點的指標,和普通列表插入一樣

for (int i = 0; i < k; i++) else else else }}

// }

for (int i = k; i < level; i++)

} size++;

return 1;

} void print()

//}printf("\n");

} void test_print()

printf("\n");

} printf("\n");

} void ppp()

printf("\n");

for (int j = 0; j < max_level; j++)

printf("\n");

printf("\n");

} }};skiplist sl;

int n;

int main()

sl.print();

} return 0;

}

POJ 2828解題報告

題目不再重述。我也是看了解題報告的.之前怎麼都想不到,如果正常模擬的話,每一次插入元素都必須準確的知道位置在pos的元素究竟是哪乙個,知道連在那乙個元素後面,才能正確的串成乙個隊嘛。如果用陣列 向中間加元素要乙個乙個的挪 用鍊錶 n 2 超時了。其他的資料結構?怎麼都想不到一種可以動態維護記錄每個元...

線段數應用 poj2828

線段數應用 題目 buy tickets 題目大意 n個人排隊等待,第 i個人到來會有兩個資訊 pos i 0,i 1 val i 表明這 個人會插隊到 pos i 的後邊,這個人的值是 val i 依次給出 n個人的到來資訊,輸出最終的 佇列從頭到尾每個人的值。思路 第 i個人插入的位置是 pos...

線段樹典型例題 poj2828

逆序處理。注意到如果逆序插入,則每次插入的位置都是第x個空位。所以可以用線段樹來尋找第x個合法位置 include include include include include using namespace std const int n 200005 int sum n 3 pos n val...