外部排序算法相關:主要用到歸並排序,堆排序,桶排序,重點是先分成不同的塊,然后從每個塊中找到最小值寫入磁盤,分析過程可以看看http://blog.csdn.net/jeason29/article/details/50474772
hash值算法
1.題目描述
給定a、b兩個文件,各存放50億個url,每個url各占64字節,內存限制是4G,讓你找出a、b文件共同的url?
2.思考過程
(1)首先我們最常想到的方法是讀取文件a,建立哈希表(為什么要建立hash表?因為方便后面的查找),然后再讀取文件b,遍歷文件b中每個url,對於每個遍歷,我們都執行查找hash表的操作,若hash表中搜索到了,則說明兩文件共有,存入一個集合。
(2)但上述方法有一個明顯問題,加載一個文件的數據需要50億*64bytes = 320G遠遠大於4G內存,何況我們還需要分配哈希表數據結構所使用的空間,所以不可能一次性把文件中所有數據構建一個整體的hash表。
(3)針對上述問題,我們分治算法的思想。
step1:遍歷文件a,對每個url求取hash(url)%1000,然后根據所取得的值將url分別存儲到1000個小文件(記為a0,a1,...,a999,每個小文件約300M),為什么是1000?主要根據內存大小和要分治的文件大小來計算,我們就大致可以把320G大小分為1000份,每份大約300M(當然,到底能不能分布盡量均勻,得看hash函數的設計)
step2:遍歷文件b,采取和a相同的方式將url分別存儲到1000個小文件(記為b0,b1,...,b999)(為什么要這樣做? 文件a的hash映射和文件b的hash映射函數要保持一致,這樣的話相同的url就會保存在對應的小文件中,比如,如果a中有一個url記錄data1被hash到了a99文件中,那么如果b中也有相同url,則一定被hash到了b99中)
所以現在問題轉換成了:找出1000對小文件中每一對相同的url(不對應的小文件不可能有相同的url)
step3:因為每個hash大約300M,所以我們再可以采用(1)中的想法
http://blog.csdn.net/tiankong_/article/details/77234726
在所有具有性能優化的數據結構中,我想大家使用最多的就是hash表,是的,在具有定位查找上具有O(1)的常量時間,多么的簡潔優美,
但是在特定的場合下:
①:對10億個不重復的整數進行排序。
②:找出10億個數字中重復的數字。
當然我只有普通的服務器,就算2G的內存吧,在這種場景下,我們該如何更好的挑選數據結構和算法呢?
問題分析
這年頭,大牛們寫的排序算法也就那么幾個,首先我們算下放在內存中要多少G: (10億 * 32)/(1024*1024*1024*8)=3.6G,可憐
的2G內存直接爆掉,所以各種神馬的數據結構都玩不起來了,當然使用外排序還是可以解決問題的,由於要走IO所以暫時剔除,因為我們
要玩高性能,無望后我們想想可不可以在二進制位上做些手腳?
比如我要對{1,5,7,2}這四個byte類型的數字做排序,該怎么做呢?我們知道byte是占8個bit位,其實我們可以將數組中的值作為bit位的
key,value用”0,1“來標識該key是否出現過?下面看圖:
從圖中我們精彩的看到,我們的數組值都已經作為byte中的key了,最后我只要遍歷對應的bit位是否為1就可以了,那么自然就成有序數組了。
可能有人說,我增加一個13怎么辦?很簡單,一個字節可以存放8個數,那我只要兩個byte就可以解決問題了。
可以看出我將一個線性的數組變成了一個bit位的二維矩陣,最終我們需要的空間僅僅是:3.6G/32=0.1G即可,要注意的是bitmap排序不
是N的,而是取決於待排序數組中的最大值,在實際應用上關系也不大,比如我開10個線程去讀byte數組,那么復雜度為:O(Max/10)。
(上面摘自http://www.cnblogs.com/huangxincheng/archive/2012/12/06/2804756.html,省去了代碼部分,具體代碼分析可見下文)
bitmap算法解釋
一、bitmap算法思想
32位機器上,一個整形,比如int a; 在內存中占32bit位,可以用對應的32bit位對應十進制的0-31個數,bitmap算法利用這種思想處理大量數據的排序與查詢.
優點:1.運算效率高,不許進行比較和移位;2.占用內存少,比如N=10000000;只需占用內存為N/8=1250000Byte=1.25M。
缺點:所有的數據不能重復。即不可對重復的數據進行排序和查找。
比如:
第一個4就是
00000000000000000000000000010000
而輸入2的時候
00000000000000000000000000010100
輸入3時候
00000000000000000000000000011100
輸入1的時候
00000000000000000000000000011110
思想比較簡單,關鍵是十進制和二進制bit位需要一個map圖,把十進制的數映射到bit位。下面詳細說明這個map映射表。
二、map映射表
假設需要排序或者查找的總數N=10000000,那么我們需要申請內存空間的大小為int a[1 + N/32],其中:a[0]在內存中占32為可以對應十進制數0-31,依次類推:
bitmap表為:
a[0]--------->0-31
a[1]--------->32-63
a[2]--------->64-95
a[3]--------->96-127
..........
那么十進制數如何轉換為對應的bit位,下面介紹用位移將十進制數轉換為對應的bit位。
三、位移轉換
例如十進制0,對應在a[0]所占的bit為中的第一位:
00000000000000000000000000000001
0-31:對應在a[0]中
i =0 00000000000000000000000000000000
temp=0 00000000000000000000000000000000
answer=1 00000000000000000000000000000001
i =1 00000000000000000000000000000001
temp=1 00000000000000000000000000000001
answer=2 00000000000000000000000000000010
i =2 00000000000000000000000000000010
temp=2 00000000000000000000000000000010
answer=4 00000000000000000000000000000100
i =30 00000000000000000000000000011110
temp=30 00000000000000000000000000011110
answer=1073741824 01000000000000000000000000000000
i =31 00000000000000000000000000011111
temp=31 00000000000000000000000000011111
answer=-2147483648 10000000000000000000000000000000
32-63:對應在a[1]中
i =32 00000000000000000000000000100000
temp=0 00000000000000000000000000000000
answer=1 00000000000000000000000000000001
i =33 00000000000000000000000000100001
temp=1 00000000000000000000000000000001
answer=2 00000000000000000000000000000010
i =34 00000000000000000000000000100010
temp=2 00000000000000000000000000000010
answer=4 00000000000000000000000000000100
i =61 00000000000000000000000000111101
temp=29 00000000000000000000000000011101
answer=536870912 00100000000000000000000000000000
i =62 00000000000000000000000000111110
temp=30 00000000000000000000000000011110
answer=1073741824 01000000000000000000000000000000
i =63 00000000000000000000000000111111
temp=31 00000000000000000000000000011111
answer=-2147483648 10000000000000000000000000000000
淺析上面的對應表:
1.求十進制0-N對應在數組a中的下標:
十進制0-31,對應在a[0]中,先由十進制數n轉換為與32的余可轉化為對應在數組a中的下標。比如n=24,那么 n/32=0,則24對應在數組a中的下標為0。又比如n=60,那么n/32=1,則60對應在數組a中的下標為1,同理可以計算0-N在數組a中的下標。
2.求0-N對應0-31中的數:
十進制0-31就對應0-31,而32-63則對應也是0-31,即給定一個數n可以通過模32求得對應0-31中的數。
3.利用移位0-31使得對應32bit位為1.
四、編程實現
解析本例中的void set(int i) { a[i>>SHIFT] |= (1<<(i & MASK)); }
1.i>>SHIFT:
其中SHIFT=5,即i右移5為,2^5=32,相當於i/32,即求出十進制i對應在數組a中的下標。比如i=20,通過i>>SHIFT=20>>5=0 可求得i=20的下標為0;
2.i & MASK:
其中MASK=0X1F,十六進制轉化為十進制為31,二進制為0001 1111,i&(0001 1111)相當於保留i的后5位。
比如i=23,二進制為:0001 0111,那么
0001 0111
& 0001 1111 = 0001 0111 十進制為:23
比如i=83,二進制為:0000 0000 0101 0011,那么
0000 0000 0101 0011
& 0000 0000 0001 0000 = 0000 0000 0001 0011 十進制為:19
i & MASK相當於i%32。
3.1<<(i & MASK)
相當於把1左移 (i & MASK)位。
比如(i & MASK)=20,那么i<<20就相當於:
0000 0000 0000 0000 0000 0000 0000 0001 >>20
=0000 0000 0000 1000 0000 0000 0000 0000
4.void set(int i) { a[i>>SHIFT] |= (1<<(i & MASK)); }等價於:
void set(int i)
{
a[i/32] |= (1<<(i%32));
}
問題:
解決法案:
遍歷法
直接尋址表法
a | 0 | 1 | 2 | ...... | 1000022 | ..... | 100000030 | ... | 2*32- 1 |
flag | 0 | 1 | 1 | 1 | 0 | 0 | 1 | 0 | 1 |
Bit-Map
a | 0 | 1 | ...... | 2*32 / 8- 1 |
bit | 0 1 2 3 4 5 6 7 | 0 1 2 3 4 5 6 7 | ...... | 0 1 2 3 4 5 6 7 |
flag | 0 0 0 0 0 0 0 0 | 0 0 1 0 0 0 0 0 | ...... | 0 0 0 0 0 0 0 0 |
BitMap 應用
枚舉
1.全組合
字符串全組合枚舉(對於長度為n的字符串,組合方式有2^n種),如:abcdef,可以構造一個從字符串到二進制的映射關系,通過枚舉二進制來進行全排序。
null --> 000000
f --> 000001
e --> 000010
ef --> 000011
……
abcedf --> 111111
2.哈米爾頓距離
給定N(1<=N<=100000)個五維的點A(x1,x2,x3,x4,x5),求兩個點X(x1,x2,x3,x4,x5)和Y(y1,y2,y3,y4,y5),使得他們的哈密頓距離(d=|x1-y1| + |x2-y2| + |x3-y3| + |x4-y4| + |x5-y5|)最大。
搜索
爬蟲系統中常用的URL去重(Bloom Filter算法)
壓縮
在2.5億個整數中找出不重復的整數,注,內存不足以容納這2.5億個整數?
給40億個不重復的unsigned int的整數,沒排過序的,然后再給一個數,如何快速判斷這個數是否在那40億個數當中?
位排序
(以上摘抄自http://blog.csdn.net/qq_26891045/article/details/51137589)
另:可以看看KMP(字符串匹配算法)