圖的基本概念
1.圖的定義
由頂點和邊組成的集合,G=(V,E)
2.基本概念
鄰接點:
對于無向圖u v來說,uv互為鄰接點
對于有向圖u->v來說,v是u的鄰接點,但u不是v的臨界點
路徑:
一個頂點到另一個頂點所經過的頂點構成的序列
有向圖:
邊是有方向的,單項
無向圖:
邊是無方向的,雙向
有權圖:
邊有實際意義,也就是權重
無權圖:
邊無實際意義,無權重,有邊置1,無邊置0
完全圖:
任意兩個頂點之間都有兩邊
連通圖:
任意兩個頂點之間都有路徑
稀疏圖:
邊遠遠小于頂點數(n<<m)
稠密圖:
邊遠遠大于頂點數(n>>m)
圖的存儲方式
前言:
圖的常見存儲方式有
鄰接矩陣、鄰接表、鏈式前向星
鄰接多重表、十字鏈表
1.鄰接矩陣
<注意>
鄰接矩陣的大小至于頂點數有關,為n方,于變數m無關
鄰接矩陣的優點是可以在O(1)的時間復雜度下快速找到鄰接點,直接g[u] [v]==1?
缺點也很明顯,有效邊和無效邊都存儲,太占空間了,適用于稠密圖,這樣無效邊就會變小,不會浪費空間了
遍歷n個頂點的鄰接點的時間復雜度是O(n^2)
無向無權圖
#include<iostream>
#include<vector>
#include<stack>
using namespace std;
const int N=1e4+10;
//g[i][j]的狀態時1/0 1表示ij之間右邊 0表示ij之間沒邊
int g[N][N];//鄰接矩陣
int main() {//輸入n個頂點,m條邊 的無向無權圖int n,m;cin>>n>>m;//輸入邊,初始化鄰接矩陣for(int i=1;i<=m;i++){int u,v;cin>>u>>v;g[u][v]=g[v][u]=1;}/* 1 2 3 4 5 6 7*1 0 1 1 1 0 0 0*2 1 0 0 0 1 0 0*3 1 0 0 0 0 0 0*4 1 0 0 0 0 1 0*5 0 1 0 0 0 0 0*6 0 0 0 1 0 0 1*7 0 0 0 0 0 1 0* 可以發現矩陣是對稱的卻其中非零元素個數是2m個* */for(int i=1;i<=n;i++){cout<<i<<"的鄰接點為:";for(int j=1;j<=n;j++){if(g[i][j]) cout<<j<<" ";}cout<<endl;}return 0;
}
有向無權圖
#include<iostream>
#include<vector>
#include<stack>
using namespace std;
const int N=1e4+10;
int g[N][N];//鄰接矩陣
int main() {//輸入n個頂點,m條邊 的無向無權圖int n,m;cin>>n>>m;//輸入邊,初始化鄰接矩陣for(int i=1;i<=m;i++){int u,v;cin>>u>>v;g[u][v]=1;}/* 1 2 3 4 5 6 7*1 0 1 1 1 0 0 0*2 0 0 0 0 1 0 0*3 0 0 0 0 0 0 0*4 0 0 0 0 0 1 0*5 0 0 0 0 0 0 0*6 0 0 0 0 0 0 0*7 0 0 0 0 0 1 0* 可以發現矩陣是非對稱的卻其中非零元素個數是m個* */for(int i=1;i<=n;i++){cout<<i<<"的鄰接點為:";for(int j=1;j<=n;j++){if(g[i][j]) cout<<j<<" ";}cout<<endl;}return 0;
}
2.鄰接表
<注意>
鄰接表不會存儲無效的邊,所以它不會浪費空間
但是它無法直接定位鄰接點,鄰接矩陣能直接定位鄰接點時它申請矩陣的橫縱坐標表示的就是頂點的值,直接g[i] [j] 判斷是否為1就可以判斷j是否是i的鄰接點,但是鄰接表中的二維vector數組的橫縱坐標表示的不是頂點的值,所無法直接定位,而鄰接表用的是二維靜態vector數組,u頂點的vector[u]數組里面放的都是u的鄰接點,不是鄰接點不放
因為鄰接表采用的是二維靜態vector數組,所以它的空間大小也于m邊數量無關,至于頂點數n有關
無向無權圖
#include<iostream>
#include<vector>
#include<stack>
using namespace std;
const int N=1e4+10;
//二維靜態vector數組 鄰接表 每個vector[u] 里面都是存的u的鄰接點
vector<int> g[N];int main() {//輸入n個頂點,m條邊 的無向無權圖int n,m;cin>>n>>m;//輸入邊,初始化鄰接矩陣for(int i=1;i<=m;i++){int u,v;cin>>u>>v;//無向圖 鄰接表g[u].push_back(v);g[v].push_back(u);}/** vector<int> g[1] 2 3 4* vector<int> g[2] 1* vector<int> g[3] 1* vector<int> g[4] 1 6* vector<int> g[5] 2* vector<int> g[6] 4 7* vector<int> g[7] 6* */for(int i=1;i<=n;i++){cout<<i<<"的鄰接點為:";for(int j=0;j<g[i].size();j++){//鄰接表里面存的就是鄰接點cout<<g[i][j]<<" ";}cout<<endl;}return 0;
}
有向無權圖
#include<iostream>
#include<vector>
#include<stack>
using namespace std;
const int N=1e4+10;
//二維靜態vector數組 鄰接表 每個vector[u] 里面都是存的u的鄰接點
vector<int> g[N];int main() {//輸入n個頂點,m條邊 的無向無權圖int n,m;cin>>n>>m;//輸入邊,初始化鄰接矩陣for(int i=1;i<=m;i++){int u,v;cin>>u>>v;//有向圖 鄰接表g[u].push_back(v);;}/** vector<int> g[1] 2 3 4* vector<int> g[2] 1* vector<int> g[3] 1* vector<int> g[4] 1 6* vector<int> g[5] 2* vector<int> g[6] 4 7* vector<int> g[7] 6* */for(int i=1;i<=n;i++){cout<<i<<"的鄰接點為:";for(int j=0;j<g[i].size();j++){//鄰接表里面存的就是鄰接點cout<<g[i][j]<<" ";}cout<<endl;}return 0;
}
總結:
鄰接矩陣中,行列坐標表示頂點,數組值表示是否存在鄰接點
鄰接表中,行坐標表示頂點,行坐標的vector數組中存儲的是它的鄰接點
圖論算法
DFS
1.DFS(棧)
DFS就是沿著一條路走到黑,知道路沒有了再回頭,棧可以后進先搜
用鄰接矩陣實現
#include<iostream>
#include<vector>
#include<stack>
using namespace std;
const int N=1e4+10;
int g[N][N],n,m;
//標記數組,用來標記已經入棧過的頂點
bool vis[N];
void DFS(int s){stack<int> stk;//起始點入棧,入棧就標記stk.push(s);vis[s]=true;while(!stk.empty()){int cur=stk.top();stk.pop();cout<<cur<<" ";//把棧頂頂點所有的鄰接點入棧//鄰接矩陣for(int i=1;i<=n;i++){//入棧過的就不能再搜了if(!vis[i]&&g[cur][i]) stk.push(i),vis[i]=true;}//鄰接表for(int i=0;i<g[cur].size();i++){//入棧過的就不能再搜了if(!vis[i]) stk.push(g[cur][i]),vis[g[cur][i]]=ture;}}
}
int main() {cin>>n>>m;int s;cin>>s;for(int i=1;i<=m;i++){int u,v;cin>>u>>v;g[u][v]=g[v][u]=1;}DFS(s);return 0;
}
入棧就標記,出棧就搜鄰接點,沒被搜過的入棧
2.DFS(遞歸)
遞歸分為兩部分,搜索部分和回溯部分
#include <iostream>using namespace std;
const int N=1e4+10;
int n,m,g[N][N];
bool vis[N];
//dfs(int status)狀態,s為搜索狀態
void dfs(int s){//正在搜索s//正在搜索的先輸出cout<<s<<" ";//找當前搜索狀態的鄰接點for(int i=1;i<=n;i++){if(g[s][i]&&!vis[i]){vis[i]=1;dfs(i);}}
}
/** cout<<1 2 3 5 7 6 8 4* vis 1 3 5 6 8 4* 1.DFS(1) cout<<1 i=2 vis[2]=1 DFS(2) i=7 vis[7]=1 DFS(7) i=8 vis[8]=1 DFS(8)X* 2.DFS(8) cout<<8 i=4 vis[4]=1 DFS(4)X* 3.DFS(4) cout<<4 X* 2.DFS(7) cout<<7 i=6 vis[6]=1 DFS(6)X* 3.DFS(6) cout<<6 X* 2.DFS(2) cout<<2 i=3 vis[3]=1 DFS(3)X* 3.DFS(3) cout<<3 i=5 vis[5]=1 DFS(5)X* 4.DFS(5) cout<<5 X* */
int main(){cin>>n>>m;for(int i=1;i<=m;i++){int u,v;cin>>u>>v;g[u][v]=g[v][u]=1;}int s;cin>>s;//啟動dfs前要把初始狀態標記vis[s]=1;dfs(s);//搜索的初始狀態return 0;
}
3.連通塊問題(求連通塊數量)
循環深搜
#include <iostream>using namespace std;
const int N=1e4+10;
int n,m,g[N][N];
bool vis[N];
//dfs(int status)狀態,s為搜索狀態
void dfs(int s){//正在搜索sfor(int i=1;i<=n;i++){if(g[s][i]&&!vis[i]){vis[i]=1;dfs(i);}}
}
int main(){int cnt=0;cin>>n>>m;for(int i=1;i<=m;i++){int u,v;cin>>u>>v;g[u][v]=g[v][u]=1;}//循環深搜for(int i=1;i<=n;i++){//只要沒被標記就深搜if(!vis[i]){vis[i]=true;dfs(i);//深搜一次就cnt++cnt++;}}cout<<cnt<<endl;return 0;
}
4.DFS求無權圖最短路徑長度
#include <iostream>using namespace std;
const int N=1e4+10;
int n,m,s,t,ans=0,g[N][N];
bool vis[N];
//dfs(int status)狀態,s為搜索狀態
void dfs(int s,int depth){if(s==t){ans=min(ans,depth-1);return;}for(int i=1;i<=n;i++){if(g[s][i]&&!vis[i]){vis[i]=1;dfs(i,depth+1);//回溯階段把標記取消,這樣可以多次在不同路徑找到終點,從而找到最短路徑vis[i]=0;}}
}
/* vis 1* s=1 t=4* 1.dfs(1) ans=0 i=1 vis[1]=1 dfs(2) vis[2]=0 i=5 vis[5]=1 dfs(5) vis[5]=0 X* 2.dfs(5) ans=0 i=4 vis[4]=1 dfs(4) vis[4]=0 X* 3.dfs(4) ans=2 X* 2.dfs(2) ans=0 i=3 vis[3]=1 dfs(3) vis[3]=0 X* 3.dfs(3) ans=0 i=4 vis[4]=1 dfs(4) vis[4]=0 X* 4.dfs(4) ans=3 X* cout<<ans=2* */
int main(){int cnt=0;cin>>n>>m;for(int i=1;i<=m;i++){int u,v;cin>>u>>v;g[u][v]=g[v][u]=1;}cin>>s>>t;//最短路徑的起點和終點vis[s]=true;dfs(s,1);cout<<ans<<endl;return 0;
}
dfs解決最短路問題時間復雜度:O(2^n)
組合問題時間復雜度為:O(2^n),n最多為25,算法不會超時
排列問題時間復雜度為:O(n!),n最多為11,算法不會超時
不會產生排列情況
5.DFS求無權圖最短路徑
#include <iostream>
#include <vector>
using namespace std;
const int N=128;
vector<char> path,res;
char s,t;
bool vis[N];
int m,ans=0x3f3f3f3f,g[N][N],k;
void dfs(char s,int depth){if(s==t) {if(ans>depth-1){ans=depth-1;res=path;}return ;}for(char c='A';c<='Z';c++){if(!vis[c]&&g[s][c]){vis[c]=true;path.push_back(c);dfs(c,depth+1);path.pop_back();vis[c]=false;}}
}
/** vis A* path A* res A E F* 1.dfs(A) c=B vis[B]=1 push B dfs(B) c=E vis[E]=1 push E dfs(E) c=F vis[F]=1 push F dfs(F)* 2.dfs(F) ans=2 res X* 2.dfs(E) c=D vis[D]=1 push D dfs(D) X* 3.dfs(D) c=C vis[C]=1 push C dfs(C) X* 4.dfs(C) c=B vis[B]=1 push B dfs(B) X* 5.dfs(B) X* 2.dfs(B) c=C vis[C]=1 push C dfs(C) X* 3.dfs(C) c=D vis[D]=1 push D dfs(D) X* 4.dfs(D) c=E vis[E]=1 push E dfs(E) pop c=F vis[F]=1 push F dfs(F) pop X* 5.dfs(F) ans=4 res X* 5.dfs(E) c=F vis[F]=1 push F dfs(F) pop X* 6.dfs(F) ans=5 res X* */int main(){cin>>m;for(int i=1;i<=m;i++){char u,v;cin>>u>>v;g[u][v]=g[v][u]=1;}cin>>s>>t;vis[s]=true;path.push_back(s);dfs(s,1);cout<<"最短路長度為"<<ans<<endl;cout<<"最短路徑為:";for(int i=0;i<res.size()-1;i++){printf("%c->",res[i]);}printf("%c",res[res.size()-1]);return 0;
}
BFS
1.BFS輸出圖中元素
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
const int N=1e4+10;
int g[N][N],n,m,st;
bool vis[N];
//input:
//1 2
//2 3
//3 4
//1 9
//9 5
//5 6
//6 7
//7 4
//1 8
//8 6
//1
//output:
//1 2 8 9 3 6 5 4 7
void bfs(int s){queue<int> q;q.push(s);vis[s]=1;while(!q.empty()){int cur=q.front();cout<<cur<<" ";q.pop();for(int i=1;i<=n;i++){if(!vis[i]&&g[cur][i]){q.push(i);vis[i]=1;}}}
}
int main(){cin>>n>>m;for(int i=1;i<=m;i++){int u,v;cin>>u>>v;g[u][v]=g[v][u]=1;}cin>>st;bfs(st);return 0;
}
2.BFS解決連通塊問題
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
const int N=1e4+10;
int g[N][N],n,m,cnt;
bool vis[N];
//input:
//8 5
//
//1 2
//2 3
//4 5
//6 7
//6 8
//
//ouput:
//3
void bfs(int s){queue<int> q;q.push(s);while(!q.empty()){int cur=q.front();q.pop();for(int i=1;i<=n;i++){if(!vis[i]&&g[cur][i]){q.push(i);vis[i]=1;}}}
}
int main(){cin>>n>>m;for(int i=1;i<=m;i++){int u,v;cin>>u>>v;g[u][v]=g[v][u]=1;}for(int i=1;i<=n;i++){if(!vis[i]){vis[i]=1;bfs(i);cnt++;}}cout<<cnt<<endl;return 0;
}
3.BFS求最短路
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
const int N=1e4+10;
struct node{int id,depth;
};
int g[N][N],n,m,st,t,ans;
int pre[N];
bool vis[N];
//9 10
//
//1 2
//2 3
//3 4
//1 9
//9 5
//5 6
//6 7
//7 4
//1 8
//8 6
//
//1 7
//ouput:
//3
//1 8 6 7//bfs求最短路的時間復雜度是O(n+m)
void bfs(node s){//由于bfs不是遞歸,無法將層數和搜索狀態綁定,只能使用結構體將搜索狀態和層數綁定queue<node> q;q.push(s);vis[s.id]=1;while(!q.empty()){node cur=q.front();q.pop();if(cur.id==t){ans=cur.depth-1;return ;}for(int i=1;i<=n;i++){if(!vis[i]&&g[cur.id][i]){q.push({i,cur.depth+1});pre[i]=cur.id;vis[i]=1;}}}
}
void getpath(int s){if(!s) return ;getpath(pre[s]);cout<<s<<" ";
}
//1.g(7) g(6) 7
//2.g(6) g(8) 6
//3.g(8) g(1) 8
//4.g(1) g(0) 1
//5.g(0) X
int main(){cin>>n>>m;for(int i=1;i<=m;i++){int u,v;cin>>u>>v;g[u][v]=g[v][u]=1;}cin>>st>>t;bfs({st,1});cout<<ans<<endl;getpath(t);return 0;
}
4.迷宮問題
DFS解決-能否走出迷宮
#include <iostream>using namespace std;
const int N=1e4+10;
int n,m,sx,sy,tx,ty;
char g[N][N];//二維迷宮
bool vis[N][N],flag;
int dx[]={0,0,1,-1};
int dy[]={1,-1,0,0};
//8 8
//*****...
//*.S...**
//*****.**
//*****..*
//*T..**.*
//.**.**.*
//..*....*
//...*****//YES
void dfs(int px,int py){if(px==tx&&py==ty){//當前狀態為終點flag=true;return ;}//向著鄰接點DFSfor(int i=0;i<4;i++){int bx=px+dx[i],by=py+dy[i];//合法性判斷if(bx<1||bx>n||by<1||by>m||g[bx][by]=='*'||vis[bx][by]) continue;vis[bx][by]=1;dfs(bx,by);}
}
int main(){cin>>n>>m;for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){cin>>g[i][j];if(g[i][j]=='S') sx=i,sy=j;else if(g[i][j]=='T') tx=i,ty=j;}}vis[sx][sy]=1;dfs(sx,sy);if(flag){cout<<"YES"<<endl;}elsecout<<"NO"<<endl;return 0;
}
BFS解決-能否走出迷宮
#include <iostream>
#include <queue>
#include<Windows.h>
using namespace std;
const int N=1e4+10;
int n,m,sx,sy,tx,ty;
char g[N][N];//二維迷宮
bool vis[N][N],flag;
int dx[]={0,0,1,-1};
int dy[]={1,-1,0,0};
struct node{int x,y,depth;
};
//8 8
//*****...
//*.S...**
//*****.**
//*****..*
//*T..**.*
//.**.**.*
//..*....*
//...*****//YES
void bfs(node s){queue<node> q;q.push(s);vis[s.x][s.y]=1;while(!q.empty()){node cur=q.front();q.pop();//當前搜索狀態if(cur.x==tx&&cur.y==ty){flag=true;cout<<cur.depth-1<<endl;return;}for(int i=0;i<4;i++){int bx=cur.x+dx[i],by=cur.y+dy[i];if(bx<1||bx>n||by<1||by>m||g[bx][by]=='*'||vis[bx][by]) continue;vis[bx][by]=1;q.push({bx,by,cur.depth+1});}}
}
int main(){cin>>n>>m;for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){cin>>g[i][j];if(g[i][j]=='S') sx=i,sy=j;else if(g[i][j]=='T') tx=i,ty=j;}}bfs({sx,sy,1});if(flag){cout<<"YES"<<endl;}elsecout<<"NO"<<endl;return 0;
}