數(shù)據(jù)結(jié)構(gòu)——圖的廣度優(yōu)先遍歷

圖的廣度遍歷和深度遍歷思想不一樣。后者是用遞歸的方法來(lái)實(shí)現(xiàn)的,這個(gè)是要借助隊(duì)列來(lái)實(shí)現(xiàn)的。
實(shí)現(xiàn)的基本思想如下:
1、從圖中某個(gè)頂點(diǎn)V0出發(fā),并訪(fǎng)問(wèn)此頂點(diǎn);
2、從V0出發(fā),訪(fǎng)問(wèn)V0的各個(gè)未曾訪(fǎng)問(wèn)的鄰接點(diǎn)W1,W2,…,Wk;然后,依次從W1,W2,…,Wk出發(fā)訪(fǎng)問(wèn)各自未被訪(fǎng)問(wèn)的鄰接點(diǎn);
3、重復(fù)步驟2,直到全部頂點(diǎn)都被訪(fǎng)問(wèn)為止。</br>
廣度優(yōu)先遍歷是以層為順序,和樹(shù)的層次遍歷差不多,是將某一層上的所有節(jié)點(diǎn)都搜索到了之后才向下一層搜索;而深度優(yōu)先遍歷是將某一條枝椏上的所有節(jié)點(diǎn)都搜索到了之后,才轉(zhuǎn)向搜索另一條枝椏上的所有節(jié)點(diǎn)。
深度優(yōu)先遍歷從某個(gè)頂點(diǎn)出發(fā),首先訪(fǎng)問(wèn)這個(gè)頂點(diǎn),然后找出剛訪(fǎng)問(wèn)這個(gè)結(jié)點(diǎn)的第一個(gè)未被訪(fǎng)問(wèn)的鄰結(jié)點(diǎn),然后再以此鄰結(jié)點(diǎn)為頂點(diǎn),繼續(xù)找它的下一個(gè)新的頂點(diǎn)進(jìn)行訪(fǎng)問(wèn),重復(fù)此步驟,直到所有結(jié)點(diǎn)都被訪(fǎng)問(wèn)完為止。
廣度優(yōu)先遍歷從某個(gè)頂點(diǎn)出發(fā),首先訪(fǎng)問(wèn)這個(gè)頂點(diǎn),然后找出這個(gè)結(jié)點(diǎn)的所有未被訪(fǎng)問(wèn)的鄰接點(diǎn),訪(fǎng)問(wèn)完后再訪(fǎng)問(wèn)這些結(jié)點(diǎn)中第一個(gè)鄰接點(diǎn)的所有結(jié)點(diǎn),重復(fù)此方法,直到所有結(jié)點(diǎn)都被訪(fǎng)問(wèn)完為止。
可以看到兩種方法最大的區(qū)別在于前者從頂點(diǎn)的第一個(gè)鄰接點(diǎn)一直訪(fǎng)問(wèn)下去再訪(fǎng)問(wèn)頂點(diǎn)的第二個(gè)鄰接點(diǎn);后者從頂點(diǎn)開(kāi)始訪(fǎng)問(wèn)該頂點(diǎn)的所有鄰接點(diǎn)再依次向下,一層一層的訪(fǎng)問(wèn)。
在這里我是用對(duì)圖的存儲(chǔ)方式是用鄰接矩陣來(lái)實(shí)現(xiàn)的。
這種方法是用一個(gè)一維數(shù)組來(lái)表示頂點(diǎn),用一個(gè)二緯數(shù)組來(lái)實(shí)現(xiàn)對(duì)邊的存儲(chǔ),若a和b之間有邊,那就讓二位數(shù)組中的這兩個(gè)數(shù)據(jù)對(duì)應(yīng)的數(shù)據(jù)中填入1;沒(méi)有回路用一個(gè)很大的樹(shù)來(lái)表示就好了
具體可參考圖的鄰接矩陣百度百科
對(duì)于圖的廣度遍歷的具體的演示過(guò)程請(qǐng)看廣度優(yōu)先優(yōu)酷
在這里給出C++的代碼

在這里寫(xiě)的是無(wú)向圖,測(cè)試用的是無(wú)向圖,其實(shí)有向圖和這個(gè)基本一樣,只是加了一個(gè)邊的方向,想要修改也就是在搜索下一條邊的時(shí)候要搜兩次,一次是a到b,一個(gè)是b到a;

