并查集與類型推導(dǎo)

某次參加筆試的最后一題大意如下:給定一組用戶[0..n]皆辽,以及他們之間的好友關(guān)系因苹,問這些好友構(gòu)成了多少個(gè)朋友圈缕减?
例如有用戶[1..5]庭再,好友關(guān)系有(1,2),(3,4),(4,5)捞奕,則共有兩個(gè)好友圈:[1,2]和[3,4,5]

并查集

題目的意思可以理解為給定一個(gè)無向圖,求其中連通子圖的個(gè)數(shù)拄轻。算法來自于《算法導(dǎo)論(第二版)》的第21章
對(duì)于一個(gè)并查集來說颅围,我們通常希望其支持三種操作:
MakeSet(x):當(dāng)我們新加入一個(gè)元素,且與當(dāng)前任何節(jié)點(diǎn)有連接恨搓,因此它(目前為止)單獨(dú)成為一個(gè)集合院促。
Union(x,y):添加兩個(gè)元素之間的聯(lián)系筏养,因此兩個(gè)元素屬于的集合需要合并成為一個(gè)集合。
FindSet(x):查詢一個(gè)元素到底屬于哪個(gè)集合常拓。
顯然當(dāng)有n個(gè)元素時(shí)渐溶,Union操作至多有n-1次,MakeSet操作至多為n次弄抬。

代碼實(shí)現(xiàn)

可以用一個(gè)數(shù)組p來儲(chǔ)存所有元素對(duì)應(yīng)的集合茎辐。例如p[x]=1表示元素x屬于編號(hào)為1的集合。然而整個(gè)編號(hào)為1的集合可能又屬于另一個(gè)更大的集合掂恕,因此需要查詢p[1]來找到其父集合……以此類推拖陆,直到有p[a]=a為止。由此可見懊亡,這其實(shí)是一種基于樹的實(shí)現(xiàn)依啰。

圖片源自《算法導(dǎo)論》

  • 兩種策略
  • 按秩合并(union by rank)
    rank可以看作是每個(gè)根節(jié)點(diǎn)的高度(并不嚴(yán)格是,其實(shí)是其高度的一個(gè)上界)店枣,合并兩棵樹樹時(shí)速警,將高度較小的樹的根指向高度較大的樹可以保證合并后的樹的高度上界不再增加。若高度相等鸯两,則合并后的樹高度加一闷旧。
  • 路徑壓縮(path compression)
    當(dāng)從某一結(jié)點(diǎn)以此查找到根節(jié)點(diǎn)時(shí),可以將經(jīng)過路徑的所有節(jié)點(diǎn)的父節(jié)點(diǎn)都修改為根節(jié)點(diǎn)甩卓。但我們的方法中并不修改根節(jié)點(diǎn)的秩鸠匀,因?yàn)樾薷囊活w樹的高度需要遍歷整棵樹的所有節(jié)點(diǎn)才能確定,這顯然得不償失逾柿,這也就解釋了為什么秩并不是每棵樹的高度,而是它高度的一個(gè)上界宅此。
圖片源自《算法導(dǎo)論》
int MakeSet(int x)
{
    p[x]=x;
    rank[x]=0;
}

void Union(int a,int b)
{
    int x=FindSet(a);
    int y=FindSet(b);
    if(rank[x]>rank[y])
    {
        p[y]=x;
    }
    else
    {
        p[x]=y;
    }
    if(rank[x]==rank[y])
    {
        rank[y]=rank[y]+1;
    }
}

int FindSet(int x)
{
    if(x!=p[x])
    {
        p[x]=FindSet(p[x]);
    }
    return p[x];
}

MakeSet机错、UnionFindSet操作共m個(gè),其中有n個(gè)MakeSet操作父腕,單獨(dú)使用按秩合并的策略運(yùn)行時(shí)間為


