数据结构——图的广度优先遍历

图的广度遍历和深度遍历思想不一样。后者是用递归的方法来实现的,这个是要借助队列来实现的。
实现的基本思想如下:
1、从图中某个顶点V0出发,并访问此顶点;
2、从V0出发,访问V0的各个未曾访问的邻接点W1,W2,…,Wk;然后,依次从W1,W2,…,Wk出发访问各自未被访问的邻接点;
3、重复步骤2,直到全部顶点都被访问为止。</br>
广度优先遍历是以层为顺序,和树的层次遍历差不多,是将某一层上的所有节点都搜索到了之后才向下一层搜索;而深度优先遍历是将某一条枝桠上的所有节点都搜索到了之后,才转向搜索另一条枝桠上的所有节点。
深度优先遍历从某个顶点出发,首先访问这个顶点,然后找出刚访问这个结点的第一个未被访问的邻结点,然后再以此邻结点为顶点,继续找它的下一个新的顶点进行访问,重复此步骤,直到所有结点都被访问完为止。
广度优先遍历从某个顶点出发,首先访问这个顶点,然后找出这个结点的所有未被访问的邻接点,访问完后再访问这些结点中第一个邻接点的所有结点,重复此方法,直到所有结点都被访问完为止。
可以看到两种方法最大的区别在于前者从顶点的第一个邻接点一直访问下去再访问顶点的第二个邻接点;后者从顶点开始访问该顶点的所有邻接点再依次向下,一层一层的访问。
在这里我是用对图的存储方式是用邻接矩阵来实现的。
这种方法是用一个一维数组来表示顶点,用一个二纬数组来实现对边的存储,若a和b之间有边,那就让二位数组中的这两个数据对应的数据中填入1;没有回路用一个很大的树来表示就好了
具体可参考图的邻接矩阵百度百科
对于图的广度遍历的具体的演示过程请看广度优先优酷
在这里给出C++的代码

在这里写的是无向图,测试用的是无向图,其实有向图和这个基本一样,只是加了一个边的方向,想要修改也就是在搜索下一条边的时候要搜两次,一次是a到b,一个是b到a;

这是队列的实现代码

//
//  Graph.hpp
//  图的广度优先
//
//  Created by 橘子和香蕉 on 2018/11/26.
//  Copyright © 2018 橘子和香蕉. All rights reserved.
//


/*
 这这里用链表的形式来实现队列;
        队列的实现有两种,一种是数组,循环队列。一种是链表
        两个指针,一个指向头,一个指向尾。数据是在尾指针添加,在头指针删除。
 */

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

class Cqueue{
private:
    qnode *front;//头指针
    qnode *rear;//尾指针
    int length;//队列的长度
public:
    Cqueue();
    ~Cqueue();
    void inQueue(int data);//data入队
    int  outQueue();//出队列
    void printCqueue();//输出队列
    bool isEmpty();//判断队列是不是空
};
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;
}


下面是图的实现代码:

//
//  Graph.hpp
//  图的广度优先
//
//  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];//顶点表
    int edge[VERTEXNUM][VERTEXNUM];//边表
    int vertexNum;//顶点个数
    int edgeNum;//边的个数
    
    int locate(dataType data);//在顶点表中找data的位置
    bool isHaveNevEdge(dataType data);//data是不是还有邻接点
    void move(dataType data);//若一个顶点被删除后,没有邻接点,那就从顶点表中删除这个元素,然后在顶点表和边表中都要移动数据元素。
    int  getFirstVertex(dataType data);//得到元素的第一个邻接点位置。
    int  getNextNevVertex(dataType data,dataType  FrontData);//得到data顶点从Frontdata之后的第一个顶点位置。
    
public:
    void init(int vertex ,int edgeNum);//初始化边数和顶点数。并且初始化边表的数组。
     void  create();//创建图
    void printGraph();//输出
    void addEdge(dataType start,dataType end);//添加一条边
    void deleteEdege(dataType start,dataType end);//删除一条边
    void breadthFirstSearch(dataType data);//广度优先遍历
};

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<<"顶点边:\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);//  入队列
    
    cout<<position<<endl;
    
    
    while(queue.isEmpty() == false){
        queueHeadPosition = queue.outQueue();//获得队列的头元素
         cout<<vertex[queueHeadPosition].data<<"\t";
        for (
             int i = getFirstVertex(vertex[queueHeadPosition].data);//获得队列头元素的第一个邻接点
             i>=0;
             i = getNextNevVertex(vertex[queueHeadPosition].data, vertex[i].data)//获得从i之后下一个邻接点
             ) {
            if(vertex[i].isAccess == false){
                queue.inQueue(i);
                vertex[i].isAccess = true;
//                cout<<vertex[i].data<<"\t";
            }
        }
    }
    
    
    
    cout<<endl;
}

下面给出main函数:

//
//  main.cpp
//  图的广度优先
//
//  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;
}


注意:在测试的时候不能在一次运行的时候连续从一个顶点开始广度优先遍历,因为在第一次遍历的时候就将其设置为访问过,第二次遍历的时候就不能继续访问了,这个问题也是很好解决的,就是在添加一个函数用来没次访问完了后将所有的结点设置为没有访问过就好了,这样就可以在一次运行中连续运行。而不是下面这样,需要注释几个,每次只能从一个结点出发。

image.png

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,039评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,223评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,916评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,009评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,030评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,011评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,934评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,754评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,202评论 1 309
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,433评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,590评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,321评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,917评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,568评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,738评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,583评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,482评论 2 352

推荐阅读更多精彩内容