這是隊(duì)列的實(shí)現(xiàn)代碼

//
//  Graph.hpp
//  圖的廣度優(yōu)先
//
//  Created by 橘子和香蕉 on 2018/11/26.
//  Copyright ? 2018 橘子和香蕉. All rights reserved.
//


/*
 這這里用鏈表的形式來(lái)實(shí)現(xiàn)隊(duì)列;
        隊(duì)列的實(shí)現(xiàn)有兩種,一種是數(shù)組,循環(huán)隊(duì)列。一種是鏈表
        兩個(gè)指針,一個(gè)指向頭,一個(gè)指向尾。數(shù)據(jù)是在尾指針添加,在頭指針刪除。
 */

#include <iostream>
using namespace std;
typedef struct qnode{
    int position;
    qnode *next;
}qnode;

class Cqueue{
private:
    qnode *front;//頭指針
    qnode *rear;//尾指針
    int length;//隊(duì)列的長(zhǎng)度
public:
    Cqueue();
    ~Cqueue();
    void inQueue(int data);//data入隊(duì)
    int  outQueue();//出隊(duì)列
    void printCqueue();//輸出隊(duì)列
    bool isEmpty();//判斷隊(duì)列是不是空
};
Cqueue::Cqueue(){
    front =  new qnode;
    front->next = NULL;
    rear = front;
    length = 0;
}
Cqueue::~Cqueue(){
    qnode *p;
    while (front != NULL ) {
        p = front;
        front = front->next;
        delete p;
    }
}
void Cqueue::inQueue(int data){
//    qnode *n = new qnode;
//    n->position = data;
//    n->next = rear->next;
//    rear->next = n;
//    rear = n;
//    if(front ->next == NULL ){
//        front->next = n;
//    }
//    length ++;
    qnode *p = new qnode;
    p->position = data;
    p->next = NULL;
    rear->next = p;
    rear = p;
    length++;
    

}
int Cqueue::outQueue(){
    if(front == rear){
        cout<<"error\n";
        return -1;
    }
//    qnode *p = front ->next;
//    int data = p->position;
//    front->next = p->next;
//    if(p->next == NULL){
//        rear = front;
//    }
//    delete p;
//    length--;
//    return data;
    qnode *p = front->next;
    int data = p->position;
    front->next = p->next;
    if(p->next ==NULL){
        rear = front;
    }
    delete p;
    length--;
    return data;
}
void Cqueue::printCqueue(){
    qnode *p = front->next;
    while (p != NULL) {
        cout<<p->position<<"\t";
        p = p->next;
    }
    cout<<"length"<<length<<endl;
    cout<<endl;
}
bool  Cqueue::isEmpty(){
    return front == rear?true:false;
}


下面是圖的實(shí)現(xiàn)代碼:

//
//  Graph.hpp
//  圖的廣度優(yōu)先
//
//  Created by 橘子和香蕉 on 2018/11/26.
//  Copyright ? 2018 橘子和香蕉. All rights reserved.
//


#include <iostream>
using namespace std;
#define VERTEXNUM 100
#define dataType char
typedef struct node{
    dataType data;
    bool isAccess;
}node;
class Graph{
private:
    node vertex[VERTEXNUM];//頂點(diǎn)表
    int edge[VERTEXNUM][VERTEXNUM];//邊表
    int vertexNum;//頂點(diǎn)個(gè)數(shù)
    int edgeNum;//邊的個(gè)數(shù)
    
    int locate(dataType data);//在頂點(diǎn)表中找data的位置
    bool isHaveNevEdge(dataType data);//data是不是還有鄰接點(diǎn)
    void move(dataType data);//若一個(gè)頂點(diǎn)被刪除后,沒(méi)有鄰接點(diǎn),那就從頂點(diǎn)表中刪除這個(gè)元素,然后在頂點(diǎn)表和邊表中都要移動(dòng)數(shù)據(jù)元素。
    int  getFirstVertex(dataType data);//得到元素的第一個(gè)鄰接點(diǎn)位置。
    int  getNextNevVertex(dataType data,dataType  FrontData);//得到data頂點(diǎn)從Frontdata之后的第一個(gè)頂點(diǎn)位置。
    
public:
    void init(int vertex ,int edgeNum);//初始化邊數(shù)和頂點(diǎn)數(shù)。并且初始化邊表的數(shù)組。
     void  create();//創(chuàng)建圖
    void printGraph();//輸出
    void addEdge(dataType start,dataType end);//添加一條邊
    void deleteEdege(dataType start,dataType end);//刪除一條邊
    void breadthFirstSearch(dataType data);//廣度優(yōu)先遍歷
};