單獨(dú)使用路徑壓縮弱匪,若有n個(gè)MakeSet操作和f個(gè)FindSet操作,運(yùn)行時(shí)間為![](http://latex.codecogs.com/svg.latex?\Theta\left(n+f\cdot\left(1+\textup{log}_{2+f/n}n\right)\right)

類型推導(dǎo)

在一個(gè)函數(shù)體中璧亮,各個(gè)參數(shù)的相互作用可以看作是一個(gè)無向有環(huán)圖(DAG)萧诫。依據(jù)函數(shù)內(nèi)的各個(gè)節(jié)點(diǎn),可以生成一系列的類型約束等式枝嘶。

圖片來自Essentials of Porgramming languages

圖中的代碼proc(f)proc(x)-((f 3),(f x))等價(jià)于如下scheme代碼:

(lambda (f) (lambda (x) (- (f 3) (f x))))

或者如下C++代碼

template<typename T3,typename Tf,typename Tx>
T3 func(Tf f, Tx x)
{
  return (f 3)-(f x);
}

約束生成(constraints generation)的規(guī)則如下:


內(nèi)容來自Programming Languages and Lambda Calculi

得到類型等式后帘饶,接下來是對(duì)等式進(jìn)行合一(unify)和替換(substitute)操作。
合并規(guī)則如下:


內(nèi)容來自Programming Languages and Lambda Calculi

關(guān)于多態(tài)的推導(dǎo):

內(nèi)容來自Programming Languages and Lambda Calculi

下面是Essentials of Programming Languages中的步驟演示


可以看到群扶,每一個(gè)等式及刻,都需要進(jìn)行unify操作镀裤,因?yàn)轭愋拖嗟燃创硭鼈儗儆谕患稀5枰⒁獾氖墙煞梗@里的unify操作和之前提到的union并不完全相同暑劝,因?yàn)槲覀冞€需要處理函數(shù)類型。若有類似t1->t2=t3->t4的等式颗搂,則需要同時(shí)unify(t1,t3)unify(t2,t4)担猛。另一方面,在合并類型時(shí)可能是有方向的丢氢,需要將泛化(generic)類型向具化(normalized)類型合并傅联。例如有t1=t2,t2=int,則得到t1=int,t2=int卖丸,這也意味這上文提到的按秩合并的策略在此并不可用纺且,但路徑壓縮依然可行。

更多閱讀

  • 《算法導(dǎo)論》中關(guān)于并查集算法時(shí)間復(fù)雜度的證明
  • Hindley-Milner類型系統(tǒng)
  • 邏輯式編程與合一算法
  • Recursive Type(本文中的類型推導(dǎo)僅對(duì)STLC(simply typed lambda calculus)成立稍浆,根據(jù)STLC的strong normalization特性载碌,所有STLC能表達(dá)的程序中不會(huì)有遞歸且一定終止,如果突破這一限制呢衅枫?)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末嫁艇,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子弦撩,更是在濱河造成了極大的恐慌步咪,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,695評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件益楼,死亡現(xiàn)場(chǎng)離奇詭異猾漫,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)感凤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門悯周,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人陪竿,你說我怎么就攤上這事禽翼。” “怎么了族跛?”我有些...
    開封第一講書人閱讀 168,130評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵闰挡,是天一觀的道長。 經(jīng)常有香客問我礁哄,道長长酗,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,648評(píng)論 1 297
  • 正文 為了忘掉前任姐仅,我火速辦了婚禮花枫,結(jié)果婚禮上刻盐,老公的妹妹穿的比我還像新娘。我一直安慰自己劳翰,他們只是感情好敦锌,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,655評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著佳簸,像睡著了一般乙墙。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上生均,一...
    開封第一講書人閱讀 52,268評(píng)論 1 309
  • 那天听想,我揣著相機(jī)與錄音,去河邊找鬼马胧。 笑死汉买,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的佩脊。 我是一名探鬼主播蛙粘,決...
    沈念sama閱讀 40,835評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼威彰!你這毒婦竟也來了出牧?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,740評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤歇盼,失蹤者是張志新(化名)和其女友劉穎舔痕,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體豹缀,經(jīng)...
    沈念sama閱讀 46,286評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡伯复,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,375評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了邢笙。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片边翼。...
    茶點(diǎn)故事閱讀 40,505評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖鸣剪,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情丈积,我是刑警寧澤筐骇,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站江滨,受9級(jí)特大地震影響铛纬,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜唬滑,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,873評(píng)論 3 333
  • 文/蒙蒙 一告唆、第九天 我趴在偏房一處隱蔽的房頂上張望棺弊。 院中可真熱鬧,春花似錦擒悬、人聲如沸模她。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽侈净。三九已至,卻和暖如春僧凤,著一層夾襖步出監(jiān)牢的瞬間畜侦,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評(píng)論 1 272
  • 我被黑心中介騙來泰國打工躯保, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留旋膳,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,921評(píng)論 3 376
  • 正文 我出身青樓途事,卻偏偏與公主長得像验懊,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子盯孙,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,515評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容