一、圖的定義和相關術語
- 圖是由頂點(Vertex)和邊(Edge)
- 圖可以分為有向圖和無向圖,無向圖所有邊都是雙邊的
- 頂點的度是指該頂點相連的邊的條數,特別是對於有向圖的邊數稱為頂點的出度,頂點的入邊條數稱為該頂點的入度。
- 頂點和邊都可以有一定的屬性,量化的屬性稱為權值,頂點的權值和邊的權值分別稱為點權和邊權。
二、圖的存儲
- 鄰接矩陣,本質上是一個二維數組,里面可以存放權值,但是是開辟了一個二維數組,不能夠開辟很大的,一般的結點數不能超過
1000。
- 鄰接表, N個頂點就會有N個列表,常常使用vector來實現鄰接表。
vector<int> Adj[N];
Adj[1].push_back(3);
struct Node{
int v;
int w;
};
vector<Node> Adj[N];
//如果想添加邊
Node temp;
temp.v = 3;
temp.w = 4;
Adj[1].push_back(temp);
//更快的方式,用定義結構體Node時構造函數
struct Node{
int v, w;
Node(int _v, int _w) : v(_v), w(_w) {}
}
//這樣就可以不用定義臨時變量
Adj[1].push_back(Node(3, 4));
三、圖的遍歷
- 一般有兩種深度優先搜索(DFS)和廣度優先搜索(BFS)
1. 采用深度優先搜索法遍歷圖
- 連通分量,在無向圖中,如果圖的任意兩個點都可以連通,則稱圖G為連通圖;否則稱為非連通圖,且稱其中的極大連通子圖為連通分量。
- 強連通分量,在有向圖中,如果兩個頂點都可以通過一條有向路徑到達另一個頂點,就稱為這兩個頂點強連通。如果這個圖任意兩個頂點都是強連通,那么就稱這個圖是強連通圖;否則圖就是非強連通圖,且其中極大強連通子圖為強連通分量。
- 把連通分量和強連通分量均稱為連通塊
- 代碼實現
//偽代碼
DFS(u){//訪問頂點u
vis[u] = true;
for(從u出發能夠達到的所有頂點v){
if(vis[v] == false) DFS(v);
}
}
DFSTrave(G){//遍歷圖
for(G的所有頂點u){
if(vis[u] == false) DFS(u);
}
}
鄰接矩陣版本
const int MAXV = 1000;//最大頂點數
const int INF = 1000000000;//設INF為一個很大的數
int n, G[MAXN][MAXN];
bool vis[MAXV] = {false};//用於判斷頂點是否被訪問
void DFS(int u, int depth){
vis[u] = true;
//如果需要對u進行一些操作,可以在這里進行
//下面是對所有從u出發能到達的分支頂點進行枚舉
for(int v = 0; v < n; v++){
if(vis[v] == false && G[u][v] != INF){
DFS(v, depth+1);
}
}
}
void DFSTrace(){//遍歷圖
for(int u = 0; u < n; u++){//對於每個頂點
if(vis[u] == false){
DFS(u, 1);//訪問u和u所在的連通塊,1表示初始為第一層
}
}
}
鄰接表
vector<int> Adj[MAXN];//圖G的鄰接表
int n;//n為頂點數,MAXN為最大頂點數
bool vis[MAXN] = {false};
void DFS(int u, int depth){
vis[u] = true;
//一些操作也可在此處進行操作
for(int i = 0; i < Adj[u].size(); i++){
int v = Adj[u][i];
if(vis[v] == false){
DFS(v, depth+1);
}
}
}
void DFSTrace(){
for(int u = 0; u < n; u++){
if(vis[u] == false){
DFS(u, 1);
}
}
}
2. 采用廣度優先搜索法遍歷圖
- 具體實現,就是建立一個隊列,並且把初始頂點加入隊列,此后每次都取出隊首頂點進行訪問,並且把該頂點可以到達的的未曾到過的結點加入隊列(注意不是訪問,而是加入),直到隊列為空。
- 偽代碼
BFS(u){
queue q;
將u入隊列;
inq[u] = true;
while(q非空){
取出隊首元素u進行訪問;
for(遍歷該頂點可以到的所有結點v){
if(inq[v] == false){
將v入隊列;
inq[v] = true;
}
}
}
}
BFSTrace(G){
for(G所有結點){
if(inq[u] == false){
BFS(u);
}
}
}
鄰接矩陣版
const int maxn;
const int INF;
int n;
vector<int> G[maxn][maxn];
bool inq[maxn] = {false};
void BFS(int u){
queue<int> q;
q.push(u);
inq[u] = true;
while(!q.empty()){
int now = q.front();
q.pop();
for(int i = 0; i < n; i++){//這里直接數遍歷所有結點,而不是這個Adj[now].size()
if(inq[v] == false && G[now][v] != INF){
q.push(v);
inq[v] = true;
}
}
}
}
void BFSTrace(){
for(int u = 0; i < n; u++){
if(inq[u] == false){
BFS(q);
}
}
}
鄰接表版
vector<int> Adj[maxn];
int n;
bool inq[maxn];
BFS(int u){
queue<int> q;
q.push(u);
inq[u] = true;
while(!q.empty()){
int u = q.front();
q.pop();
for(int i = 0; i < Adj[u].size(); i++){
int v = Adj[u][i];
if(inq[v] == false){
q.push(v);
inq[v] = true;
}
}
}
}
void BFSTrace(){
for(int u = 0; u < n; u++){
if(inq[u] == false){
BFS(u);
}
}
}
如果需要層號鄰接表版
struct Node{
int v;
int layer;
};
vector<Node> Adj[maxn];
void BFS(int s){
queue<Node> q;
Node start;
start.v = s;
start.layer = 0;
q.push(start);
inq[start.v] = true;
while(!q.empty()){
Node topNode = q.front();
q.pop();
int u = topNode.v;
for(int i = 0; i < Adj[u].size(); i++){
Node next = Adj[u][i];
next.layer = topNode.layer + 1;
if(inq[next.v] == false){
q.push(next);
inq[next.v] = true;
}
}
}
}