圖 - 圖基礎(chǔ)和圖存儲

樹和圖都是非線性表數(shù)據(jù)結(jié)構(gòu)继蜡,之前我們已經(jīng)學(xué)習(xí)了樹,現(xiàn)在我們就學(xué)習(xí)更為復(fù)雜的圖逛腿。

圖的概念

在這里主要介紹圖的類型以及關(guān)于圖的一些概念稀并,我們會使用 微信、微博单默、QQ 三種社交軟件的社交關(guān)系作為例子幫助理解稻轨。

無向圖、頂點(diǎn)雕凹、邊、度

在樹中政冻,我們將元素成為節(jié)點(diǎn)枚抵,而在圖中的元素,我們叫做頂點(diǎn)(vertex)明场。兩個(gè)頂點(diǎn)之間建立的連接關(guān)系叫做邊(edge)汽摹。一個(gè)頂點(diǎn)有多少邊與之相連,成為一個(gè)頂點(diǎn)的度(degree)苦锨。你可以看一下下面的圖:

無向圖.jpg

圖的應(yīng)用非常廣泛逼泣,上面的圖是一個(gè)無向圖趴泌,我們可以用它表示微信中的社交關(guān)系:每個(gè)用戶看作一個(gè)頂點(diǎn)。如果兩個(gè)用戶之間互為好友拉庶,就在兩者之間建立一條邊嗜憔。一個(gè)人有多少好友,就是該頂點(diǎn)的度氏仗。

有向圖

微信中以互相添加好友作為建立邊的原則吉捶,這種社交關(guān)系在微博中不太一樣:在微博中,允許用戶單向關(guān)注皆尔,即用戶 A 關(guān)注了 B呐舔,而 B 不一定要關(guān)注 A。我們?nèi)绾斡脠D來表示這種關(guān)系呢慷蠕?

我們需要為圖引入“方向”的概念珊拼。

如果 A 關(guān)注了 B ,我們就在圖中畫一條從 A 到 B 的帶剪頭的邊流炕,使用剪頭表示邊的方向澎现。我們把這種有方向邊的圖稱作有向圖。類似的浪感,我們將之前講到的例子稱為無向圖昔头。

有向圖.jpg

在有向圖中,我們把度分為入度 和 出度
入度:表示有多少條邊指向這個(gè)頂點(diǎn)影兽;
出度:標(biāo)識有多少條邊以這個(gè)頂點(diǎn)為起點(diǎn)指向其它頂點(diǎn)揭斧。

帶權(quán)圖

QQ中的社交關(guān)系要更復(fù)雜一些,在兩個(gè)QQ好友之間會有“親密度”這樣一個(gè)功能峻堰。它不僅維護(hù)了兩個(gè)用戶之間的好友關(guān)系讹开,還記錄了兩個(gè)用戶之間的親密度。

在圖中捐名,我們使用帶權(quán)圖表示這種關(guān)系旦万。帶權(quán)圖中,每條邊都有一個(gè)權(quán)重镶蹋,我們可以通過這個(gè)權(quán)重來表示QQ好友之間的親密度:

帶權(quán)圖.jpg

圖的存儲

圖的存儲可以分為兩類:鄰接矩陣 和 鄰接表成艘。

鄰接矩陣(Adjacency Matrix)

圖最直觀的存儲方法是使用鄰接矩陣。

我們用一個(gè)二維數(shù)組記錄各個(gè)頂點(diǎn)之間的邊:

  • 無向圖:如果頂點(diǎn) i 與頂點(diǎn) j 之間有邊贺归,就將 A[i][j] 和 A[j][i] 標(biāo)記為 1淆两;
  • 有向圖:如果有一個(gè)邊從 i 指向 j ,將A[i][j] 標(biāo)記為 1 拂酣;
  • 帶權(quán)圖:在數(shù)組中存儲權(quán)重秋冰。
圖存儲-鄰接矩陣.jpg
鄰接矩陣的優(yōu)點(diǎn):
  • 鄰接矩陣的存儲方式簡單、直接婶熬,可以高效地獲取兩個(gè)頂點(diǎn)之間的關(guān)系剑勾。
  • 鄰接矩陣可以方便地使用矩陣之間的計(jì)算埃撵。
鄰接矩陣的缺點(diǎn):
  • 對于無向圖來說,鄰接矩陣是關(guān)于主軸對稱的虽另,這浪費(fèi)了一半的存儲空間暂刘。
  • 如果用鄰接矩陣存儲稀疏圖,會非常浪費(fèi)空間洲赵。比如微信有好幾億的用戶鸳惯,但是每個(gè)人的好友并不會很多。如果使用鄰接矩陣存儲叠萍,絕大部分的空間都會被浪費(fèi)掉芝发。

鄰接表(Adjacency List)

我們有另一種存儲圖的方法:使用鏈表記錄邊,也就是使用鄰接表苛谷。

鄰接表有點(diǎn)像散列表辅鲸,每個(gè)頂點(diǎn)會有一條鏈,鏈表中存儲的是與這個(gè)頂點(diǎn)相連的其它頂點(diǎn)(這兩個(gè)頂點(diǎn)之間有一條邊)腹殿。下圖是有向圖的存儲独悴,鏈表中存儲的頂點(diǎn)是指向的頂點(diǎn)。在無向圖中的存儲方式類似锣尉。
圖存儲-鄰接表.jpg
鄰接表的優(yōu)點(diǎn):
  • 鄰接表存儲比較節(jié)省空間刻炒。
  • 我們可以通過對鏈表的升級改造提升鄰接表的性能。例如自沧,將鏈表改造成紅黑樹或跳表坟奥,這樣可以支持快速查找。你甚至可以將鏈表改造成有序動態(tài)數(shù)組拇厢,用二分查找爱谁。(你可真是花哨 (→ . →) )
鄰接表的缺點(diǎn):
  • 鄰接表的存儲方式對緩存不夠友好。
  • 鄰接表比鄰接矩陣更耗時(shí)間孝偎。
  • 鄰接表記錄了頂點(diǎn)出度信息访敌,如果要查詢?nèi)攵刃畔ⅲ托枰~外的逆鄰接表衣盾。

應(yīng)用場景

如何存儲微博的好友關(guān)系寺旺?

數(shù)據(jù)結(jié)構(gòu)是為算法服務(wù)的,所以具體選擇哪種存儲方式势决,與期望的操作有關(guān)迅涮。對微博的用戶關(guān)系,假設(shè)我們需要支持下面的操作:

  • 判斷用戶 A 是否關(guān)注的 B徽龟。
  • A 關(guān)注 B。
  • A 取消關(guān)注 B唉地。
  • 根據(jù)用戶名稱的首字母排序据悔,分頁獲取用戶的粉絲列表传透。
  • 根據(jù)用戶名稱的首字母排序,分頁獲取用戶的關(guān)注列表极颓。

分析和選取

  • 社交網(wǎng)絡(luò)是一張稀疏圖朱盐,左移我們使用鄰接表存儲。
  • 一張鄰接表只能查詢某個(gè)用戶的關(guān)注列表菠隆,無法查詢他被誰關(guān)注(也就是粉絲列表)兵琳。這里就需要引入逆鄰接表表示某個(gè)用戶被關(guān)注的關(guān)系:
    逆鄰接表.jpg
  • 某個(gè) 大V 可能有茫茫多的粉絲,這就對列表的查找性能提出了要求骇径,同時(shí)我們又需要對用戶進(jìn)行分頁展示躯肌,使用跳表替代鏈表是一個(gè)非常好的選擇。
  • 微博有龐大的用戶破衔,一臺電腦的內(nèi)存是無法存儲的清女,這需要我們對鄰接表和逆鄰接表進(jìn)行分布存取,具體思路在之前的哈希應(yīng)用中已經(jīng)講過:
    分布式存儲圖.jpg
  • 當(dāng)然晰筛,你也可以使用數(shù)據(jù)庫完成存儲:
    圖存儲-數(shù)據(jù)庫存儲.jpg

