在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

圖的邏輯結構是怎樣的?如何去實現它?

算法與數據結構 ? 來源:labuladong ? 作者:labuladong ? 2021-05-08 16:34 ? 次閱讀

經常有讀者問我「圖」這種數據結構,因為我們公眾號什么數據結構和算法都寫過了,唯獨沒有專門介紹「圖」。

其實在學習數據結構和算法的框架思維中說過,雖然圖可以玩出更多的算法,解決更復雜的問題,但本質上圖可以認為是多叉樹的延伸。

面試筆試很少出現圖相關的問題,就算有,大多也是簡單的遍歷問題,基本上可以完全照搬多叉樹的遍歷。

至于最小生成樹,Dijkstra,網絡流這些算法問題,他們當然很牛逼,但是,就算法筆試來說,學習的成本高但收益低,沒什么性價比,不如多刷幾道動態規劃,真的。

那么,本文依然秉持我們號的風格,只講「圖」最實用的,離我們最近的部分,讓你心里對圖有個直觀的認識。

圖的邏輯結構和具體實現

一幅圖是由節點和邊構成的,邏輯結構如下:

圖的邏輯結構是怎樣的?如何去實現它?

什么叫「邏輯結構」?就是說為了方便研究,我們把圖抽象成這個樣子。

根據這個邏輯結構,我們可以認為每個節點的實現如下:

/* 圖節點的邏輯結構 */class Vertex {

int id;

Vertex[] neighbors;

}

看到這個實現,你有沒有很熟悉?它和我們之前說的多叉樹節點幾乎完全一樣:

/* 基本的 N 叉樹節點 */class TreeNode {

int val;

TreeNode[] children;

}

所以說,圖真的沒啥高深的,就是高級點的多叉樹而已。

不過呢,上面的這種實現是「邏輯上的」,實際上我們很少用這個Vertex類實現圖,而是用常說的鄰接表和鄰接矩陣來實現。

比如還是剛才那幅圖:

圖的邏輯結構是怎樣的?如何去實現它?

用鄰接表和鄰接矩陣的存儲方式如下:

鄰接表很直觀,我把每個節點x的鄰居都存到一個列表里,然后把x和這個列表關聯起來,這樣就可以通過一個節點x找到它的所有相鄰節點。

鄰接矩陣則是一個二維布爾數組,我們權且成為matrix,如果節點x和y是相連的,那么就把matrix[x][y]設為true。如果想找節點x的鄰居,去掃一圈matrix[x][。。]就行了。

圖的邏輯結構是怎樣的?如何去實現它?

那么,為什么有這兩種存儲圖的方式呢?肯定是因為他們各有優劣。

對于鄰接表,好處是占用的空間少。

你看鄰接矩陣里面空著那么多位置,肯定需要更多的存儲空間。

但是,鄰接表無法快速判斷兩個節點是否相鄰。

比如說我想判斷節點1是否和節點3相鄰,我要去鄰接表里1對應的鄰居列表里查找3是否存在。但對于鄰接矩陣就簡單了,只要看看matrix[1][3]就知道了,效率高。

所以說,使用哪一種方式實現圖,要看具體情況。

好了,對于「圖」這種數據結構,能看懂上面這些就綽綽夠用了。

那你可能會問,我們這個圖的模型僅僅是「有向無權圖」,不是還有什么加權圖,無向圖,等等……

其實,這些更復雜的模型都是基于這個最簡單的圖衍生出來的。

有向加權圖怎么實現?很簡單呀:

如果是鄰接表,我們不僅僅存儲某個節點x的所有鄰居節點,還存儲x到每個鄰居的權重,不就實現加權有向圖了嗎?

如果是鄰接矩陣,matrix[x][y]不再是布爾值,而是一個 int 值,0 表示沒有連接,其他值表示權重,不就變成加權有向圖了嗎?

無向圖怎么實現?也很簡單,所謂的「無向」,是不是等同于「雙向」?

