數據結構-圖和圖遍歷(DFS、BFS)


一、圖的定義和相關術語

  1. 圖是由頂點(Vertex)和邊(Edge)
  2. 圖可以分為有向圖和無向圖,無向圖所有邊都是雙邊的
  3. 頂點的度是指該頂點相連的邊的條數,特別是對於有向圖的邊數稱為頂點的出度,頂點的入邊條數稱為該頂點的入度。
  4. 頂點和邊都可以有一定的屬性,量化的屬性稱為權值,頂點的權值和邊的權值分別稱為點權和邊權。

二、圖的存儲

  • 一般圖來說存儲方式有兩種:鄰接矩陣鄰接表
  1. 鄰接矩陣,本質上是一個二維數組,里面可以存放權值,但是是開辟了一個二維數組,不能夠開辟很大的,一般的結點數不能超過1000
  2. 鄰接表, 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. 采用深度優先搜索法遍歷圖

  1. 連通分量,在無向圖中,如果圖的任意兩個點都可以連通,則稱圖G為連通圖;否則稱為非連通圖,且稱其中的極大連通子圖為連通分量。
  2. 強連通分量,在有向圖中,如果兩個頂點都可以通過一條有向路徑到達另一個頂點,就稱為這兩個頂點強連通。如果這個圖任意兩個頂點都是強連通,那么就稱這個圖是強連通圖;否則圖就是非強連通圖,且其中極大強連通子圖為強連通分量。
  3. 把連通分量和強連通分量均稱為連通塊
  4. 代碼實現
//偽代碼
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. 采用廣度優先搜索法遍歷圖

  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;
            }
        }
    }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM