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

简介:

图的广度遍历和深度遍历思想不一样。后者是用递归的方法来实现的,这个是要借助队列来实现的。
实现的基本思想如下:
1、从图中某个顶点V0出发,并访问此顶点;
2、从V0出发,访问V0的各个未曾访问的邻接点W1,W2,…,Wk;然后,依次从W1,W2,…,Wk出发访问各自未被访问的邻接点;
3、重复步骤2,直到全部顶点都被访问为止。
广度优先遍历是以层为顺序,和树的层次遍历差不多,是将某一层上的所有节点都搜索到了之后才向下一层搜索;而深度优先遍历是将某一条枝桠上的所有节点都搜索到了之后,才转向搜索另一条枝桠上的所有节点。
深度优先遍历从某个顶点出发,首先访问这个顶点,然后找出刚访问这个结点的第一个未被访问的邻结点,然后再以此邻结点为顶点,继续找它的下一个新的顶点进行访问,重复此步骤,直到所有结点都被访问完为止。
广度优先遍历从某个顶点出发,首先访问这个顶点,然后找出这个结点的所有未被访问的邻接点,访问完后再访问这些结点中第一个邻接点的所有结点,重复此方法,直到所有结点都被访问完为止。
可以看到两种方法最大的区别在于前者从顶点的第一个邻接点一直访问下去再访问顶点的第二个邻接点;后者从顶点开始访问该顶点的所有邻接点再依次向下,一层一层的访问。
在这里我是用对图的存储方式是用邻接矩阵来实现的。
这种方法是用一个一维数组来表示顶点,用一个二纬数组来实现对边的存储,若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

相关文章
|
1月前
|
存储 编译器 C++
【初阶数据结构】掌握二叉树遍历技巧与信息求解:深入解析四种遍历方法及树的结构与统计分析
【初阶数据结构】掌握二叉树遍历技巧与信息求解:深入解析四种遍历方法及树的结构与统计分析
|
5月前
|
存储 算法
数据结构===图
数据结构===图
|
1月前
|
存储 算法
数据结构与算法学习十六:树的知识、二叉树、二叉树的遍历(前序、中序、后序、层次)、二叉树的查找(前序、中序、后序、层次)、二叉树的删除
这篇文章主要介绍了树和二叉树的基础知识,包括树的存储方式、二叉树的定义、遍历方法(前序、中序、后序、层次遍历),以及二叉树的查找和删除操作。
27 0
|
2月前
|
存储 算法 C语言
数据结构基础详解(C语言): 二叉树的遍历_线索二叉树_树的存储结构_树与森林详解
本文从二叉树遍历入手,详细介绍了先序、中序和后序遍历方法,并探讨了如何构建二叉树及线索二叉树的概念。接着,文章讲解了树和森林的存储结构,特别是如何将树与森林转换为二叉树形式,以便利用二叉树的遍历方法。最后,讨论了树和森林的遍历算法,包括先根、后根和层次遍历。通过这些内容,读者可以全面了解二叉树及其相关概念。
|
3月前
【数据结构】遍历二叉树(递归思想)-->赋源码
【数据结构】遍历二叉树(递归思想)-->赋源码
53 4
|
4月前
|
算法 Python
逆袭之路!用 Python 玩转图的 DFS 与 BFS,让数据结构难题无处遁形
【7月更文挑战第12天】图的遍历利器:DFS 和 BFS。Python 中,图可表示为邻接表或矩阵。DFS 沿路径深入,回溯时遍历所有可达顶点,适合找路径和环。BFS 层次遍历,先近后远,解决最短路径问题。两者在迷宫、网络路由等场景各显神通。通过练习,掌握这些算法,图处理将游刃有余。
65 3
|
4月前
|
存储 算法 Python
“解锁Python高级数据结构新姿势:图的表示与遍历,让你的算法思维跃升新高度
【7月更文挑战第13天】Python中的图数据结构用于表示复杂关系,通过节点和边连接。常见的表示方法是邻接矩阵(适合稠密图)和邻接表(适合稀疏图)。图遍历包括DFS(深度优先搜索)和BFS(广度优先搜索):DFS深入探索分支,BFS逐层访问邻居。掌握这些技巧对优化算法和解决实际问题至关重要。**
45 1
|
5月前
|
编译器 数据库 索引
数据结构篇:树形数据结构的基本概念及其遍历方法
数据结构篇:树形数据结构的基本概念及其遍历方法
87 0
|
5月前
|
存储 算法
【二叉树】数据结构——BST二叉树基本概念及算法设计(插入、删除、遍历操作)
【二叉树】数据结构——BST二叉树基本概念及算法设计(插入、删除、遍历操作)
|
5月前
|
存储
数据结构学习记录——如何建立图(邻接矩阵、邻接表-图节点的结构、创建并初始化、插入变、完整图的建立)
数据结构学习记录——如何建立图(邻接矩阵、邻接表-图节点的结构、创建并初始化、插入变、完整图的建立)
78 0