int Graph::locate(dataType data){
    for (int i  = 0; i<vertexNum;i++) {
        if(vertex[i].data == data){
            return I;
        }
    }
    return -1;
}
void Graph::create(){
    cout<<"input Graph data\n";
    for (int i = 0; i<vertexNum; i++) {
        cin>>vertex[i].data;
        vertex[i].isAccess  = false;
    }
    for (int j = 0; j<edgeNum; j++) {
        
    cout<<"input start and end of edge:\n";
    char start ,end;
    cin>>start>>end;
    int startPosition = locate(start);
    int endPosition = locate(end);
    edge[startPosition][endPosition] = 1;
    edge[endPosition][startPosition] = 1;
        
    }
    
}
void Graph::init(int vertex, int edgeNum){
    this->vertexNum = vertex;
    this->edgeNum = edgeNum;
    for (int i = 0; i<VERTEXNUM; i++) {
        for (int j = 0; j<VERTEXNUM; j++) {
            edge[i][j] = INT_MAX;
        }
    }
    
}
void Graph::printGraph(){
    cout<<endl;
    cout<<endl;
    cout<<"頂點(diǎn)邊:\n";
    cout<<"vertexNum:"<<vertexNum<<" edgeNum:"<<edgeNum<<endl;
    for (int i = 0; i<vertexNum; i++) {
        cout<<vertex[i].data<<"\t";
    }
    
    cout<<"邊表如下:\n";
    for (int j = 0; j<edgeNum; j++) {
        for (int k = 0; k<edgeNum ; k++) {
            cout<<edge[j][k]<<"\t";
        }
        cout<<endl;
    }
}
bool  Graph::isHaveNevEdge( dataType data ){
    int position = locate(data);
    for (int i = 0; i<vertexNum; i++) {
        if(edge[position][i] !=  INT_MAX){
            return true;
        }
    }
    return false;
}
void Graph::move(dataType data){
    int positon = locate(data);
    for (int i = positon; i<vertexNum; i++) {
        vertex[i].data = vertex[i+1].data;
        vertex[i].isAccess = vertex[i+1].isAccess;
    }
    vertexNum --;
    
    for (int i = positon; i<vertexNum; i++) {
        for (int j = 0; j<vertexNum; j++) {
            edge[i][j] = edge[i+1][j];
        }
    }
    edgeNum--;
}
void Graph::addEdge(char start, char end){
    int startPositon = locate(start);
    int endPosition = locate(end);
    if(startPositon == -1 && endPosition != -1){
        vertex[vertexNum].data = start;
        vertex[vertexNum].isAccess = false;
        this->vertexNum+=1;
        this->edgeNum+=1;
        startPositon = vertexNum-1;
    }
    if(startPositon != -1 && endPosition == -1){
        vertex[vertexNum].data = end;
        vertex[vertexNum].isAccess = false;
        this->vertexNum+=1;
        this->edgeNum+=1;
        endPosition = vertexNum-1;
    }
    if(startPositon == -1 && endPosition == -1){
        vertex[vertexNum].data = start;
        vertex[vertexNum].isAccess = false;
        this->vertexNum+=1;
        this->edgeNum+=0;
        startPositon = vertexNum-1;
        
        vertex[vertexNum].data = end;
        vertex[vertexNum].isAccess = false;
        this->vertexNum+=1;
        this->edgeNum+=1;
        endPosition = vertexNum-1;
    }
    
    edge[startPositon][endPosition] = 1;
    edge[endPosition][startPositon] = 1;
    cout<<startPositon<<endPosition;
    cout<<  edge[startPositon][endPosition]<<";"<<edge[endPosition][startPositon] <<endl;
}
void Graph::deleteEdege(dataType start, dataType end){
    int startPosition = locate(start);
    int endPosition = locate(end);
    if(startPosition == -1 || endPosition == -1){
        cout<<"error\n";
        return;
    }
    edge[startPosition][endPosition] = INT_MAX;
    edge[endPosition][startPosition] = INT_MAX;
    if(! isHaveNevEdge(start)){
        move(start);
    }
    if(! isHaveNevEdge(end)){
        move(end);
    }
}
int  Graph::getFirstVertex(dataType data){
    int position = locate(data);
    for (int i = 0; i<vertexNum; i++) {
        if( edge[position][i] == 1 ){
            return I;
        }
    }
    return -1;
}
int Graph::getNextNevVertex(dataType data ,dataType frontData){
    int position = locate(data);
    int frontPosition = locate(frontData);
    for (int i = frontPosition+1 ; i<vertexNum; i++) {
        if(edge[position][i] == 1){
            return I;
        }
    }
    return  -1;
}