圖的邏輯結構是怎樣的?如何去實現它?

如果連接無向圖中的節點x和y,把matrix[x][y]和matrix[y][x]都變成true不就行了;鄰接表也是類似的操作。

把上面的技巧合起來,就變成了無向加權圖……

好了,關于圖的基本介紹就到這里,現在不管來什么亂七八糟的圖,你心里應該都有底了。

下面來看看所有數據結構都逃不過的問題:遍歷。

圖的遍歷

圖怎么遍歷?還是那句話,參考多叉樹,多叉樹的遍歷框架如下:

/* 多叉樹遍歷框架 */void traverse(TreeNode root) {

if (root == null) return;

for (TreeNode child : root.children)

traverse(child);

}

圖和多叉樹最大的區別是,圖是可能包含環的,你從圖的某一個節點開始遍歷,有可能走了一圈又回到這個節點。

所以,如果圖包含環,遍歷框架就要一個visited數組進行輔助:

Graph graph;

boolean[] visited;

/* 圖遍歷框架 */void traverse(Graph graph, int s) {

if (visited[s]) return;

// 經過節點 s

visited[s] = true;

for (TreeNode neighbor : graph.neighbors(s))

traverse(neighbor);

// 離開節點 s

visited[s] = false;

}

好吧,看到這個框架,你是不是又想到了 回溯算法核心套路 中的回溯算法框架?

這個visited數組的操作很像回溯算法做「做選擇」和「撤銷選擇」,區別在于位置,回溯算法的「做選擇」和「撤銷選擇」在 for 循環里面,而對visited數組的操作在 for 循環外面。

在 for 循環里面和外面唯一的區別就是對根節點的處理。

比如下面兩種多叉樹的遍歷:

void traverse(TreeNode root) {

if (root == null) return;

System.out.println(“enter: ” + root.val);

for (TreeNode child : root.children) {

traverse(child);

}

System.out.println(“leave: ” + root.val);

}

void traverse(TreeNode root) {

if (root == null) return;

for (TreeNode child : root.children) {

System.out.println(“enter: ” + child.val);

traverse(child);

System.out.println(“leave: ” + child.val);

}

}

前者會正確打印所有節點的進入和離開信息,而后者唯獨會少打印整棵樹根節點的進入和離開信息。

為什么回溯算法框架會用后者?因為回溯算法關注的不是節點,而是樹枝,不信你看 回溯算法核心套路 里面的圖,它可以忽略根節點。

顯然,對于這里「圖」的遍歷,我們應該把visited的操作放到 for 循環外面,否則會漏掉起始點的遍歷。

當然,當有向圖含有環的時候才需要visited數組輔助,如果不含環,連visited數組都省了,基本就是多叉樹的遍歷。

題目實踐

下面我們來看力扣第 797 題「所有可能路徑」,函數簽名如下:

List《List《Integer》》 allPathsSourceTarget(int[][] graph);

題目輸入一幅有向無環圖,這個圖包含n個節點,標號為0, 1, 2,。。。, n - 1,請你計算所有從節點0到節點n - 1的路徑。

輸入的這個graph其實就是「鄰接表」表示的一幅圖,graph[i]存儲這節點i的所有鄰居節點。

比如輸入graph = [[1,2],[3],[3],[]],就代表下面這幅圖:

圖的邏輯結構是怎樣的?如何去實現它?

算法應該返回[[0,1,3],[0,2,3]],即0到3的所有路徑。

解法很簡單,以0為起點遍歷圖,同時記錄遍歷過的路徑,當遍歷到終點時將路徑記錄下來即可。

既然輸入的圖是無環的,我們就不需要visited數組輔助了,直接套用圖的遍歷框架:

// 記錄所有路徑

List《List《Integer》》 res = new LinkedList《》();

public List《List《Integer》》 allPathsSourceTarget(int[][] graph) {

LinkedList《Integer》 path = new LinkedList《》();

traverse(graph, 0, path);

return res;

}

