数据结构学习之图的深度优先遍历和广度优先遍历

图的遍历的关键是确定每个顶点的邻接顶点,也即是确定由顶点组成的邻接矩阵。

这里参考网上的一篇基于C++的邻接矩阵的实现过程

图类的实现:

#include<iostream>
#include<iomanip>
using namespace std;
//最大权值
#define MAXWEIGHT 100
//用邻接矩阵实现图
class Graph
{
private:
	//是否带权
	bool isWeighted;
	//是否有向
	bool isDirected;
	//顶点数
	int numV;
	//边数
	int numE;
	//邻接矩阵
	int **matrix;
public:
	/*
	构造方法
	numV是顶点数,isWeighted是否带权值,isDirected是否有方向
	*/
	Graph(int numV, bool isWeighted = false, bool isDirected = false);
	//建图
	void createGraph();
	//析构方法
	~Graph();
	//获取顶点数
	int getVerNums()
	{return numV;}
	//获取边数
	int getEdgeNums()
	{return numE;}
	//设置指定边的权值
	void setEdgeWeight(int tail, int head, int weight);
	//打印邻接矩阵
	void printAdjacentMatrix();
	//检查输入
	bool check(int i, int j, int w = 1);
};

图类的定义:

/*
构造方法
numV是顶点数,isWeighted是否带权值,isDirected是否有方向
*/
Graph::Graph(int numV, bool isWeighted, bool isDirected)
{
	while (numV <= 0)
	{
		cout << "输入的顶点数不正确!,重新输入 ";
		cin >> numV;
	}
	this->numV = numV;
	this->isWeighted = isWeighted;
	this->isDirected = isDirected;
	matrix = new int*[numV];  //指针数组
	int i, j;
	for (i = 0; i < numV; i++)
		matrix[i] = new int[numV];
	//对图进行初始化
	if (!isWeighted)  //无权图
	{
		//所有权值初始化为0
		for (i = 0; i < numV; i++)
		for (j = 0; j < numV; j++)
			matrix[i][j] = 0;
	}
	else  //有权图
	{
		//所有权值初始化为最大权值
		for (i = 0; i < numV; i++)
		for (j = 0; j < numV; j++)
			matrix[i][j] = MAXWEIGHT;
	}
}
//建图
void Graph::createGraph()
{
	cout << "输入边数 ";
	while (cin >> numE && numE < 0)
		cout << "输入有误!,重新输入 ";

	int i, j, w;
	if (!isWeighted)  //无权图
	{
		if (!isDirected)  //无向图
		{
			cout << "输入每条边的起点和终点:\n";
			for (int k = 0; k < numE; k++)
			{
				cin >> i >> j;
				while (!check(i, j))
				{
					cout << "输入的边不对!重新输入\n";
					cin >> i >> j;
				}
				matrix[i][j] = matrix[j][i] = 1;
			}
		}
		else  //有向图
		{
			cout << "输入每条边的起点和终点:\n";
			for (int k = 0; k < numE; k++)
			{
				cin >> i >> j;
				while (!check(i, j))
				{
					cout << "输入的边不对!重新输入\n";
					cin >> i >> j;
				}
				matrix[i][j] = 1;
			}
		}
	}
	else  //有权图
	{
		if (!isDirected)   //无向图
		{
			cout << "输入每条边的起点、终点和权值:\n";
			for (int k = 0; k < numE; k++)
			{
				cin >> i >> j >> w;
				while (!check(i, j, w))
				{
					cout << "输入的边不对!重新输入\n";
					cin >> i >> j >> w;
				}
				matrix[i][j] = matrix[j][i] = w;
			}
		}
		else  //有向图
		{
			cout << "输入每条边的起点、终点和权值:\n";
			for (int k = 0; k < numE; k++)
			{
				cin >> i >> j >> w;
				while (!check(i, j, w))
				{
					cout << "输入的边不对!重新输入\n";
					cin >> i >> j >> w;
				}
				matrix[i][j] = w;
			}
		}
	}
}
//析构方法
Graph::~Graph()
{
	int i = 0;
	for (i = 0; i < numV; i++)
		delete[] matrix[i];
	delete[]matrix;
}
//设置指定边的权值
void Graph::setEdgeWeight(int tail, int head, int weight)
{
	if (isWeighted)
	{
		while (!check(tail, head, weight))
		{
			cout << "输入不正确,重新输入边的起点、终点和权值 ";
			cin >> tail >> head >> weight;
		}
		if (isDirected)
			matrix[tail][head] = weight;
		else
			matrix[tail][head] = matrix[head][tail] = weight;
	}
	else
	{
		while (!check(tail, head, 1))
		{
			cout << "输入不正确,重新输入边的起点、终点 ";
			cin >> tail >> head;
		}
		if (isDirected)
			matrix[tail][head] = 1-matrix[tail][head];
		else
			matrix[tail][head] = matrix[head][tail] = 1 - matrix[tail][head];
	}
}
//输入检查
bool Graph::check(int i, int j, int w)
{
	if (i >= 0 && i < numV && j >= 0 && j < numV && w > 0 && w <= MAXWEIGHT)
		return true;
	else
		return false;
}
//打印邻接矩阵
void Graph::printAdjacentMatrix()
{
	int i, j;
	cout.setf(ios::left);
	cout << setw(4) << " ";
	for (i = 0; i < numV; i++)
		cout << setw(4) << i;
	cout << endl;
	for (i = 0; i < numV; i++)
	{
		cout << setw(4) << i;
		for (j = 0; j < numV; j++)
			cout << setw(4) << matrix[i][j];
		cout << endl;
	}
}

