所以我覺得這個題吧,其實正解是個跳表。
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...