/* 圖的遍歷框架 */void traverse(int[][] graph, int s, LinkedList《Integer》 path) {

// 添加節點 s 到路徑

path.addLast(s);

int n = graph.length;

if (s == n - 1) {

// 到達終點

res.add(new LinkedList《》(path));

path.removeLast();

return;

}

// 遞歸每個相鄰節點

for (int v : graph[s]) {

traverse(graph, v, path);

}

// 從路徑移出節點 s

path.removeLast();

}

這道題就這樣解決了。

最后總結一下,圖的存儲方式主要有鄰接表和鄰接矩陣,無論什么花里胡哨的圖,都可以用這兩種方式存儲。

在筆試中,最常考的算法是圖的遍歷,和多叉樹的遍歷框架是非常類似的。

當然,圖還會有很多其他的有趣算法,比如二分圖判定呀,環檢測呀(編譯器循環引用檢測就是類似的算法)等等,以后有機會再講吧,本文就到這了。

責任編輯:lq6

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 算法
    +關注

    關注

    23

    文章

    4625

    瀏覽量

    93141
  • 節點
    +關注

    關注

    0

    文章

    220

    瀏覽量

    24471
  • 數據結構
    +關注

    關注

    3

    文章

    573

    瀏覽量

    40183

原文標題:為什么我沒寫過「圖」相關的算法?