调用主函数main:

int main()
{
	cout << "******使用邻接矩阵实现图结构***by David***" << endl;
	bool isDirected, isWeighted;
	int numV;
	cout << "建图" << endl;
	cout << "输入顶点数 ";
	cin >> numV;
	cout << "边是否带权值,0(不带) or 1(带) ";
	cin >> isWeighted;
	cout << "是否是有向图,0(无向) or 1(有向) ";
	cin >> isDirected;
	Graph graph(numV, isWeighted, isDirected);
	cout << "这是一个";
	isDirected ? cout << "有向、" : cout << "无向、";
	isWeighted ? cout << "有权图" << endl : cout << "无权图" << endl;
	graph.createGraph();
	cout << "打印邻接矩阵" << endl;
	graph.printAdjacentMatrix();
	cout << endl;
	int tail, head, weight;
	cout << "修改指定边的权值" << endl;
	if (isWeighted)  //针对有权图
	{
		cout << "输入边的起点、终点和权值 ";
		cin >> tail >> head >> weight;
		graph.setEdgeWeight(tail, head, weight);
	}
	else  //针对无权图
	{
		cout << "输入边的起点、终点 ";
		cin >> tail >> head;
		graph.setEdgeWeight(tail, head, 1);
	}
	cout << "修改成功!" << endl;
	cout << "打印邻接矩阵" << endl;
	graph.printAdjacentMatrix();
	system("pause");
	return 0;
}

以上是计算有向/无向和有权/无权图的顶点邻接矩阵。

参考博文:http://blog.csdn.net/zhangxiangdavaid/article/details/38321327

下面是一个图的深度优先遍历和广度优先遍历的实例:

深度优先遍历:

/*
深度优先搜索
从vertex开始遍历,visit是遍历顶点的函数指针
*/
void Graph::dfs(int vertex, void (*visit)(int))
{
	stack<int> s;
	//visited[i]用于标记顶点i是否被访问过
	bool *visited = new bool[numV];
	//count用于统计已遍历过的顶点数
	int i, count;
	for (i = 0; i < numV; i++)
		visited[i] = false;
	count = 0;
	while (count < numV)
	{
		visit(vertex);
		visited[vertex] = true;
		s.push(vertex);
		count++;
		if (count == numV)
			break;
		while (visited[vertex])
		{
			for (i = 0; i < numV
				&& (visited[i] 
				|| matrix[vertex][i] == 0 || matrix[vertex][i] == MAXWEIGHT); i++);
			if (i == numV)  //当前顶点vertex的所有邻接点都已访问完了
			{
				if (!s.empty())
				{
					s.pop();   //此时vertex正是栈顶,应先出栈
					if (!s.empty())
					{
						vertex = s.top();
						s.pop();
					}
					else  //若栈已空,则需从头开始寻找新的、未访问过的顶点
					{
						for (vertex = 0; vertex < numV && visited[vertex]; vertex++);
					}
				}
				else  //若栈已空,则需从头开始寻找新的、未访问过的顶点
				{
					for (vertex = 0; vertex < numV && visited[vertex]; vertex++);
				}
			}
			else  //找到新的顶点应更新当前访问的顶点vertex
				vertex = i;
		}
	}
	delete[]visited;
}

