背景
廢話不多說,了解這么多。潦草結束
洛谷博客也有這篇文章
相關知識
基礎模板涉及:
進階算法涉及:(這篇文章不寫)
一、圖的基本概念
圖
圖是一種數據結構,從嚴格意義上講,它的定義為:
G在這里代表的是圖,而V是一個非空的有限集合,代表頂點(也就是結點),E代表邊的集合。
如下圖,下面這個就是圖
圖的一些定義與概念
因為有很多,我一項一項列出來
1.有向與無向:
有向圖就是圖的邊有方向,按箭頭的方向從一點到另一點。
無向圖就是圖的邊沒有方向,雙向通行。
2.度、出度、入度
度是無向圖中的,而出度和入度是有向圖中的
上圖中每個結點的周圍都寫着它的度(或者是出度,入度)
無向圖的度就是這個點所連接的邊的數量
有向圖的出度就是從這個點出發的邊數,入度就是從任一點(除自己外)出發,連接自己的邊數就叫入度
這么說你可能很繞腦,你看圖就行了
3.邊權(權值)
邊權,你可以抽象的理解為走這條邊的費用(也可以理解為長度、代價等),也就是下圖
比如有向圖,從1到2或者從2到1都要有1的代價,再比如無向圖里,從3到4要有4的代價,這就是邊權了。
4.連通
連通並不是兩個點之間有邊就算連通,只要是從A出發,不論怎么走,只要能走到B,那么就可以說A和B是連通的。
准確、科學的說只要能從U點出發,經過若干點后達到點V,那么就稱UV是連通的
上圖中,1和2是連通的,1和3也是連通的,2和3同理
5.完全圖、稀疏圖、稠密圖
完全圖就是在有n個點,且有n(n−1)/2條邊的圖,叫做完全圖
而稀疏圖和稠密圖就是一些比較模糊的了,稠密圖就是邊的數量接近完全圖但不是完全圖的圖,稀疏圖是邊更少,比稠密圖的邊還要少的圖
6.環(回路)
起點與終點相同的路徑,叫做環。每個圖有環,就叫環圖(也叫哈爾密頓圖),否則叫無環圖。
7.強連通分量
在一個有向圖中,由n個點構成環的子圖叫做強連通分量,單個點(未算在其他子圖中的點)也算強連通分量。
在上圖中,有2個強連通分量: 第1個是由1->3->5->4構成的子圖 第2個是由2構成的子圖
二、圖的存儲結構與建邊
存圖有很多種方法,這里介紹兩種
1.鄰接矩陣
鄰接矩陣是個 二維數組 ~~
它的調用格式是這樣的
g[i][j]//表示從i到j的邊的權值(也可以用來存是否存在這個信息)
我這里給出幾個圖康康吧
這個圖,用鄰接矩陣存儲后是:(給出鄰接矩陣)
你有沒有發現什么呢?
你會發現這個圖他是對稱的。。。
這就證明,他是個無向圖。
為什么呢?不因為什么,就因為他是無向圖,兩邊都可以通行
那么為什么要這樣存圖呢?
我先來說明一下啊
第 i行存儲的是結點 i所連接的邊的權值(或者是否連接這個點)
那么第i行的第j列不就是從i到j的那條邊了嗎?
你會發現,哇!原來這么簡單
當然,鄰接矩陣的好處之一就是方便調用邊
在有邊權的時候,要先初始化才可以使用
for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ g[i][j]=0x7fffffff; } }
當然了,你也可以寫這個
memset(g,0x7f,sizeof(g));
但是要注意寫cstring或者string.h頭文件
存圖該怎么存呢?
總不能一個一個去存吧,,,而且你還不知道數據
那么就要用到for了
for(int i=1;i<=m;i++){//m是邊的數量 int from,to,w; cin>>from>>to>>w; g[from][to]=w; }
g[from][to]意思就是從from到to的那條邊,w就是權值,從from到to的邊的權值是w
當然,如果你要存的是這條邊是否存在,那么你初始化要寫0,然后有邊就寫1
當然!這只是有向圖
無向圖的話就證明兩邊都可以走對吧?那么如果寫一個雙向的箭頭不也是一樣嗎?所以說我們要這樣存圖
for(int i=1;i<=m;i++){//m是邊的數量 int from,to,w; cin>>from>>to>>w; g[from][to]=w; g[to][from]=w; }
沒錯,就是存兩邊,只不過起點和終點反過來了而已(雖然只是起點終點反過來了,但是他們的意義完全不一樣)
總結:鄰接矩陣的優點是方便度的計算(讀入時就可以算)、方便判斷兩點是否有邊及其權值、大小是n*n、占用的單元只與頂點有關。缺點是尋找一個點的所有相連的邊需要1-n循環,而且內存過大容易爆
2.鄰接表
哇,,,終於碼到鄰接表了,累死我了啊啊啊
鄰接表非常像鏈表
要用到結構體了。。。
首先定義一個名為Edge(邊)的結構體並定義一個Edge類型的edge數組
struct Edge{ int nxt,to,w; }edge[MAX];
nxt就是nxet的意思,就是下一條邊的指針。不寫next的原因是評測機可能會吞特殊字,所以這里寫了nxt
to是這條邊指向的結點
w就是權值了
MAX嘛,,,自己定義,但是要注意存題上給的二倍(比如1e5+9要變成2e5+9),因為可能是無向圖
然后建邊嘛。。。這個就比較麻煩了
首先需要定義一個變量和一個數組
int num_edge=0,head[MAX];
num_edge就是當前edge數組存到的位置
head[i]就代表結點i連接的最后一條邊
這么說你又雙叒叕可能會有點暈(其實我更暈),一會寫建邊給你講
void add_edge(int from,int to,int w){ edge[++num_edge].nxt=head[from]; edge[num_edge].to=to; edge[num_edge].w=w; head[from]=num_edge;
}
這個就是建邊的手寫數組
需要3個參數,我就不講了。。。。
首先函數的第一行,++num_edge就是先自增后存儲,這樣就不用額外寫一個num_edge++了(我這里是從1開始存的,要從0開始存的話,你num_edge初始值可以為-1)
也就是說,建一個新邊,首先邊的總數+1了(就是++num_edge),他的下一條邊的值就是from(也就是新邊的起始點)的最后一條邊的指針(這里是之前的最后一條邊的指針,它加進來之后就是最后一條邊就是他了)
然后第二、三步,建立新邊指向的結點和權值
第四步,就是第一步寫的“它加進來之后就是最后一條邊就是他了”,只不過要存他的指針而已,當然就是num_edge。就是from(新邊的起始點)的最后一條邊更新
然后就變成了這樣:
for(int i=1;i<=n;i++){ int from,to,w; cin>>from>>to>>w; add_edge(from,to,w); add_edge(to,from,w); }
這就是建邊的全過程了,累死我了
你可能會覺得,還是鄰接矩陣好用,鄰接表根本沒聽懂(???),還不好用,這么麻煩
但是到了后面寫Dijkstra等高級算法的時候,特別是學優化的時候,你不僅會習慣寫鄰接表,還會說真香。。。(我直呼內行!)
三、結束了,總結一下下
唉鴨累死我了先寫這些吧
做圖、碼字、寫代碼都累死我了,用了2個多小時才寫出來
所以各方面都體諒一下吧。。。
這里練習題我就不提供了,大家自學一下吧
回顧:
這次我們學了鄰接矩陣、鄰接表以及圖的概念,你都學會了嗎?
沒學會再去學一遍吧qaq
這次就到這里了886
轉載請注明出處作者~