void Graph::breadthFirstSearch(dataType data){
    cout<<"DFS:\n";
    Cqueue queue;
    int position = locate(data);
    int queueHeadPosition = -1;
    if(position == -1){
        cout<<"the vettex is not exist\n";
        return;
    }
    vertex[position].isAccess = true;
    queue.inQueue(position);//  入隊(duì)列
    
    cout<<position<<endl;
    
    
    while(queue.isEmpty() == false){
        queueHeadPosition = queue.outQueue();//獲得隊(duì)列的頭元素
         cout<<vertex[queueHeadPosition].data<<"\t";
        for (
             int i = getFirstVertex(vertex[queueHeadPosition].data);//獲得隊(duì)列頭元素的第一個(gè)鄰接點(diǎn)
             i>=0;
             i = getNextNevVertex(vertex[queueHeadPosition].data, vertex[i].data)//獲得從i之后下一個(gè)鄰接點(diǎn)
             ) {
            if(vertex[i].isAccess == false){
                queue.inQueue(i);
                vertex[i].isAccess = true;
//                cout<<vertex[i].data<<"\t";
            }
        }
    }
    
    
    
    cout<<endl;
}

下面給出main函數(shù):

//
//  main.cpp
//  圖的廣度優(yōu)先
//
//  Created by 橘子和香蕉 on 2018/11/26.
//  Copyright ? 2018 橘子和香蕉. All rights reserved.
//

#include <iostream>
using namespace std;
int main(){
//        Cqueue a;
//        a.inQueue(3);
//        a.inQueue(4);
//        a.inQueue(5);
//        a.printCqueue();
//        a.outQueue();
//        a.printCqueue();
//        a.outQueue();
//        a.printCqueue();
//        for (int i = 23; i<40; i++) {
//            a.inQueue(i);
//        }
//        a.printCqueue();
    Graph s;
    s.init(4, 4);
    s.create();
    s.printGraph();
//    s.addEdge('b', 'e');
//    s.printGraph();
//    s.deleteEdege('b', 'e');
//    s.printGraph();
//    s.breadthFirstSearch('d');
//    s.breadthFirstSearch('b');
    s.breadthFirstSearch('a');
    
    return 1;
}


注意:在測(cè)試的時(shí)候不能在一次運(yùn)行的時(shí)候連續(xù)從一個(gè)頂點(diǎn)開(kāi)始廣度優(yōu)先遍歷,因?yàn)樵诘谝淮伪闅v的時(shí)候就將其設(shè)置為訪(fǎng)問(wèn)過(guò),第二次遍歷的時(shí)候就不能繼續(xù)訪(fǎng)問(wèn)了,這個(gè)問(wèn)題也是很好解決的,就是在添加一個(gè)函數(shù)用來(lái)沒(méi)次訪(fǎng)問(wèn)完了后將所有的結(jié)點(diǎn)設(shè)置為沒(méi)有訪(fǎng)問(wèn)過(guò)就好了,這樣就可以在一次運(yùn)行中連續(xù)運(yùn)行。而不是下面這樣,需要注釋幾個(gè),每次只能從一個(gè)結(jié)點(diǎn)出發(fā)。

image.png

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,345評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,494評(píng)論 3 416
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 176,283評(píng)論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀(guān)的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 62,953評(píng)論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,714評(píng)論 6 410
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 55,186評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評(píng)論 3 441
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,410評(píng)論 0 288
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,940評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,776評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,976評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評(píng)論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,210評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 34,642評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 35,878評(píng)論 1 286
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,654評(píng)論 3 391
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,958評(píng)論 2 373

推薦閱讀更多精彩內(nèi)容