#include
#include
#include
主要有顶点数组、邻接矩阵、顶点数、弧数
#define Node 10
typedef struct
{
int vex[Node]; //顶点数组
int arcs[Node][Node]; //邻接矩阵
int vexNum, arcNum; //顶点数,弧数
} G, *Graph;
这里我在函数内直接读取键盘输入的顶点个数 i 来初始化图
初始化顶点数组为 -1 ,邻接矩阵为 0 ,顶点数为 i ,弧为 0
void initGraph(Graph g)
{
int i;
printf("input the number of Nodes:\n");
scanf("%d", &i);
g->vexNum = i;
g->arcNum = 0;
for (int t = 0; t < i; t++)
{
g->vex[t] = -1;
}
for (int z = 0; z < i; z++)
{
for (int k = 0; k < i; k++)
{
g->arcs[z][k] = 0;
}
}
return;
}
接受输入<(int)a,(int)b>
及((int)a,(int)b)
类型
修改returnData
数组的值
即returnData[0]=a returnData[1]=b
并返回合法与否:1/0
注:返回值只是单纯表示结点的值而非在数组中位置
本例中接受任意数字输入,可以理解为结点的名称为该数字(:
int inputValid(char *input, int *returnData)
{ //合法化判断 并且获得结点信息 这里设置的是int类型的数
int i = 1, j = 0, number = 0;
char getData[20];
if (input[0] != '(' && input[0] != '<')
{
if (input[0] == '#')
return 0;
printf("Wrong input %c\n", input[0]);
return 0;
}
while (input[i] != ',')
{
getData[j++] = input[i++];
}
for (int t = j - 1, z = 0; t >= 0; t--, z++)
{
number += (getData[t] - '0') * pow(10, z);
getData[t] = 0;
}
*returnData = number;
*returnData++;
number = 0;
i++;
j = 0;
while (input[i] != '>' && input[i] != ')')
{
getData[j++] = input[i++];
}
if (i > Node)
return 0;
for (int t = j - 1, z = 0; t >= 0; t--, z++)
{
number += (getData[t] - '0') * pow(10, z);
getData[t] = 0;
}
*returnData = number;
return 1;
}
返回结点在vex[]
数组内的下标索引
如果没有找到该结点则返回 -1
int GetNodePosition(Graph g, int inputNode)
{ //结点在数组的下标
for (int i = 0; i < g->vexNum; i++)
{
if (g->vex[i] == inputNode)
return i;
}
return -1;
}
这里直接使用该函数读取结点
功能:创建新结点、输入结点的关系来设置邻接矩阵->构建图
直接每轮读取一个字符串,形式:<,>/(,)
,结合上一个函数进行合法判断
(这里不合法就直接break了,误输入处理就没做233)
读取到#
就完成读取任务,结束循环
void SetNode(Graph g)
{ //获得节点的去重集合 写入信息 主要是顺序存储节点 如果节点重复就不存在节点数组里了
int j = 0, data[2] = {0}, status0 = 0, status1 = 0, position_i, position_j, typeOfGraph;
char input[20];
printf("input data the form should be <,> or (,) And those numbers should bigger than 0\n"); //<)这种输入目前不支持
printf("input # to quit\n");
scanf("%s", &input);
for (int i = 0; i < g->vexNum; i++)
{
for (int j =0;j<g->vexNum;j++)
g->arcs[i][j] = 0;
}
for (int z = 0; z < g->vexNum; z++)
{
g->vex[z] = -1;
}
while (inputValid(input, data) && input[0] != '#')
{
if (input[0] == '<')
typeOfGraph = 0; //有向图
else if (input[0] == '(')
typeOfGraph = 1; //无向图
for (int t = 0, count0 = 0, count1 = 0; t < g->vexNum; t++)
{
if (data[0] == g->vex[t])
count0++;
if (data[1] == g->vex[t] && data[0] != data[1])
count1++;
if (t == g->vexNum - 1 && count0 == 0)
status0++, printf("create new node %d\n", data[0]);
if (t == g->vexNum - 1 && count1 == 0)
status1++, printf("create new node %d\n", data[1]);
}
if (status0 > 0)
{
for (int t = 0; t < g->vexNum; t++)
{
if (g->vex[t] == -1)
{
g->vex[t] = data[0];
break;
}
}
status0 = 0;
}
if (status1 > 0)
{
for (int t = 0; t < g->vexNum; t++)
{
if (g->vex[t] == -1)
{
g->vex[t] = data[1];
break;
}
}
status1 = 0;
}
position_i = GetNodePosition(g, data[0]); //节点位置i
position_j = GetNodePosition(g, data[1]); //节点位置j (i,j)
if (typeOfGraph == 0)
{
g->arcs[position_i][position_j]++;
g->arcNum++;
}
else if (typeOfGraph == 1)
{
g->arcs[position_i][position_j]++;
g->arcs[position_j][position_i]++;
g->arcNum++;
}
data[0] = 0;
data[1] = 0;
fflush(stdin);
scanf("%s", &input);
}
}
输出图的结点数组以及邻接矩阵
void printGraph(Graph g)
{
int t,n,z;
for ( t = 0; t < g->vexNum; t++)
{
printf("Node%d:%d\n", t, g->vex[t]);
}
printf("\n");
printf(" ");
for ( n = 0; n < g->vexNum; n++)
{
printf("%d ", g->vex[n]);
}
printf("\n");
printf(" ");
for ( n = 0; n < g->vexNum; n++)
{
printf("- ", g->vex[n]);
}
printf("\n");
for ( t = 0; t < g->vexNum; t++)
{
printf("Node:%d|", g->vex[t]);
for ( z = 0; z < g->vexNum; z++)
{
printf("%d ", g->arcs[t][z]);
}
printf("\n");
}
}
基于邻接矩阵求解是否邻接
v w 都是结点的名称
返回值在正常情况下是 0 不邻接 /1 邻接
如果结点位置为 -1 则表示结点不存在或初始化错误,返回 -1 当作异常值
int isNeighbour(Graph g, int v, int w)
{ //邻接情况
int position_i, position_j;
position_i = GetNodePosition(g, v);
position_j = GetNodePosition(g, w);
if (position_j == -1 || position_i == -1)
return -1;
if (g->arcs[position_i][position_j] >= 1)
return 1;
return 0;
}
基于邻接矩阵
也就是求行和
int getOutDegree(Graph g, int v)
{ //计算出度
int position_v, sum = 0;
position_v = GetNodePosition(g, v);
if (position_v == -1)
return -1;
for (int i = 0; i < g->vexNum; i++)
{
sum += g->arcs[position_v][i];
}
return sum;
}
基于邻接矩阵
也就是求列和
int getInDegree(Graph g, int v)
{ //计算入度
int position_v, sum = 0;
position_v = GetNodePosition(g, v);
if (position_v == -1)
return -1;
for (int i = 0; i < g->vexNum; i++)
{
sum += g->arcs[i][position_v];
}
return sum;
}
void PrintMatrix(int depth, int (*a)[])
{
int(*p)[depth];
p = a;
for (int i = 0; i < depth; i++)
{
for (int j = 0; j < depth; j++)
{
printf("%d ", *(*(p + i) + j));
}
printf("\n");
}
}
void PrintNeighbourMatrix(Graph g)
{
for (int i = 0; i < g->vexNum; i++)
{
for (int j = 0; j < g->vexNum; j++)
{
printf("%d ", g->arcs[i][j]);
}
printf("\n");
}
}
会把b
矩阵加到a
矩阵去
void MatrixAdd(int depth, int (*a)[], int (*b)[])
{ //方阵加法 把后面的矩阵加到前面的矩阵
int(*p)[depth], (*p2)[depth];
p = a;
p2 = b;
for (int i = 0; i < depth; i++)
{
for (int j = 0; j < depth; j++)
{
*(*(p + i) + j) += *(*(p2 + i) + j);
}
}
}
把矩阵a与b
的乘法结构放到c
矩阵里
void MatrixMultiple(int depth, int (*a)[], int (*b)[], int (*c)[])
{
int(*p)[depth], (*p2)[depth], (*p3)[depth];
p = a;
p2 = b;
p3 = c;
for (int k = 0; k < depth; k++)
{
for (int i = 0; i < depth; i++)
{
for (int j = 0; j < depth; j++)
{
*(*(p3 + k) + i) += (*(*(p + k) + j)) * (*(*(p2 + j) + i));
}
}
}
}
Bn = A + A^2 + A^3 +…
若要求可达矩阵就把矩阵中 >0 的数换成1即可
这里的函数求得结果是Bn矩阵
Bn矩阵具有更多的信息
另:
求解可达矩阵的方法
1.Bn矩阵 >0 元素置换成1
2.布尔矩阵运算
3.Warshall算法
void getReachableMatrix(Graph g, int (*a)[])
{
int(*p)[g->vexNum], i = 1;
int b0[g->vexNum][g->vexNum], b1[g->vexNum][g->vexNum], b_init[g->vexNum][g->vexNum];
p = a;
for (int k = 0; k < g->vexNum; k++)
{
for (int z = 0; z < g->vexNum; z++)
{
b0[k][z] = g->arcs[k][z];
b_init[k][z] = g->arcs[k][z];
b1[k][z] = 0;
*(*(p + k) + z) = 0;
}
}
MatrixAdd(g->vexNum, p, b_init);
PrintMatrix(g->vexNum, p);
printf("****************\n");
while (i < g->vexNum)
{
for (int k = 0; k < g->vexNum; k++)
{
for (int z = 0; z < g->vexNum; z++)
{
b1[k][z] = 0;
}
}
MatrixMultiple(g->vexNum, b_init, b0, b1);
MatrixAdd(g->vexNum, p, b1);
for (int k = 0; k < g->vexNum; k++)
{
for (int z = 0; z < g->vexNum; z++)
{
b0[k][z] = b1[k][z];
}
}
i++;
}
return;
}
求第一个邻接结点(基于邻接矩阵)
这里直接调用了isNeighbour
函数
返回第一个邻接结点在结点矩阵中的下标索引
若无结点与之邻接则返回 -1
int firstNeighbourNode(Graph g, int v)
{
int position_v, position_return;
position_v = GetNodePosition(g, v);
for (position_return = 0; position_return < g->vexNum; position_return++)
{
if (position_return == position_v)
continue;
if (isNeighbour(g, v, g->vex[position_return]))
return position_return;
}
return -1;
}
求下一个邻接结点
返回v
下一个邻接结点的下标索引,并且该结点未被访问过
若没找到则返回 -1
int nextNeighbourNode(Graph g, int v, int isvisited[])
{
int position_v, position_return;
position_v = GetNodePosition(g, v);
for (position_return = 0; position_return < g->vexNum; position_return++)
{
if (position_return == position_v)
continue;
if (isNeighbour(g, v, g->vex[position_return]))
{
if (isvisited[position_return] == 0)
return position_return;
}
}
return -1;
}
利用数组visitedMat[]
(值为0/1) 确定当前结点是否被访问过
如果visitedMat[i]=1
即被访问过,就不再输出该结点
整体思路:
输出开始结点 -> 开始结点visited 设数组为1 -> w = v的第一个邻接结点位置
while(w 存在)
{
if(w处结点没访问过)调用自己 w作为开始位置
w 取v的下一个邻接结点
}
递归的实质也是利用了栈,如果想实现非递归就使用栈相关应用即可
void deepFirst_traver(Graph g, int v, int visitedMat[])
{ //深度优先遍历
int position_v, position_w; //v是进入递归的首结点
position_v = GetNodePosition(g, v);
printf("%d\n", v);
visitedMat[position_v] = 1;
position_w = firstNeighbourNode(g, v);
while (position_w != -1)
{
if (visitedMat[position_w] == 0)
deepFirst_traver(g, g->vex[position_w], visitedMat);
position_w = nextNeighbourNode(g, v, visitedMat);
}
}
注:这里需要使用队列
我使用的是上次写的 c语言实现队列基本功能
data类型没改,直接用了char类型的 ︿( ̄︶ ̄)︿
若运行有误调整成int型即可
整体思路:
访问初始结点v0,取其在顶点数组的下标索引入队
打印初始节点
while(队列不空)
{
队头顶点编号出队
w = vex[v位置]
的第一个邻接结点的位置索引
while(w 存在){
if(visited[w]
=0 也就是没被访问过){
打印该节点
设置visited数组
w位置入队列
}
更新w = vex[v位置]
的下一个结点(未被访问过的)
}
}
void breadth_FirstSearch(Graph g, int v0,Queue q){
int position_v,position_j,visited[g->vexNum],i,position;
char v;
for(i=0;i<g->vexNum;i++){
visited[i]=0;
}
initQueue(q);
position_v = GetNodePosition(g,v0);
printf("%d\n",v0);
visited[position_v] = 1;
QueueEnter(q,(char)position_v);
while(QueueEmpty(q) == 0){
QueueOut(q,&v);
position_j = firstNeighbourNode(g,g->vex[v]);
i = position_j;
while(position_j != -1){
position = position_j;
if(visited[position] == 0){
printf("%d\n",g->vex[position]);
visited[position]=1;
QueueEnter(q,(char)position);
}
position_j = nextNeighbourNode(g,g->vex[v],visited);
}
}
}
DFS
函数可以求解以i为开始结点的所有简单路径
再使用Hamilton
函数搜索所有路径
对DFS的修改之处:
计数器 n
访问顶点时会将该顶点的位置索引存储在path数组中
同时计数器 n自增1
递归出口在于 计数器的值 = 结点总数
如果某顶点的全部有邻接关系的结点都访问过,仍然未得到简单路径,就将该顶点置为未访问,计数器自减1
(相当于发现当前结点不合适,那么放弃本次寻找的路径,重新从另一点尝试)
void DFS(Graph g,int i,int path[],int visited[],int *n){
//深度优先算法找包含全部顶点的简单路径
//对于单个i(函数未进行递归时),本函数可以找到以位置i为起点的所有简单路径
int t,j;
visited[i] = 1;
path[*n]=i;
(*n)++;
//printf("---%d\n",*n);
if((*n)==g->vexNum){
for(t=0;t<g->vexNum-1;t++){
printf("%d->",g->vex[path[t]]);
}
printf("%d\n",g->vex[path[t]]);
}
for(j=0;j<g->vexNum;j++){
if(g->arcs[i][j] && !visited[j])DFS(g,j,path,visited,n);
}
visited[i]=0;
(*n)--;
}
void Hamilton(Graph g){
//哈密尔顿链
int i,n=0;
int visited[g->vexNum],path[g->vexNum];
for (i=0;i<g->vexNum;i++){
visited[i] = 0;
path[i] = 0;
}
for(i=0;i<g->vexNum;i++){
if(!visited[i])DFS(g,i,path,visited,&n);
}
}
代码部分I/O漏洞还是存在的
本例只为尝试实现一些基本的pseudo-code
(๑•̀ㅂ•́)و✧