广度优先遍历:

/*
广度优先搜索
从vertex开始遍历,visit是遍历顶点的函数指针
*/
void Graph::bfs(int vertex, void(*visit)(int))
{
	//使用队列
	queue<int> q;
	//visited[i]用于标记顶点i是否被访问过
	bool *visited = new bool[numV];
	//count用于统计已遍历过的顶点数
	int i, count;
	for (i = 0; i < numV; i++)
		visited[i] = false;
	q.push(vertex);
	visit(vertex);
	visited[vertex] = true;
	count = 1;
	while (count < numV)
	{
		if (!q.empty())
		{
			vertex = q.front();
			q.pop();
		}
		else
		{
			for (vertex = 0; vertex < numV && visited[vertex]; vertex++);
			visit(vertex);
			visited[vertex] = true;
			count++;
			if (count == numV)
				return;
			q.push(vertex);
		}
		//代码走到这里,vertex是已经访问过的顶点
		for (int i = 0; i < numV; i++)
		{
			if (!visited[i] && matrix[vertex][i] > 0 && matrix[vertex][i] < MAXWEIGHT)
			{
				visit(i);
				visited[i] = true;
				count ++;
				if (count == numV)
					return;
				q.push(i);
			}
		}
	}
	delete[]visited;
}

主调用函数:

void visit(int vertex)
{
	cout << setw(4) << vertex;
}
int main()
{
	cout << "******图的遍历:深度优先、广度优先***by David***" << endl;
	bool isDirected, isWeighted;
	int numV;
	cout << "建图" << endl;
	cout << "输入顶点数 ";
	cin >> numV;
	cout << "边是否带权值,0(不带) or 1(带) ";
	cin >> isWeighted;
	cout << "是否是有向图,0(无向) or 1(有向) ";
	cin >> isDirected;
	Graph graph(numV, isWeighted, isDirected);
	cout << "这是一个";
	isDirected ? cout << "有向、" : cout << "无向、";
	isWeighted ? cout << "有权图" << endl : cout << "无权图" << endl;
	graph.createGraph();
	cout << "打印邻接矩阵" << endl;
	graph.printAdjacentMatrix();
	cout << endl;
	cout << "深度遍历" << endl;
	for (int i = 0; i < numV; i++)
	{
		graph.dfs(i, visit);
		cout << endl;
	}
	cout << endl;
	cout << "广度遍历" << endl;
	for (int i = 0; i < numV; i++)
	{
		graph.bfs(i, visit);
		cout << endl;
	}
	system("pause");
	return 0;
}

参考自博文:http://blog.csdn.net/zhangxiangdavaid/article/details/38323633

下面是参考《大话数据结构》上的C语言实现的图的深度遍历和广度遍历:

#include <stdio.h>
#include <stdlib.h>
#include <iostream>
using namespace std;

#define MAXVEX 100
#define MAX 100
#define INFINITY 65535
#define MAXSIZE 100

typedef int VertexType;
typedef int EdgeType;
typedef int Boolean;
Boolean visited[MAX];
typedef int QElemType;

typedef struct{
    QElemType data[MAXSIZE];
    int front;
    int rear;
}Queue;


typedef struct{
    VertexType vexs[MAXVEX];
    EdgeType arc[MAXVEX][MAXVEX];
    int numVertexes,numEdges;
}MGraph;

