圖的概念、存儲及遍歷

来源:https://www.cnblogs.com/wuxiangnong/archive/2019/05/05/10816475.html
-Advertisement-
Play Games

圖的概念、存儲及遍歷 圖是一種特殊的數據結構,由點和邊構成,它可以用來描述元素之間的網狀關係,這個網狀沒有順序,也沒有層次,就是簡單的把各個元素連接起來。圖在我們的生活中也十分常見,地圖就是最簡單的例子。 圖的基本概念: 頂點集合為V,邊集合為E的圖記作G=(V,E)。另外,G=(V,E)的頂點數和 ...


圖的概念、存儲及遍歷

圖是一種特殊的數據結構,由點和邊構成,它可以用來描述元素之間的網狀關係,這個網狀沒有順序,也沒有層次,就是簡單的把各個元素連接起來。圖在我們的生活中也十分常見,地圖就是最簡單的例子。


圖的基本概念:

頂點集合為V,邊集合為E的圖記作G=(V,E)。另外,G=(V,E)的頂點數和邊數分別為|V|和|E|。對於兩個圖G和G',如果G'的頂點集合與邊集合均為G的頂點集合與邊集合的子集,那麼稱G'是G的子圖。子圖實際上就是一張圖裡面小一點的圖,也可以是點,不難理解。

有向圖:圖的邊有方向,只能按箭頭方向從一點到另一點。

無向圖:圖的邊沒有方向,可以雙向。

如圖,(a)就是有向圖,(b)就是無向圖。

頂點的度:無向圖中連著頂點的邊的數目。

頂點的入度和出度:有向圖中,以這個頂點為起點的邊的數量稱為這個頂點的出度;以這個頂點為終點的邊稱為這個頂點的入度。

邊權:邊的費用,可以形象的理解為“過路費”。對於一張存在邊權的圖,我們稱為“帶權圖”。

連通:如果圖中兩點U,V之間存在一條由U經過若幹邊、點到達V的路徑,則稱U,V是連通的。

迴路:起點和終點相同的路徑,稱為“迴路”或“環”。另外,不存在環的有向圖稱為Directed Acyclic Graph(DAG)。

完全圖:每個點都與其它所有的點有連邊的圖。

n個點的有向完全圖的邊數計算方法:每個點都可以自己為起點連出n-1條邊,因為除了它自己,剩下的n-1個點都能作為它連邊的終點,而整張圖有n個點,所以最終結果為:n(n-1)條邊;n個點的無向完全圖的邊數計算方法:因為是無向的,那麼a連到b、從b連到a這兩條邊只能算作一條,所以,無向完全圖的邊數應該是有向完全圖的一半,即:n(n-1)/2條。

稀疏圖:一張邊數遠遠少於完全圖的圖

稠密圖:一張邊數接近完全圖的圖


圖的存儲:

對於如何存儲一張圖,我們主要有三種方法。

(1)鄰接矩陣:對於一張圖來說,我們可以存儲點的信息,也可以存儲邊的信息,而鄰接矩陣存儲的是點的信息。對於一個點,我們需要知道的是它和哪些點有連邊,有時還要知道連邊的邊權。那麼,不難想到用二維數組來實現。

用map[u][v]來表示u和v 之間是否有連邊。如果u,v之間有連邊,那麼map[u][v]=1或邊權,如果是帶全圖就賦值邊權,否則賦值1;反之,賦值0或無窮大,如果是無向圖,就加上map[v][u]= map[u][v]。

對於帶權圖,我們極可能設計對整張圖的計算,所以要賦值邊權及無窮大,無窮大比無窮小要方便得多,因為在很多演算法中,我們需要邊權儘量小,如果賦值無窮小,那不是優先選這條實際上不存在的邊了?還可以這樣想:我們的邊權相當於“過路費”,我們現在無法從u到達v,你覺得是因為過路費太貴了還是過路費太便宜了?肯定是由於過路費太貴,我們付不起。這樣一想,就不難理解了。

對於上面三幅圖,它們的鄰接矩陣分別如下:

註意,鄰接矩陣不能存儲有重邊的圖,因為數組裡的每一個位置只能記錄一個值。那麼,鄰接矩陣的空間複雜度就是O(n2),n為點數,添加及查詢邊的複雜度均為O(1)。這種方式適合存儲稠密圖,因為它申請的空間是準備用來存儲每個點到其它所有點的邊的邊權的,如果是稀疏圖,會造成很大的浪費。

代碼實現:

//有向圖
for(i=1;i<=n;i++)
    for(j=1;j<=n;j++)
        map[i][j]=0x7ffffff;
for(i=1;i<=n;i++)map[i][i]=0;
for(i=1;i<=e;i++)
{
    scanf("%d%d%d",&u,&v,&w);
    map[u][v]=w;
}

(2)邊集數組:除了存儲點的信息,我們還可以存儲邊的信息。對於一條邊,我們需要知道它的起點、終點以及邊權,那麼,我們不難想到可以用結構體來存儲。這種存儲方式非常簡單粗暴,適用於對邊依次進行處理,但不適用於對頂點的處理和對任意一條邊的處理,因為邊集數組查詢邊的複雜度為O(e),e為邊數,這個不難理解,因為我們需要遍歷整個數組。如果要處理頂點,那麼每一次頂點的拓展都要花費O(e)的時間,時間複雜度非常大,而每次查找任意一條邊也要花費O(e)的時間,所以均不適用。如果是對邊的順次處理,那來一遍迴圈就ok了。

從空間上來看,邊集數組適用於稀疏圖,因為要存儲每一條邊,所以邊集數組的空間複雜度為O(e),插入邊的複雜度為O(1)。根據上面說的完全圖邊數的計算方法,即使是無向圖,當n=10000時,完全圖的邊數也達到了49995000,完全存不下,因此適用於稀疏圖。

代碼實現:

for(i=1;i<=e;i++)
{
    scanf("%d%d%d",&u,&v,&w);
    e[i].u=u;
    e[i].v=v;
    e[i].len=w;
}

(3)鄰接表:邊集數組的缺陷在於邊與邊之間沒有聯繫,那我們想辦法將邊聯繫起來。能聯繫在一起的邊,必然要有什麼共同點,我們不妨讓它們的某個東西相同。什麼東西相同呢?邊權嗎?把邊權相等的邊放在一起對我們沒有任何幫助。本質上,我們對邊集數組的不滿在於它無法快速地進行點的拓展,第一它不知道這個點連出了哪些邊,第二它也不知道連出了幾條,這才是最要命的。

問題很明確,我們需要快速地知道一個點連出的邊,那我們不妨把它們放在一起。別忘了,我們還需要很快地知道這個點連出的一條邊的下一條邊。有點拗口,我們不妨一個點連出的邊編號1、2、3,我們需要快速地知道1下一條是2,2下一條是3,這樣的形式,我們可以採用鏈表的形式來存儲。將同一個頂點連出的邊鏈接在同一個邊鏈表中,鏈表裡的每一個點代表一條邊,這個點叫做邊結點。

現在,我們要記錄的每個邊結點的信息稍有變動:我們要記錄這條邊的終點,這條邊的邊權,以及這條邊的上一條邊,因為我們採用的是鏈表的形式,所以要記錄前驅,當然你也可以抽象地理解為:這條邊的下一跳邊,因為實現的時候處理完這條邊下一個就處理我們記錄的這下(上)一條邊了,這個操作依然可以用結構體實現。然後,我們還要記錄每個點連出的最後一條邊,同樣可以抽象理解為第一條邊。這樣,我們每次拿到這個點連出的第一條邊,然後順著第一條慢慢往下拿到第二條、第三條,時間複雜度均為O(),比邊集數組快飛了。

鄰接表的空間複雜度為O(n+e),我們可以算一下:首先,我們至少需要O(n)的數組來存放每個點連出邊的集合,然後我們又需讀入了e條邊,所以複雜度為O(n+e)。查詢邊的複雜度為O(Mi),Mi為由vi連出的邊的數目,插入邊的複雜度為O(1)。根據空間複雜度的計算,可以得出:鄰接表同樣適用於稀疏圖。

