图(c实现)
图
- 顶点(vertex)
- 关联(adjacent)
限制:不允许自身环
术语:
- 路径: 多条相通的边的组成
- 权重:图的边上附带的信息
- 网:带权重的图
- 简单路径:路径不包含重复的顶点
- 完全图:具有在最多边的图
- 回路(环路):起点与终点相同的路径
- 度(degree):出度与入度
有向图
基本概念
边的顶点对有方向的图
顶点与弧
-
强连通:两点之间有路径可以达到
-
强连通图:图中任意两点之间的路径都可到达
-
强连通分量:极大连通子图
-
<a,b>:a邻接到b,b邻接与a
-
<a,b>:<a,b>关联a,b
无向图

基本概念
边的两个顶点之间没有次序关系的图
顶点与边
-
无向边连通:连点之间存在路径
-
连通图:任意两点之间都是连通的
-
连通分量:无向图中极大连通子图
-
(a,b):a,b相邻
-
(a,b):(a,b)关联a,b;
图的结构实现
顺序实现
#define INF ~(1 << (sizeof(int)*8 -1)) // 最大值
#define GRAPHMAXNUM 30 //图的顶点容量
typedef int VertexElempy; //顶点元素
typedef struct GRAPH{
VertexElempy vertexs[GRAPHMAXNUM]; //顶点集合
int adjacents[GRAPHMAXNUM][GRAPHMAXNUM]; //关联集合
int veNum; // 顶点数量
int adNum; // 关联数量
}Graph;
链式实现
#define INF ~(1 << (sizeof(int)*8 -1)) // 最大值
#define GRAPHMAXNUM 30 //图的顶点容量
typedef int VertexElempy; //顶点元素
typedef struct VERTEXNODE* pVertexNode; //顶点节点指针
typedef struct VERTEXNODE
{
VertexElempy vertex; //顶点
pVertexNode link; //关联
}VertexNode;
typedef struct LGRAPH{
pVertexNode nodes[GRAPHMAXNUM]; //顶点链表集合
int veNum; // 顶点数量
int adNum; // 关联数量
}LGraph;
图的遍历
深度优先
迭代方式

代码实现:
/*
依赖:
顺序图-Graph;栈-Stack;
改进:
链表图-LGraph;
封装:
1.该节点是否被遍历
2.判断是否遍历完全
3.节点的顺序存储位置
优化:
1.去掉压栈空间 - 压入顶点地址 - 增加查找顶点位置函数
*/
void GraphDfs(Graph *graph)
{
if (!graph)
{
return ;
}
if (graph->veNum==0)
{
return ;
}
Stack *stack = StackInit();
int visit[GRAPHMAXNUM] = {0}; // 遍历情况
int endFlag = 1; // 结束标志
int p = 0; // 所处节点位置
int temp[GRAPHMAXNUM] = {0}; // 压栈空间
temp[p]=p;
StackPush(stack, &temp[p]);
printf("%d ",graph->vertexs[0]);
visit[0]=1;
for (;;)
{
// 判断遍历情况
endFlag = 1;
for (size_t i = 0; i < graph->veNum; i++)
{
if (!visit[i])
{
endFlag = 0;
}
}
if (endFlag)
{
printf("\n");
break;
}
for (size_t i = 0; i < graph->veNum; i++)
{
// 存在关系
if (graph->adjacents[p][i]!=INF)
{
// 关系顶点未被访问
if (!visit[i])
{
p = i;
temp[p] = p;
visit[p] = 1;
endFlag = 1;
StackPush(stack,&temp[p]);
printf("%d ",graph->vertexs[p]);
break;
}
}
}
// 变更为已访问顶点
if (!endFlag)
{
p = *(int *)StackPop(stack);
}
}
printf("\n");
}
递归方式

层次遍历
迭代方式

代码实现:
/*
依赖:
Queue-队列 GraphVertexOn-查找顶点存储位置
问题:
能否使用递归实现?
*/
void GraphBfs(Graph *graph)
{
Queue *queue = queueInit();
VertexElempy p;
int visit[GRAPHMAXNUM] = {0};
queueIn(queue, &graph->vertexs[0]);
while (!queueIsEmpty(queue))
{
// 弹出队列
p = *(VertexElempy *)queueOut(queue);
visit[GraphVertexOn(graph, p)] = 1;
printf("%d ", p);
for (size_t i = 0; i < graph->veNum; i++)
{
// 未被访问,存在关联的顶点,存入队列
// 满足条件全部入栈
if (!visit[i] &&
graph->adjacents[GraphVertexOn(graph, p)][i] != INF)
{
queueIn(queue,&graph->vertexs[i]);
visit[i] = 1;
}
}
}
printf("\n");
}
最短路径
递归方式
| 中转节点 | A | B | C | D | E | F | G |
|---|---|---|---|---|---|---|---|
| A | 0 | 28 | INF | INF | INF | 10 | INF |
| B | 0 | 28 | 44 | INF | INF | 10 | 42 |
| F | 0 | 28 | 44 | INF | 35 | 10 | 42 |
| G | 0 | 28 | 44 | 52 | 35 | 10 | 42 |
| C | 0 | 28 | 44 | 52 | 35 | 10 | 42 |
| E | 0 | 28 | 44 | 52 | 35 | 10 | 42 |
| D | 0 | 28 | 44 | 52 | 35 | 10 | 42 |

代码实现:
int GraphShortyPath(Graph *graph)
{
int visit[GRAPHMAXNUM] = {0};
for (size_t j = 0; j < graph->veNum; j++)
{
GraphShortOne(graph, graph->vertexs[j], visit, j);
for (size_t i = 0; i < graph->veNum; i++)
{
visit[i]=0;
}
}
return 0;
}
int GraphShortOne(Graph *graph, VertexElempy vertex, int *visit, int num)
{
int endFlag = 1;
visit[num] = 1;
// 顶点遍历完全
for (size_t i = 0; i < graph->veNum; i++)
{
if (!visit[i])
{
endFlag = 0;
}
}
if (endFlag)
{
return 0;
}
for (size_t j = 0; j < graph->veNum; j++)
{
// 存在关联 关系顶点未被访问
if (!visit[j] && graph->adjacents[GraphVertexOn(graph, vertex)][j] != INF)
{
// 更新距离
for (size_t i = 0; i < graph->veNum; i++)
{
// 递归顶点 与 目标顶点数据不进行更新
if (i == num || i == j)
{
;
}
else
{
int a,b,c;
a = graph->adjacents[num][i];
b = graph->adjacents[j][i];
c = graph->adjacents[num][j];
if (b==INF)
{
continue;
}
graph->adjacents[num][i]=a>c+b?c+b:a;
}
}
visit[j] = 1;
GraphShortOne(graph,graph->vertexs[j],visit,num);
}
}
return 0;
}

浙公网安备 33010602011771号