void CreateMGraph(MGraph *g)
{
    int i,j,k,w;
    cout<<" 请输入顶点数和边数:"<<endl;
    //scanf("%d,%d",&g->numVertexes,&g->numEdges);
    cin>>g->numVertexes;
    cin>>g->numEdges;
    for(i=0;i<g->numVertexes;i++){
        //scanf(&g->vexs[i]);
        cin>>g->vexs[i];
    }
    for(i=0;i<g->numVertexes;i++){
        for(j=0;j<g->numVertexes;j++){
            g->arc[i][j]=INFINITY;
        }
    }
    for(k=0;k<g->numEdges;k++){
        cout<<"输入边(vi,vj)上的下标i,下标j和权w:"<<endl;
        //scanf("%d,%d,%d",&i,&j,&w);
        cin>>i;
        cin>>j;
        cin>>w;
        g->arc[i][j]=w;
        g->arc[j][i]=g->arc[i][j];
    }
}

void DFS(MGraph *g,int i)
{
    int j;
    visited[i]=1;
    cout<<" G.vexs[i]= "<<g->vexs[i]<<" i= "<<i<<endl;
    for(j=0;j<g->numVertexes;j++){
        if(g->arc[i][j]==1  && !visited[j]){
            cout<<"j= "<<j;
            DFS(g,j);
        }
    }
}
void DFSTraverse(MGraph *g)
{
    int i;
    for(i=0;i<g->numVertexes;i++){
        visited[i]=0;
    }
    for(i=0;i<g->numVertexes;i++){
        if(!visited[i]){
            cout<<"in Traverse i= "<<i<<endl;
            DFS(g,i);
        }
    }
}

int InitQueue(Queue *Q)
{
    Q->front=0;
    Q->rear=0;

    return 0;
}
int EnQueue(Queue *Q,QElemType e)
{
    if((Q->rear+1)%MAXSIZE == Q->front) ///判断队列是f否满
       return -1;
       Q->data[Q->rear]=e;
       Q->rear=(Q->rear+1)%MAXSIZE;

       return 0;
}
int DeQueue(Queue *Q,QElemType *e)
{
    if(Q->front == Q->rear)
        return -1;
    *e=Q->data[Q->front];
    Q->front=(Q->front+1)%MAXSIZE;

    return 0;
}
int QueueEmpty(Queue *Q)
{
    if(Q->rear == Q->front)
        return -1;
    return 0;
}
///广度优先遍历
void BFSTraverse(MGraph *G)
{
    int i,j;
    Queue Q;
    for(i=0;i<G->numVertexes;i++)
        visited[i]=0;
        InitQueue(&Q); ///初始化辅助队列
        for(i=0;i<G->numVertexes;i++){   ///循环访问每一个顶点
            if(!visited[i]){   ///处理未访问过的
                visited[i]=1;
                cout<<"G->vexs[i]= "<<G->vexs[i];
                EnQueue(&Q,i);  ///此顶点入队列
                while(!QueueEmpty(&Q)){  ///判断当前队列是否为空
                    DeQueue(&Q,&i); ///队中元素出队列
                    for(j=0;j<G->numVertexes;j++){   ///判断其他与此顶点有共同边的顶点
                        if(G->arc[i][j]==1 &&!visited[j]){  ///判断是否访问过
                            visited[j]=1;  ///标记已经访问过
                            cout<<" g->vexs[j]= "<<G->vexs[j]<<endl;
                            EnQueue(&Q,j); ///将找到的顶点入队列
                        }
                    }
                }
             }
        }
}
int main()
{
   MGraph *g;
   g=(MGraph *)malloc(sizeof(MGraph));
   CreateMGraph(g);
   ///DFSTraverse(g);  ///深度优先遍历
   BFSTraverse(g);  ///广度优先遍历

    return 0;
}

上面的C版本实现用到了队列/栈的思想,但是从验证上看貌似有些问题,有细心看出来的同学可以留言指点出来

    原文作者:数据结构之图
    原文地址: https://blog.csdn.net/qq_26460507/article/details/76222207
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