struct edge
{
    int to,len,last;
}e[100001];//鄰接表 
int first[10001];
//每個點連出的最後(第一)條邊 
int len=0;//當前邊的總數,方便對邊的存儲 
void add(int u,int v,int w)
{
    len++;
    e[len].to=v;
    e[len].len=w;
    e[len].last=first[u];
    first[u]=len;
}

三種方式的比較:


圖的遍歷:

我們想要對頂點進行操作,必然要對圖進行遍歷。圖的遍歷有兩種,都是我們耳熟能詳的:深度優先遍歷、廣度優先遍歷,具體的思想和搜索一模一樣,這裡不再重覆。

深度優先遍歷:

//鄰接表
void dfs(int dep)
{
    blablabla......
    int i;
    for(i=first[dep];i;i=e[i].last)
        if(b[a[i].to]==0)
        {
            b[a[i].to]=1;
            dfs(a[i].to);
            b[a[i].to]=0;   
        }
}

廣度優先遍歷:

//鄰接表
void bfs()
{
    int head=0,tail=1;
    q[tail]=x;//x表示我們最開始的起點
    b[x]=1;
    while(head<tail)
    {
        head++;
        int i,t=q[head];
        for(i=first[t];i;i=a[i].last)
            if(b[a[i].to]==0)
            {
                blablabla......
                b[a[i].to]=1;
                q[tail++]=a[i].to;  
            }   
    } 
}

By wxn


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 建議先瞭解為什麼項目要使用 MQ 消息隊列,MQ 消息隊列有什麼優點,如果在業務邏輯上沒有此種需求,建議不要使用中間件。中間件對系統的性能做優化的同時,同時增加了系統的複雜性也維護難易度;其次,需要瞭解各種常見的 MQ 消息隊列有什麼區別,以便在相同的成本下選擇一種最合適本系統的技術。 本文主要討論 ...
  • 在程式中如何讀寫文件?不同的編程語言有不同的方式,而 JAVA 則提出了“流”的概念,通過“流”來讀寫文件 什麼是流: 流(Stream)是指一連串的數據(字元或位元組),是以先進先出的方式發送信息的通道,數據源發送的數據經過這個通道到達目的地,按流向區分為輸入流和輸出流 什麼是輸入流:數據流從數據源 ...
  • 新聞 "FableConf 2019開始徵集提案" "2019理事會選舉 " "如同DSL一般的Elmish封裝器fable elmish,可以創建控制台或者終端界面" "介紹VS Code的遠程開發" "F (.NET Core 2.1)開發容器" "SAFE開發容器定義示例" "Rider 20 ...
  • #include<iostream> #include<string> #define ml 10 using namespace std; typedef struct{//定義Data數據項 std::string name; long num; }Data; struct Link{//定義結 ...
  • 數組,是我們最常用的,但是有時候,我們要用數組,但是又不知道數組的類的長度的時候, 我們java就有一個很好用的工具Collection,這都是java的爸爸的用心良苦,Collection中包含List和Set 和Map,但是今天老師講了List和Set。List是有序泛型數組。Set是無序泛型數 ...
  • wc命令含義 wc命令用查看文件的行數、單詞數、字元數等信息 wc命令格式 wc [-clmw] [file ...] wc命令參數以及實例介紹 (1)最基礎的查看文件的行數、單詞數、字元數等信息 (2)-l 選項 :統計文件的行數信息 (3) -w 選項:統計文件的單詞數信息 (4)-c 選項:統 ...
  • 1 import pandas as pd 2 import numpy as np 3 4 # merge合併 ,類似於Excel中的vlookup 5 6 df1 = pd.DataFrame({'key': ['K0', 'K1', 'K2', 'K3'], 7 'A': ['A0', 'A1... ...
  • 一,複習 二,今日內容 三,模塊 四,導入模板完成的三件事 五,起別名 六,模塊的分類 七,模塊的載入順序 八,環境變數 九,from...import語法導入 十,from...import * 十一,鏈式導入 十二,迴圈導入 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...