图的广度遍历和深度遍历思想不一样。后者是用递归的方法来实现的,这个是要借助队列来实现的。
实现的基本思想如下:
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;
}
注意:在测试的时候不能在一次运行的时候连续从一个顶点开始广度优先遍历,因为在第一次遍历的时候就将其设置为访问过,第二次遍历的时候就不能继续访问了,这个问题也是很好解决的,就是在添加一个函数用来没次访问完了后将所有的结点设置为没有访问过就好了,这样就可以在一次运行中连续运行。而不是下面这样,需要注释几个,每次只能从一个结点出发。