版本號標記

這是一個處理需要反覆標記的問題的一個小技巧,以一個ACM形式的題目爲例: 
輸入:第一行是一個數字N,表示N個case,之後每個case第一行是一個數字M,表示這個case有M個數字輸入,接下來是M個數字,每個數字範圍是0<=n<K 
輸出:對每個case,輸出M個數字去重後的數量 

當然,這題本身沒什麼難度,弄個hash_set就行了,不過爲了說明問題,我們假定做題的人比較笨,使用bitmap: 
bm = BitMap(K) 
N = read_int() 
for i from 1 to N: 
    M = read_int() 
    for j from 1 to M: 
        bm.set(read_int()) 
    count = 0 
    for k from 0 to K-1: 
        if bm.is_set(k): 
            count += 1 
            bm.unset(k) 
    print count 

構造一個bitmap,對於每個case,設置對應的位,最後統計數量,這裏借鑑了上一篇中的做法,統計的時候順便unset,就省去了初始化,空間和時間複雜度都是O(K),指數級的,所以說這是一個比較笨的算法(爲何是指數級,後面再說) 

換一種思路,對於每個case的M個數字,如果每次read_int後判斷下是否已在位圖,則可以實時統計count,修改j的循環爲: 
bm.reset() 
count = 0 
for j from 1 to M: 
    num = read_int() 
    if bm.is_set(num): 
        continue 
    count += 1 
    bm.set(num) 

不過這並沒什麼改善,雖然後面k的循環省了,bm.reset()依然要遍歷 

由於bitmap中每個元素只有0和1兩種狀態,每個case是獨立的,因此每次要初始化(上述兩種方案只是初始化方式不同),如果將狀態擴展爲多個,就能避免反覆初始化: 
tag = new int[K]; //假設自動初始化爲全0 
N = read_int() 
for i from 1 to N: 
    M = read_int() 
    count = 0 
    for j from 1 to M: 
        num = read_int() 
        if tag[num] == i: 
            continue 
        count += 1 
        tag[num] = i 
    print count 

用一個整數數組tag代替bitmap,初始化爲0,對於第i個case,用i來標記num,這樣一來每個case雖然公用tag,但各自標記方法不同,就不用初始化了,不考慮tag的建立,就單個case來說,時間複雜度是O(M) 

於是,對於上篇末尾說的延遲清除,如果將marked和unmarked兩種狀態標記做一下改動,就可以區分已標記、待回收和新對象,而且省卻了全局初始化,代價就是垃圾收集器自己維護一個遞增的版本管理。不過,實際使用的算法不一定是這麼寫的
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章