图的存储结构分为邻接矩阵和邻接表两种,带权的图叫做网。
邻接矩阵
邻接矩阵适合边多的图
无向图
两个顶点之间的连线是双向的,一个一维数组存顶点,一个二维数组存边
若有边则值为1,反之为0
邻接矩阵需要知道点数、边数、一个二维数组存边、一个指针指向点,一个规定最多存放顶点数的值。
#define SIZE 10
class Graph
{
int m_MaxVertex;//最大顶点个数
int m_NumVertex;//实际顶点个数
int m_NumEdge;//边数
char* m_VertexArr;//存顶点
int m_Edge[SIZE][SIZE];//存边
public:
Graph();//构造
~Graph();//析构
void InsertVertex(char v);//插入点
void InsertEdge(char v1, char v2);//在点v1和v2之间插入边
void PrintGraph();//输出
int GetVertexIndex(char v);//获取下标
void DeleteEdge(char v1, char v2);//删除边
void DeleteVertex(char v);//删除点
};
构造和析构函数
Graph::Graph()
{
m_MaxVertex = SIZE;//设置最大的顶点数
m_NumVertex = m_NumEdge = 0;//初始为0
for (int i = 0; i < m_MaxVertex; i++)
{
for (int j = 0; j < m_MaxVertex; j++)
m_Edge[i][j] = 0;
}
m_VertexArr = new char[m_MaxVertex];//m_VertexArr指向装顶点的数组
}
Graph::~Graph()
{
if (m_VertexArr != nullptr)
{
delete[]m_VertexArr;
m_VertexArr = nullptr;
}
m_NumEdge = m_NumVertex = 0;
}
插入顶点
如果顶点数大于最多能存的顶点数,直接返回
否则存入存顶点的数组m_VertexArr,实际顶点数m_NumVertex++
//插入顶点
void Graph::InsertVertex(char v)
{
if (m_NumVertex >= m_MaxVertex)
{
return;
}
m_VertexArr[m_NumVertex++] = v;
}
得到一个顶点的下标
从第一个遍历到最后一个,若找到了,返回下标;若没找到,返回-1。
//得到下标
int Graph::GetVertexIndex(char v)
{
for (int i = 0; i < m_NumVertex; i++)
{
if (v == m_VertexArr[i])
{
return i;
}
}
return -1;
}
插入边
在v1和v2两个顶点之间插入边:
首先找到两个顶点的下标;
若找到,使其存边数组中值等于1,因为是无向图,所以要m_Edge[p1][p2] = m_Edge[p2][p1]=1;
边数++
//插入边
void Graph::InsertEdge(char v1, char v2)
{
int p1 = GetVertexIndex(v1);
int p2 = GetVertexIndex(v2);
if (p1 == -1 || p2 == -1)return;
m_Edge[p1][p2] = m_Edge[p2][p1]=1;
m_NumEdge++;
}
删除边
与插入边类似。
//删边
void Graph::DeleteEdge(char v1, char v2)
{
int p1 = GetVertexIndex(v1);
int p2 = GetVertexIndex(v2);
if (p1 == -1 || p2 == -1)return;
m_Edge[p1][p2] = m_Edge[p2][p1] = 0;
m_NumEdge--;
}
删除点
方法1:直接删
这个方法比较麻烦,要将点、边数组中该点后面的数据全部往前移,如果边多不适合该方法
1.找下标,2.求相连边数,3.删点,4.删边行列(出、入)
void Graph::DeleteVertex(char v)
{
int p = GetVertexIndex(v);//找点下标
if (p == -1)return;
int i = 0,j = 0;
int delEdge = 0;//删除边的个数
for (i = 0; i < m_NumVertex; i++)//计算与该点相连的边的个数
{
if (m_Edge[p][i] = 1)
delEdge++;
}
for (i = p; i < m_NumVertex-1; i++)
{
m_VertexArr[i] = m_VertexArr[i + 1];//删顶点(把数组中后面的点往前移)
}
for (i = p; i < m_NumVertex-1; i++)
{
for (j = 0; j < m_NumVertex; j++)
{
m_Edge[j][i] = m_Edge[j][i + 1];//删列
}
}
for (i = p; i < m_NumVertex - 1; i++)
{
for (j = 0; j < m_NumVertex - 1; j++)
{
m_Edge[i][j] = m_Edge[i + 1][j];//删行
}
}
m_NumEdge -= delEdge;//更新边数
m_NumVertex--;//更新点数
}
方法2:替换法
用最后一个顶点替换要删除的顶点,边用最后一行替换要删的,这样数组大小直接--就可删。
void Graph::DeleteVertex(char v)
{
int p = GetVertexIndex(v);//找下标
if (p == -1)return;
int i = 0, j = 0;
int delEdge = 0;//删除的边数
for (i = 0; i < m_NumVertex; i++)//求相连的边数
{
if (m_Edge[p][i] = 1)
delEdge++;
}
m_VertexArr[p] = m_VertexArr[m_NumVertex - 1];//要删的点替换成数组中最后一个点
for (i = 0; i < m_NumVertex; i++)
{
m_Edge[i][p] = m_Edge[i][m_NumVertex - 1];//要删的边替换成数组中最后列边
}
for (i = 0; i < m_NumVertex - 1; i++)//注意这里点数-1
{
m_Edge[p][i] = m_Edge[m_NumVertex - 1][i];//要删的边替换成数组中最后行边
}
m_NumEdge -= delEdge;//更新边
m_NumVertex--;//更新点
}
输出
void Graph::PrintGraph()
{
int i, j;
cout << " ";
for (i = 0; i < m_NumVertex; i++)
{
cout << m_VertexArr[i] << " ";//A B C D
}
cout << endl;
for (i = 0; i < m_NumVertex; i++)
{
cout << m_VertexArr[i] << " ";//竖行的ABCD
for (j = 0; j < m_NumVertex; j++)
{
cout << m_Edge[i][j] << " ";//输出边
}
cout << endl;
}
}
测试
int main()
{
Graph g;
cout << "插入ABCD顶点,在AB、AD、BC、BD、CD间插入边\n";
g.InsertVertex('A');
g.InsertVertex('B');
g.InsertVertex('C');
g.InsertVertex('D');
g.InsertEdge('A', 'B');
g.InsertEdge('A', 'D');
g.InsertEdge('B', 'C');
g.InsertEdge('B', 'D');
g.InsertEdge('C', 'D');
g.PrintGraph();
cout << "删除AD间的边\n";
g.DeleteEdge('A', 'D');
g.PrintGraph();
cout << "删除点B\n";
g.DeleteVertex('B');
g.PrintGraph();
}
网的邻接矩阵中无边的为正无穷符号,可以用INT_MAX表示,其他的边数组的值为权值