綜上嫡丙,我們需要使用鄰接表和逆鄰接表作為存儲的基本結(jié)構(gòu),使用跳表代替鏈表方便查詢读第,使用分布式的思想保存如此龐大的數(shù)據(jù)曙博。(有沒有對這個(gè)分析過程感到神奇?)

小結(jié)

  • 理解了幾個(gè)概念:無向圖怜瞒、有向圖父泳、帶權(quán)圖、頂點(diǎn)盼砍、邊尘吗、度、入度浇坐、出度睬捶。
  • 學(xué)習(xí)了兩種圖的主要存儲方式:鄰接矩陣 和 鄰接表。
  • 鄰接矩陣:查詢效率高近刘,方便矩陣運(yùn)算擒贸。但是特別耗費(fèi)空間。
  • 鄰接表:節(jié)省內(nèi)存空間觉渴,但是它的查詢效率較低介劫,不方便查找。針對這個(gè)問題案淋,我們可以對鏈表進(jìn)行改造以適應(yīng)各種需求座韵。

以上就是本節(jié)的內(nèi)容

注:本文章的主要內(nèi)容來自我對極客時(shí)間app的《數(shù)據(jù)結(jié)構(gòu)與算法之美》專欄的總結(jié),我大量引用了其中的代碼和截圖,如果想要了解具體內(nèi)容誉碴,可以前往極客時(shí)間

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末宦棺,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子黔帕,更是在濱河造成了極大的恐慌代咸,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,204評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件成黄,死亡現(xiàn)場離奇詭異呐芥,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)奋岁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評論 3 395
  • 文/潘曉璐 我一進(jìn)店門思瘟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人厦取,你說我怎么就攤上這事潮太。” “怎么了虾攻?”我有些...
    開封第一講書人閱讀 164,548評論 0 354
  • 文/不壞的土叔 我叫張陵铡买,是天一觀的道長。 經(jīng)常有香客問我霎箍,道長奇钞,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,657評論 1 293
  • 正文 為了忘掉前任漂坏,我火速辦了婚禮景埃,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘顶别。我一直安慰自己谷徙,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評論 6 392
  • 文/花漫 我一把揭開白布驯绎。 她就那樣靜靜地躺著完慧,像睡著了一般。 火紅的嫁衣襯著肌膚如雪剩失。 梳的紋絲不亂的頭發(fā)上屈尼,一...
    開封第一講書人閱讀 51,554評論 1 305
  • 那天,我揣著相機(jī)與錄音拴孤,去河邊找鬼脾歧。 笑死,一個(gè)胖子當(dāng)著我的面吹牛演熟,可吹牛的內(nèi)容都是我干的鞭执。 我是一名探鬼主播,決...
    沈念sama閱讀 40,302評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼兄纺!你這毒婦竟也來了免猾?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,216評論 0 276
  • 序言:老撾萬榮一對情侶失蹤囤热,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后获三,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體旁蔼,經(jīng)...
    沈念sama閱讀 45,661評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評論 3 336
  • 正文 我和宋清朗相戀三年疙教,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了棺聊。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,977評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡贞谓,死狀恐怖限佩,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情裸弦,我是刑警寧澤祟同,帶...
    沈念sama閱讀 35,697評論 5 347
  • 正文 年R本政府宣布,位于F島的核電站理疙,受9級特大地震影響晕城,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜窖贤,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評論 3 330
  • 文/蒙蒙 一砖顷、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧赃梧,春花似錦滤蝠、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至粤攒,卻和暖如春所森,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背夯接。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評論 1 270
  • 我被黑心中介騙來泰國打工焕济, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人盔几。 一個(gè)月前我還...
    沈念sama閱讀 48,138評論 3 370
  • 正文 我出身青樓晴弃,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子上鞠,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評論 2 355