首發於微信公衆號:幾何思惟算法
世界上任何兩個互不相識的人,最多隻須要經過6箇中間人,就能夠創建聯繫。
哈佛大學的社會心理學家米爾格蘭姆於1967設計了一個連鎖信件實驗。他將一套連鎖信件隨機發送給居住在內布拉斯加州奧馬哈的160我的,信中放了一個波士頓股票經紀人的名字,並要求每名收信人把這封信寄給本身認爲是比較接近這名股票經紀人的朋友。這位朋友收到信後,再把信寄給他認爲更接近這名股票經紀人的朋友。最終,大部分信件都寄到了這名股票經紀人手中,每封信平均經手6次到達。微信
例如你認識老王,老王認識李大爺,李大爺又認識某人,如此關聯,你和奧巴馬之間,最多隻差6我的介紹就能夠加微信好友啦。設計
若是我如今知道了全部人的通信錄好友,我想知道我到底能不能認識老奧,怎麼驗證呢?
全球有77億人口,每一個人的好友圈也有幾百上千,這樣的數據量是很大的,簡單的一個一個的查找是行不通的。3d
那麼問題來了,人口普查哪家強,四川成都找老王。。。
全部的信息數據以下表:code
轉換成圖的形式會比較直觀。若是把2個互相認識的人用線鏈接起來,問題就轉化成:你和老奧之間可否找到一條通路(暫不考慮最短是否是不超過6我的)。blog
假設朋友的朋友都是朋友,朋友的敵人也是朋友(或者敵人的朋友仍是朋友,whatever...)。io
咱們把全部直接認識的,或者能間接認識的都放到一個大集合中,創建一個大朋友圈。
問題就變成:老奧在不在咱們的大朋友圈裏?class
若是你的大朋友圈裏面有人認識川普,那就要把川普的朋友圈裏面的全部人都加進來,造成一個新的朋友圈。效率
相信敏銳的你已經發現問題的本質,這裏面只有2個重要的操做,來跟我一塊兒大聲朗讀,並...查...。這就須要一種能高效處理集合的合併與查找的算法,並查集就是專門爲這種場景量身定製。二維碼
並查集本質是一個森林,裏面有不少樹。
每一個樹有一個根,以不一樣的根表明不一樣的集合。以下,root1,root2表明兩個集合。
初始時,每一個元素都屬於一個獨立的集合,該元素做爲根。每一個根指向一個虛擬根-n,表明權重(表示該集合有n個元素)。
更新合併
將權重小的集合的根指向權重大的集合的根(此操做是爲儘可能下降樹的深度)。
查找
判斷2個元素是否屬同一集合,只需向上查找根,再判斷是否相同。
過程當中作路徑壓縮,加快下一次查找速度。
查找
int findFather(int s) { int root = s, temp; // 查找s的最頂層根 while (father[root] >= 0) { root = father[root]; } // 路徑壓縮,提升後續查找效率 while (s != root) { temp = father[s]; father[s] = root; s = temp; } return root; }
合併
void unionSet(int s, int e) { int rootS = findFather(s); int rootE = findFather(e); int weight = father[rootS] + father[rootE]; // 將結點數少的集合做爲結點數多的集合的兒子節點 if (father[rootS] > father[rootE]) { father[rootS] = rootE; father[rootE] = weight; } else { father[rootE] = rootS; father[rootS] = weight; } }
例題,poj1182,poj1308,poj1456,poj1611
掃描下方二維碼關注公衆號,第一時間獲取更新信息!