文章出處:【微信號:TheAlgorithm,微信公眾號:算法與數據結構】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    LMH7322怎樣改善輸出波形呢 ?

    圖一 我按照LMH7322資料中,按照上圖一 畫的 PCB (見圖二) 圖二 測試時輸出的波形為: 請問工程師 怎樣改善輸出波形呢 ?
    發表于 09-02 06:57

    時序邏輯電路有哪些結構特點呢

    時序邏輯電路是數字電路中的一種重要類型,具有存儲和處理信息的能力。時序邏輯電路的結構特點主要包括以下幾個方面: 存儲元件 時序邏輯電路中最
    的頭像 發表于 08-28 11:07 ?508次閱讀

    數字邏輯怎么把邏輯圖畫成電路

    將數字邏輯中的邏輯圖畫成電路是一個涉及多個步驟的過程,以下是一個詳細的指導: 一、理解邏輯圖 首先,需要深入理解邏輯圖所表達的
    的頭像 發表于 08-21 17:36 ?1000次閱讀

    組合邏輯電路的結構特點是什么?

    組合邏輯電路是一種基本的數字電路,邏輯門組成,用于實現各種邏輯功能。組合邏輯電路的
    的頭像 發表于 08-11 11:14 ?1114次閱讀

    邏輯電路與時序邏輯電路的區別

    的信號。理解它們之間的區別對于設計和實現復雜的數字系統至關重要。 第一部分:邏輯電路 1.1 定義 邏輯電路是一種電子電路,根據輸入信號的邏輯
    的頭像 發表于 07-30 15:00 ?917次閱讀

    組合邏輯控制器是什么設備

    組合邏輯控制器(Combinatorial Logic Controller,簡稱CLC)是一種用于控制和管理復雜系統或設備的電子設備。通常由多個邏輯門、觸發器和其他邏輯元件組成,能
    的頭像 發表于 06-30 10:29 ?749次閱讀

    組合邏輯控制器的基本概念、實現原理及設計方法

    組合邏輯控制器(Combinatorial Logic Controller)是一種在數字電路中實現邏輯功能的設備,根據輸入信號的當前狀態來產生輸出信號,而不考慮輸入信號的歷史狀態。
    的頭像 發表于 06-30 10:26 ?2361次閱讀

    組合邏輯控制器是用什么實現

    、組合邏輯控制器概述 1.1 定義 組合邏輯控制器是一種基于組合邏輯電路的控制器,通過邏輯運算來實現
    的頭像 發表于 06-30 10:11 ?527次閱讀

    基于撲 HT for Web 實現拓撲關系

    拓撲結構在計算機網絡設計和通信領域中非常重要,因為描述了網絡中的設備(即“點”)如何相互連接(即通過“線”)。這種結構不僅涉及物理布局,即物理拓撲,還可以涉及邏輯或虛擬的連接方式,即
    的頭像 發表于 06-24 14:09 ?529次閱讀
    基于<b class='flag-5'>圖</b>撲 HT for Web <b class='flag-5'>實現</b>拓撲關系<b class='flag-5'>圖</b>

    如何實現PLC的自動化控制邏輯

    在工業自動化領域,PLC(Programmable Logic Controller,可編程邏輯控制器)扮演著至關重要的角色。PLC通過編程實現自動化控制邏輯,使設備能夠按照預定的程序進行工作,極大
    的頭像 發表于 06-15 16:44 ?1265次閱讀

    深入理解 FPGA 的基礎結構

    FPGA 的兩個最基本的部分是組合邏輯以及時序邏輯,分別實現這兩個基本部分的結構就是 FPGA 的基本單元。組合邏輯部分一般采用查找表(L
    發表于 04-03 17:39

    STM32f3在main里面應該怎樣調用HAL庫實現帶PEC的基本傳輸?

    采用中斷方式完成收發的demo(不知道可不可行),我在生成的SMBus2配置項中把ownaddress1 設置為0xA0 即從機地址; 請問在main里面應該怎樣調用HAL庫能實現帶PEC的基本傳輸?
    發表于 03-25 07:49

    SPWM調制方式是怎樣實現變壓功能的?又是怎樣實現變頻功能的?

    SPWM調制方式是怎樣實現變壓功能的?又是怎樣實現變頻功能的? SPWM是一種常見的調制方式,通過調節脈沖的寬度來控制輸出波形的幅度和頻率
    的頭像 發表于 02-06 11:09 ?2068次閱讀

    異或門的邏輯符號和邏輯電路組成

    異或門(XOR gate)是數字邏輯電路中常用的一種邏輯門。的作用是對兩個輸入信號進行邏輯運算,輸出一個結果。
    的頭像 發表于 02-04 14:18 ?1.1w次閱讀
    異或門的<b class='flag-5'>邏輯</b>符號和<b class='flag-5'>邏輯</b>電路組成

    通用陣列邏輯(GAL)電路結構設計分析

    通用陣列邏輯(GAL)是一種可編程邏輯器件,由Lattice公司在PAL(可編程陣列邏輯)的基礎上設計出來。GAL采用可編程的輸出邏輯宏單元OLMC(Output Logic Macr
    發表于 02-02 12:21 ?2128次閱讀
    通用陣列<b class='flag-5'>邏輯</b>(GAL)電路<b class='flag-5'>結構</b>設計分析
    主站蜘蛛池模板: 国内自拍 亚洲系列 欧美系列| 国产视频日本| 五月婷婷六月爱| 五月婷婷伊人网| 四虎影院大全| 日韩怡红院| 欧美人成绝费网站色www吃脚| 婷婷亚洲综合五月天小说在线| 你懂的福利| 美女中出视频| 日本色片在线观看| 欧美三级欧美一级| 激情婷婷丁香| cijilu刺激 国产| 天天cao在线| 97夜夜操| 免费观看成人欧美1314www| 五月天毛片| 六月丁香综合网| 欧美成人黄色| 久久99久久精品国产只有| 亚洲29p| 日本三级黄色录像| 韩国三级理论在线观看视频| 18女毛片| 黄色福利站| 亚欧洲乱码专区视频| 性色网站| 美女视频黄的免费视频网页| www成年人视频| 免费看大黄| 一区二区三区在线看| 性无码专区无码| 久久夜色精品国产亚洲| 一区二区三区午夜| 国产骚b| 嘿嘿嘿视频在线观看| 三级黄色录像| 高清成人| 国产视频精品久久| 卡1卡2卡3精品推荐老狼|