图

基本概念
图:一种多对多的结构,是由顶点的有穷⾮空集合和顶点之间边的集合组成。通常表示为: G(V,E). 其中,G表示一个图, V是图G中的顶点集合,E是图G中边的集合。
子图:指的是由图中一部分顶点和边构成的图,称为原图的子图。
图的存储
图的存储分为两种,顺序存储和链式存储

顺序存储(邻接矩阵)
图的顺序存储需要用到两个数组,一个数组存放图中顶点本身的数据(一维数组),另外一个数组用于存储各顶点之间的关系(二维数组/邻接矩阵)。
假设图G有n个顶点,则邻接矩阵是一个n*n的方阵,定义为:



arc[v1][v0]>0代表顶点v1、v0为邻接点。
数据结构如下:
typedef char VertexType; /* 顶点类型 */
typedef int EdgeType; /* 边上的权值类型 */
typedef struct
{
VertexType vexs[MAXVEX]; /* 顶点表 */
EdgeType arc[MAXVEX][MAXVEX];/* 邻接矩阵,可看作边表 */
int numNodes, numEdges; /* 图中当前的顶点数和边数 */
}MGraph;
顺序存储的深度优先遍历
- 递归
/*DFS遍历*/
Boolean visited[MAXVEX]; /* 访问标志的数组 */
//1. 标识顶点是否被标记过;
//2. 选择从某一个顶点开始(注意:非连通图的情况)
//3. 进入递归,打印i点信息,标识; 边表
//4. [i][j] 是否等于1,没有变遍历过visted
void DFS(MGraph G,int i){
//1.
visited[i] = TRUE;
printf("%c",G.vexs[i]);
//2.0~numVertexes
for(int j = 0; j < G.numVertexes;j++){
if(G.arc[i][j] == 1 && !visited[j])
DFS(G, j);
}
}
void DFSTravese(MGraph G){
//1.初始化
for(int i=0;i<G.numVertexes;i++){
visited[i] = FALSE;
}
//2.某一个顶点,避免出现孤岛的情况
for(int i = 0;i<G.numVertexes;i++){
if(!visited[i]){
DFS(G, i);
}
}
}
- 非递归-利用栈思想
void DFSTraverse_1(MGraph G) {
int* stack = (int*)malloc(sizeof(int)*G.numVertexes);
int* visited = (int*)calloc(G.numVertexes, sizeof(int));
//此次循环也是为了避免孤岛的情况出现
for(int j = 0; j < G.numVertexes; j++) {
if(visited[j] == 0) {
visited[j] = 1;
int top = -1;
//第一个顶点入栈
stack[++top] = j;
printf("%c ", G.vexs[j]);
while(top>-1) {
int topPosition = stack[top];
//找到栈顶顶点对应的邻接点数组
int *m = G.arc[topPosition];
int i;
for(i = 0; i < G.numVertexes; i++) {
//遍历数组,找到未访问过的邻接点则入栈,
if(m[i]==1 && visited[i]==0) {
stack[++top] = i;
printf("%c ", G.vexs[i]);
visited[i] = 1;
break;
}
}
//如果当前数组的顶点都被访问过,则需要出栈,从上一个顶点开始遍历
if(i==G.numVertexes) {
top--;
}
}
}
}
free(stack);
free(visited);
}
顺序存储的广度优先遍历
void BFSTraverse(MGraph G){
//创建一个队列
int *queue = (int*)calloc(G.numVertexes, sizeof(int));
//队头、队尾
int rear = 0;
int front = 0;
//是否访问顶点的数组
int *visited = (int*)calloc(G.numVertexes, sizeof(int));
//防止出现孤岛
for(int i = 0; i < G.numVertexes; i++) {
//第一个顶点入队
if(visited[i] == 0) {
visited[i] = 1;
printf("%c ", G.vexs[i]);
queue[rear++] = i;
while(rear != front) {
//顶点出队
int topPosition = queue[front++];
//找到顶点对应的邻接数组
int *m = G.arc[topPosition];
for(int j = 0; j < G.numVertexes; j++) {
//遍历数组,将未被访问过的邻接点加入队列并访问
if(m[j] == 1 && visited[j] == 0) {
queue[rear++] = j;
visited[j] = 1;
printf("%c ", G.vexs[j]);
}
}
}
}
}
free(queue);
}
链式存储(邻接表)
和顺序存储类似,需要有一个数组来存储图的各个顶点,不同的是,该数组里面的元素是类似链表的数据结构。


