/*已知有向图G用邻接矩阵存储,设计算法以分别求解顶点vi的入度、出度和度。
*/
#include<iostream>
using namespace std;
#define max 1000
#define INF 65535
typedef char element;
typedef int cellType;
typedef enum{UDG,UDN,DG,DN}GraphKind;//枚举图的类型
typedef struct GraphAdjMatrix {
element data[max ];//顶点数组,存放顶点元素的值,从0开始
cellType AdjMatrix[max ][max ];//邻接矩阵,元素类型为cellType
int verNum;//顶点数
int arcNum;//弧数
GraphKind gkind;//图的类型,0-无向图,1-无向网,2-有向图,3-有向网
}Graph;//图的类型名
bool visited[max + 1]; //全局数组,标记顶点是否已经访问,visited[0]单元不用
void visit(Graph& G, int verID)
{ //顶点编号从1开始,数组0单元不用
cout << G.data[verID] << "\t";
visited[verID] = true;
}
void strLTrim(char* str); //删除字符串左边空格
//删除字符串、字符数组左边空格
void strLTrim(char* str)
{
int i, j;
int n = 0;
n = strlen(str) + 1;
for (i = 0; i < n; i++)
{
if (str[i] != ' ') //找到左起第一个非空格位置
break;
}
//以第一个非空格字符为手字符移动字符串
for (j = 0; j < n; j++)
{
str[j] = str[i];
i++;
}
}
//从数据文件创建邻接矩阵表示的图
int CreateGraphFromFile(char fileName[], Graph& G)
{
FILE* pFile; //定义文件指针
char str[1000]; //存放读出一行文本的字符串
char strTemp[10]; //判断是否注释行
cellType eWeight; //边的信息,常为边的权值
GraphKind graphType; //图类型枚举变量
errno_t err;
err = fopen_s(&pFile,fileName, "r");
if (!pFile)
{
cout << "错误:文件打开失败。" << endl;
return false;
}
while (fgets(str, 1000, pFile) != NULL)
{
strLTrim(str); //删除字符串左边空格,这是一个自定义的函数
if (str[0] == '\n') //空行,继续读取下一行
continue;
strncpy_s(strTemp,sizeof(strTemp), str, 2);
if (strstr(strTemp, "//") != NULL) //跳过注释行
continue;
else //非注释行、非空行,跳出循环
break;
}
//循环结束,str中应该已经是图的标识Graph,判断标识是否正确
if (strstr(str, "Graph") == NULL)
{
cout<<"错误:打开的文件格式错误! "<<endl;
fclose(pFile); //关闭文件
return false;
}
//读取图的类型,跳过空行
while (fgets(str, 1000, pFile) != NULL)
{
strLTrim(str); //删除字符串左边空格,这是一个自定义函数
if (str[0] == '\n') //空行,继续读取下一行
continue;
strncpy_s(strTemp, sizeof(strTemp), str, 2);
if (strstr(strTemp, "//") != NULL) //注释行,跳过,继续读取下一行
continue;
else //非空行,也非注释行,即图的类型标识
break;
}
//设置图的类型
if (strstr(str, "UDG"))
graphType = UDG; //无向图
else if (strstr(str, "UDN"))
graphType = UDN; //无向网
else if (strstr(str, "DG"))
graphType = DG; //有向图
else if (strstr(str, "DN"))
graphType = DN; //有向网
else
{
cout<<"错误:读取图的类型标记失败! "<<endl;
fclose(pFile); //关闭文件
return false;
}
//读取顶点行数据到str。跳过空行
while (fgets(str, 1000, pFile) != NULL)
{
strLTrim(str); //删除字符串左边空格,这是一个自定义函数
if (str[0] == '\n') //空行,继续读取下一行
continue;
strncpy_s(strTemp, sizeof(strTemp), str, 2);
if (strstr(strTemp, "//") != NULL) //注释行,跳过,继续读取下一行
continue;
else //非空行,也非注释行,即图的顶点元素行
break;
}
//顶点数据放入图的顶点数组
char* ptr = NULL;
char* token = strtok_s(str, " ",&ptr);
int nNum = 0;
while (token != NULL)
{
G.data[nNum] = *token;
token = strtok_s(NULL, " ",&ptr);
nNum++;
}
//图的邻接矩阵初始化
int nRow = 0; //矩阵行下标
int nCol = 0; //矩阵列下标
if (graphType == UDG || graphType == DG)
{
for (nRow = 0; nRow < nNum; nRow++)
for (nCol = 0; nCol < nNum; nCol++)
G.AdjMatrix[nRow][nCol] = 0;
}
else
{
for (nRow = 0; nRow < nNum; nRow++)
for (nCol = 0; nCol < nNum; nCol++)
G.AdjMatrix[nRow][nCol] = INF; //INF表示无穷大
}
//循环读取边的数据到邻接矩阵
int edgeNum = 0; //边的数量
element Nf, Ns; //边或弧的2个相邻顶点
while (fgets(str, 1000, pFile) != NULL)
{
strLTrim(str); //删除字符串左边空格,这是一个自定义函数
if (str[0] == '\n') //空行,继续读取下一行
continue;
strncpy_s(strTemp, sizeof(strTemp), str, 2);
if (strstr(strTemp, "//") != NULL) //注释行,跳过,继续读取下一行
continue;
char* ptr = NULL;
char* token = strtok_s(str, " ", &ptr);
if (token == NULL) //分割为空串,失败退出
{
cout<<"错误:读取图的边数据失败! "<<endl;
fclose(pFile); //关闭文件
return false;
}
Nf = *token; //获取边的第一个顶点
token = strtok_s(NULL, " ",&ptr); //读取下一个子串,即第二个顶点
if (token == NULL) //分割为空串,失败退出
{
cout << "错误:读取图的边数据失败! " << endl;
fclose(pFile); //关闭文件
return false;
}
Ns = *token; //获取边的第二个顶点
//从第一个顶点获取行号
for (nRow = 0; nRow < nNum; nRow++)
{
if (G.data[nRow] == Nf) //从顶点列表找到第一个顶点的编号
break;
}
//从第二个顶点获取列号
for (nCol = 0; nCol < nNum; nCol++)
{
if (G.data[nCol] == Ns) //从顶点列表找到第二个顶点的编号
break;
}
//如果为网,读取权值
if (graphType == UDN || graphType == DN)
{ //读取下一个子串,即边的附加信息,常为边的权重
token = strtok_s(NULL, " ",&ptr);
if (token == NULL) //分割为空串,失败退出
{
cout << "错误:读取图的边数据失败! " << endl;
fclose(pFile); //关闭文件
return false;
}
eWeight = atoi(token); //取得边的附加信息
}
if (graphType == UDN || graphType == DN)
G.AdjMatrix[nRow][nCol] = eWeight;
//如果为网,邻接矩阵中对应的边设置权值,否则置为1
else
G.AdjMatrix[nRow][nCol] = 1;
edgeNum++; //边数加1
}
G.verNum = nNum; //图的顶点数
if (graphType == UDG || graphType == UDN)
G.arcNum = edgeNum / 2; //无向图或网的边数等于统计的数字除2
else
G.arcNum = edgeNum;
G.gkind = graphType; //图的类型
fclose(pFile); //关闭文件
return true;
}
//求入度
void inDegree(Graph G) {
int i = 0, j = 0, s1 = 0;
for (i = 0; i < G.verNum; i++) {
for (j = 0; j < G.verNum; j++) {
if (G.AdjMatrix[j][i] == 1)
s1++;
}
cout << G.data[i] << "," << s1 << endl;
}
}
//求出度
void outDegree(Graph G) {
int i = 0, j = 0, s2 = 0;
for (i = 0; i < G.verNum; i++) {
for (j = 0; j < G.verNum; j++) {
if (G.AdjMatrix[i][j] == 1)
s2++;
}
cout << G.data[i] << "," << s2 << endl;
}
}
//求度
void Degree(Graph G) {
int i = 0, j = 0, s = 0;
for (i = 0; i < G.verNum; i++) {
for (j = 0; j < G.verNum; j++) {
if ((G.AdjMatrix[i][j] == 1)|| (G.AdjMatrix[j][i] == 1))
s ++;
}
cout << G.data[i] << "," << s << endl;
}
}
int main() {
Graph G;
char fileName[]=" ";
cout << "输入要打开的文件名:";
cin >> fileName;
CreateGraphFromFile(fileName, G);
cout << "顶点入度为:"<<endl;
inDegree(G);
cout << "顶点出度为:" << endl;
outDegree(G);
cout << "顶点的度为:" << endl;
Degree(G);
return 0;
}
1、给俺点个赞呗,可以让更多的人看到这篇文章,顺便激励下我,嘻嘻。
2、老铁们,关注我的原创微信公众号「帅地玩编程」,专注于写算法 + 计算机基础知识(计算机网络+ 操作系统+数据库+Linux)。
保存让你看完有所收获,不信你打我。后台回复『电子书』送你一份精选电子书大礼包,包含各类技能的优质电子书。
回答:
首先,我们需要了解邻接矩阵的概念。邻接矩阵是用矩阵来表示图的一种方式,其中矩阵的行和列代表图中的节点,矩阵中的值表示两个节点之间是否存在边。
那么为什么会出现越界错误呢?可能是因为矩阵的大小没有设置正确,导致访问了非矩阵范围内的内存。解决这个问题的方法是在创建邻接矩阵时,确保矩阵的大小与节点数目一致。
以下是可能出现越界错误的示例代码:
node_num = 10
matrix = [[0] * node_num] * node_num
# 访问矩阵中不存在的一行或一列,会出现IndexError
matrix[node_num][0] = 1
为了避免这种问题,我们可以使用numpy库来创建矩阵,并使用numpy提供的函数来操作矩阵。以下是使用numpy的示例代码:
import numpy as np
node_num = 10
matrix = np.zeros((node_num, node_num))
# 在矩阵中赋值
matrix[0][1] = 1
通过使用numpy,我们可以简化代码,并避免越界错误。
创建的时候,每次更新verNum的时候,可以和max校验一下,看看有没有超