图的简述:
图是一种复杂的数据结构,图(Graph)是由顶点(Vertex)组成的非空有穷集合和顶点之间的边(Edge)组成。
图的边可以由权值(weight),也可以没有,有权值的图称为网图。
图的边也可以有方向,没有方向的为无向图,有方向的为有向图。
图很复杂,就需要复杂的方式来表示图,一般简单的使用邻接矩阵和邻接表来表示,还有十字链表和邻接多重表等,这里只介绍前两种的实现。
无向图的邻接矩阵结构表示:
邻接矩阵简单也方便理解,即用一个一维数组储存顶点,用另一个二维数组储存各个顶点之间的边的信息。
无向图的邻接矩阵是一个对称矩阵,即arc[i][j]=arc
C++实现:
typedef char VertexType; //顶点类型 ,默认为char
typedef int EdgeType; //边的权值 默认int
#define MAXVERTEX 100 //最大的顶点数
//邻接矩阵的结构
typedef struct MGraph
{
VertexType vexs[MAXVERTEX]; //顶点数组
EdgeType arc[MAXVERTEX][MAXVERTEX]; //邻接矩阵 即边数组
int vertexNum; //顶点数
int edgeNum; //边数
};
无向邻接矩阵的深度优先遍历:
深度优先所搜(DepthFirstSearch)表示用某种规则把一个顶点所连接的后面顶点都遍历一遍,并用一个标识符来表示是否遍历过该节点。特点是不放过任何一个节点。从一个节点出发,遍历所有未访问的节点,结束后如果遍历完后仍有节点未被访问,则换一个节点作为起始点,重复此步骤直至所有节点都被访问。
bool visited[MAXVERTEX];
//深度优先遍历邻接矩阵
void DFSTraverse(MGraph G)
{
int i;
for (i = 0; i < G.vertexNum; i++)
{
visited[i] = false;
}
for (i = 0; i < G.vertexNum; i++)
{
if (visited[i] != true)
{
DepthFirstSearch(G, i); //如果该顶点延伸出去所有顶点都访问过,则访问下一个顶点
}
}
}
DepthFirstSearch函数用递归实现:
//深度优先搜索邻接矩阵
void DepthFirstSearch(MGraph G,int i)
{
int j;
visited[i] = true; //把该顶点标识符置为true
cout << G.vexs[i]; //遍历到后打印该顶点
for (j = 0; j < G.vertexNum; j++)
{
if (G.arc[i][j] == 1 && visited[j] != true)
{
DepthFirstSearch(G, j); //递归遍历未访问的节点直到没有
}
}
}
无向邻接矩阵的广度优先遍历:
//广度优先遍历邻接矩阵
void BFSTraverse(MGraph G)
{
int i, j;
queue<VertexType> q; //临时队列
for (i = 0; i < G.vertexNum; i++)
{
visited[i] = false;
}
for (i = 0; i < G.vertexNum; i++)
{
if (!visited[i])
{
visited[i] = true; //标识符设为true
cout << G.vexs[i]; //遍历到该顶点,打印顶点
q.push(G.vexs[i]); //入队
while (q.empty() != true) //如果队列不为空
{
q.pop();
for (j = 0; j < G.vertexNum; j++)
{
if (G.arc[i][j] == 1 && !visited[j])
{
visited[j] = true;
cout << G.vexs[j]; //遍历并打印
q.push(G.vexs[j]);
}
}
}
}
}
}
无向图的邻接表结构表示:
邻接表类似于链表,存在顶点节点和边节点,用顶点节点的指针指向该顶点所连接的边节点。
//邻接表的结构
typedef struct EdgeNode //边表节点
{
int adjvex; //该邻接点对应的顶点下标
EdgeType weight; //顶点到该邻接点的权值
EdgeNode *Next; //指向顶点的下一个邻接点
};
typedef struct VertexNode //顶点表节点
{
VertexType data; //顶点节点及信息
EdgeNode *firstEdge; //边表头指针,指向顶点的第一个边表
}AdjList[MAXVERTEX];
typedef struct GraphAdjList //邻接表
{
AdjList adjList; //顶电表数组
int vertexNum; //顶点数
int EdgeNum; //边数
};
无向网图邻接表的创建:
//无向图的邻接表的创建
void CreateALGraph(GraphAdjList *G)
{
int i, j, k;
EdgeNode *e;
cout << "输入顶点数和边数:";
cin >> G->vertexNum >> G->EdgeNum; //读入顶点数和边数
//创建顶点表
for (i = 0; i < G->vertexNum; i++)
{
cin >> G->adjList[i].data; //读入顶点节点信息
G->adjList[i].firstEdge = NULL; //边表初始化为NULL表
}
//创建边表
for (k = 0; k < G->EdgeNum; k++)
{
cout << "输入边(vi,vj)上的顶点序号:" << endl;
cin >> i >> j; //读入边的顶点序号
e = new EdgeNode(); //生成边表节点
//无向图,所以一条边对应两个节点i和j
//头插法
e->adjvex = j; //邻接点下标为j
e->Next = G->adjList[i].firstEdge; //e指向G的firstedge
G->adjList[i].firstEdge = e; //firstEdge指向该边节点
e = new EdgeNode();
e->adjvex = i;
e->Next = G->adjList[j].firstEdge;
G->adjList[j].firstEdge = e;
}
}
无向邻接表的深度优先遍历:
和邻接矩阵的遍历很相似
//深度优先遍历邻接表
void DepthFirstSearchAdjList(GraphAdjList GAL, int i)
{
EdgeNode *e;
visited[i] = true; //标识符设为true
cout << GAL.adjList[i].data; //遍历到后打印顶点信息
e = GAL.adjList[i].firstEdge;
while (e)
{
if (!visited[e->adjvex])
{
DepthFirstSearchAdjList(GAL, e->adjvex); //递归遍历未访问的邻接点
}
e = e->Next;
}
}
//深度优先搜索邻接表
void DFSTraverse(GraphAdjList GAL)
{
int i;
for (i = 0; i < GAL.vertexNum; i++)
{
visited[i] = false;
}
for (i = 0; i < GAL.vertexNum; i++)
{
if (!visited[i])
{
DepthFirstSearchAdjList(GAL, i);
}
}
}
无向邻接表的广度优先遍历:
//广度优先遍历邻接表
void BFSTraverse(GraphAdjList GAL)
{
int i;
EdgeNode *p;
queue<int> q;
for (i = 0; i < GAL.vertexNum; i++)
{
visited[i] = false;
}
for (i = 0; i < GAL.vertexNum; i++)
{
if (!visited[i])
{
visited[i] = true;
cout << GAL.adjList[i].data<< endl;
q.push[GAL.adjList[i]];
while (q.empty() != true)
{
q.pop();
p = GAL.adjList[i].firstEdge;
while (p)
{
if (!visited[p->adjvex])
{
visited[p->adjvex] = true;
cout << GAL.adjList[p->adjvex].data;
q.push(p->adjvex);
}
p = p->Next;
}
}
}
}
}