/* 邻接表结构****************** */
typedef struct EdgeNode /* 边表结点 */
{
int adjvex; /* 邻接点域,存储该顶点对应的下标 */
int weight; /* 用于存储权值,对于非网图可以不需要 */
struct EdgeNode *next; /* 链域,指向下一个邻接点 */
}EdgeNode;
typedef struct VertexNode /* 顶点表结点 */
{
int in; /* 顶点入度 */
char data; /* 顶点域,存储顶点信息 */
EdgeNode *firstedge;/* 边表头指针 */
}VertexNode, AdjList[MAXVEX];
typedef struct
{
AdjList adjList;
int numVertexes,numEdges; /* 图中当前顶点数和边数 */
}graphAdjList,*GraphAdjList;
深度优先遍历
邻接表的遍历和链接矩阵的遍历很类似,唯一的区别在于邻接矩阵是需要遍历每一个顶点的邻接数组,而邻接表需要遍历的是每一个顶点的邻接链表。
- 递归
Boolean visited[MAXSIZE]; /* 访问标志的数组 */
/* 邻接表的深度优先递归算法 */
void DFS(GraphAdjList GL, int i)
{
EdgeNode *p;
visited[i] = TRUE;
//2.打印顶点 A
printf("%c ",GL->adjList[i].data);
p = GL->adjList[i].firstedge;
//3.
while (p) {
if(!visited[p->adjvex])
DFS(GL,p->adjvex);
p = p->next;
}
}
/* 邻接表的深度遍历操作 */
void DFSTraverse(GraphAdjList GL)
{
//1. 将访问记录数组默认置为FALSE
for (int i = 0; i < GL->numVertexes; i++) {
/*初始化所有顶点状态都是未访问过的状态*/
visited[i] = FALSE;
}
//2. 选择一个顶点开始DFS遍历. 例如A
for(int i = 0; i < GL->numVertexes; i++)
//对未访问过的顶点调用DFS, 若是连通图则只会执行一次.
if(!visited[i])
DFS(GL, i);
}
- 非递归-栈
void DFSTraverse_1(GraphAdjList GL) {
int* stack = (int*)calloc(GL->numVertexes, sizeof(int));
int* visited = (int*)calloc(GL->numVertexes, sizeof(int));
EdgeNode *edgeNode;
for(int i = 0; i < GL->numVertexes; i++) {
if(visited[i]==0) {
visited[i] = 1;
int top = -1;
stack[++top] = i;
printf("%c ", GL->adjList[i].data);
while(top>-1) {
int topPostion = stack[top];
edgeNode = (GL->adjList[topPostion]).firstedge;
while(edgeNode) {
if(visited[edgeNode->adjvex] == 0) {
visited[edgeNode->adjvex] = 1;
stack[++top] = edgeNode->adjvex;
printf("%c ", GL->adjList[edgeNode->adjvex].data);
break;
}
edgeNode = edgeNode->next;
}
if(edgeNode == NULL) {
top--;
}
}
}
}
free(stack);
free(visited);
}
广度优先遍历
void BFSTraverse_1(GraphAdjList GL){
//创建一个队列
int *queue = (int*)calloc(GL->numVertexes, sizeof(int));
//队头、队尾
int rear = 0;
int front = 0;
EdgeNode *node;
//是否访问顶点的数组
int *visited = (int*)calloc(GL->numVertexes, sizeof(int));
//防止出现孤岛
for(int i = 0; i < GL->numVertexes; i++) {
//第一个顶点入队
if(visited[i] == 0) {
visited[i] = 1;
printf("%c ", GL->adjList[i].data);
queue[rear++] = i;
while(rear != front) {
//顶点出队
node = GL->adjList[front++].firstedge;
while(node) {
//遍历数组,将未被访问过的邻接点加入队列并访问
if(visited[node->adjvex] == 0) {
queue[rear++] = node->adjvex;
visited[node->adjvex] = 1;
printf("%c ", GL->adjList[node->adjvex].data);
}
node = node->next;
}
}
}
}
}