基本思想
- 用一维数组存储顶点:描述顶点相关的数据
- 用二维数组存储边:描述顶点间的关系和权
邻接矩阵法
设图 A = (V, E) 是一个有 n 个顶点的图,图的邻接矩阵为 Edgen, 则:
注:
解决工程问题时,习惯于对图中的每个顶点进行编号;
当需要权值时,取 W 非空表示结点间有连接。
无向图邻接矩阵法
- 无向图的邻接矩阵是对称的
有向图邻接矩阵法
- 有向图的邻接矩阵可能是不对称的
设计与实现
问题:如何具体表示顶点集数据?如何具体表示边集数组?
实现方式一
直接使用数组表示顶点集和边集
template <int N, typename V, typename E>
class MatrixGraph : public Graph<V, E>
{
protected:
V m_vertexes[N];
E m_edges[N][N];
int m_edges[N][N];
public:
// ...
};
效率分析
struct TV
{
int a1[100];
char a2[1000];
TV() {/* init array */}
};
struct TE
{
float a1[100];
long a2[1000];
TE() {/* init array */}
};
int main()
{
MatrixGraph<1000, TV, TE> g;
// ...
}
问题
构造效率低下
- 图对象构造时,频繁调用顶点类型和边类型的构造函数
空间使用率低下
- 图对象占用大量空间,而大多数空间处于闲置状态(如:含1000个顶点的图,只有少量几条边时)
无法表示空值
- 无法用统一的方式表示边为空的情形(例:当边相关类型为自定义类型时)
实现方式二
使用指针数组表示顶点集边集
template <int N, typename V, typename E>
class MatrixGraph : public Graph<V, E>
{
protected:
V *m_vertexes[N];
E *m_edges[N][N];
int m_eCount;
public:
// ...
};
问题的解决
构造效率
- 初始化图对象时,只需要将数组元素赋值为空
空间使用率
- 顶点数据元素和边数据元素在需要时动态创建
空值的表示
- 任意的顶点类型和边类型都使用 NULL 表示空值
编程实验: 图的邻接矩阵结构
文件:MatrixGraph.h
#ifndef MATRIXGRAPH_H
#define MATRIXGRAPH_H
#include "Graph.h"
#include "Exception.h"
#include "DynamicArray.h"
namespace DTLib
{
template <int N, typename V, typename E>
class MatrixGraph : public Graph<V, E>
{
public:
MatrixGraph() = default;
V getVertex(int i) const override
{
V ret;
if (!getVertex(i, ret))
{
THROW_EXCEPTION(InvalidParameterExcetion, "Parameter i is invalid ...");
}
return ret;
}
bool getVertex(int i, V &value) const override
{
bool ret = ((0 <= i) && (i < vCount()));
if (ret)
{
if (m_vertexes[i] != nullptr)
{
value = *(m_vertexes[i]);
}
else
{
THROW_EXCEPTION(InvalidOpertionExcetion, "No value assigned to this vertex ...");
}
}
return ret;
}
bool setVertex(int i, const V &value) override
{
bool ret = ((0 <= i) && (i < vCount()));
if (ret)
{
V *data = m_vertexes[i];
if (data == nullptr)
{
data = new V();
}
if (data != nullptr)
{
*data = value;
m_vertexes[i] = data; // 异常安全!!!
}
else
{
THROW_EXCEPTION(NoEnoughMemoryException, "No memory to store new vertex value ...");
}
}
return ret;
}
SharedPointer<Array<int>> getAdjacent(int i) const override
{
DynamicArray<int> *ret = nullptr;
if ((0 <= i) && (i < vCount()))
{
int n = 0;
for (int j=0; j<vCount(); ++j)
{
if (m_edges[i][j] != nullptr)
{
++n;
}
}
ret = new DynamicArray<int>(n);
if (ret != nullptr)
{
for (int j=0, k=0; j<vCount(); ++j)
{
if (m_edges[i][j] != nullptr)
{
ret->set(k++, j);
}
}
}
else
{
THROW_EXCEPTION(NoEnoughMemoryException, "No memory to create ret obj ...");
}
}
else
{
THROW_EXCEPTION(InvalidParameterExcetion, "Parameter i is invalid ...");
}
return ret;
}
E getEdge(int i, int j) const override
{
E ret;
if (!getEdge(i, j, ret))
{
THROW_EXCEPTION(InvalidParameterExcetion, "Parameter <i, j> is invalid ...");
}
return ret;
}
bool getEdge(int i, int j, E &value) const override
{
bool ret = ((0 <= i) && (i < vCount()) &&
(0 <= j) && (j < vCount()));
if (ret)
{
if (m_edges[i][j] != nullptr)
{
value = *(m_edges[i][j]);
}
else
{
THROW_EXCEPTION(InvalidOpertionExcetion, "No value assigned to this edge ...");
}
}
return ret;
}
bool setEdge(int i, int j, const E &value) override
{
bool ret = ((0 <= i) && (i < vCount()) &&
(0 <= j) && (j < vCount()));
if (ret)
{
E *ne = m_edges[i][j];
if (ne != nullptr)
{
*ne = value;
}
else
{
ne = new E();
if (ne != nullptr)
{
*ne = value;
m_edges[i][j] = ne; // 异常安全!!!
++m_eCount;
}
else
{
THROW_EXCEPTION(NoEnoughMemoryException, "No memory to store new edge value ...");
}
}
}
return ret;
}
bool removeEdge(int i, int j) override
{
bool ret = ((0 <= i) && (i < vCount()) &&
(0 <= j) && (j < vCount()));
if (ret)
{
E *todel = m_edges[i][j];
m_edges[i][j] = nullptr;
if (todel != nullptr)
{
--m_eCount;
delete todel;
}
}
return ret;
}
int vCount() const override
{
return N;
}
int eCount() const override
{
return m_eCount;
}
int OD(int i) const override
{
int ret = 0;
if ((0 <= i) && (i < vCount()))
{
for (int j=0; j<vCount(); ++j)
{
if (m_edges[i][j] != nullptr)
{
++ret;
}
}
}
else
{
THROW_EXCEPTION(InvalidParameterExcetion, "Parameter i is invald ...");
}
return ret;
}
int ID(int i) const override
{
int ret = 0;
if ((0 <= i) && (i < vCount()))
{
for (int j=0; j<vCount(); ++j)
{
if (m_edges[j][i] != nullptr)
{
++ret;
}
}
}
else
{
THROW_EXCEPTION(InvalidParameterExcetion, "Parameter i is invald ...");
}
return ret;
}
~MatrixGraph()
{
for (int i=0; i<vCount(); ++i)
{
for (int j=0; j<vCount(); ++j)
{
delete m_edges[i][j];
}
delete m_vertexes[i];
}
}
protected:
V *m_vertexes[N] = {0};
E *m_edges[N][N] = {0};
int m_eCount = 0;
MatrixGraph(const MatrixGraph &) = default;
MatrixGraph &operator = (const MatrixGraph &) = default;
};
}
#endif // MATRIXGRAPH_H
文件:main.cpp
#include <iostream>
#include "BTreeNode.h"
#include "MatrixGraph.h"
using namespace std;
using namespace DTLib;
int main()
{
MatrixGraph<3, int, int> g;
g.setEdge(0, 1, 1);
g.setEdge(1, 0, 2);
g.setEdge(1, 2, 3);
cout << "vCount : " << g.vCount() << endl;
cout << "eCount : " << g.eCount() << endl;
cout << "ID(1) : " << g.ID(1) << endl;
cout << "OD(1) : " << g.OD(1) << endl;
cout << "TD(1) : " << g.TD(1) << endl;
SharedPointer<Array<int>> aj = g.getAdjacent(1);
cout << "Adjacent(1) : " ;
for (int i=0; i<aj->length(); ++i)
{
cout << (*aj)[i] << " ";
}
cout << endl;
cout << "delete edge:" << endl;
g.removeEdge(0, 1);
cout << "eCount : " << g.eCount() << endl;
g.setVertex(0, 100);
cout << "V(0) : " << g.getVertex(0) << endl;
cout << "W(0, 1) : " << g.getEdge(0, 1) << endl;
return 0;
}
输出:
vCount : 3
eCount : 3
ID(1) : 1
OD(1) : 2
TD(1) : 3
Adjacent(1) : 0 2
delete edge:
eCount : 2
V(0) : 100
W(0, 1) : terminate called after throwing an instance of 'DTLib::InvalidOpertionExcetion'
小结
- 邻接矩阵法使用数组对图相关的数据进行存储
- 一维数组存储顶点相关的数据(空表示无相关数据)
- 二维数组存储边相关的数据(空表示结点间无连接)
- 代码实现时使用指针数组进行数据的存储(提高效率)
以上内容整理于狄泰软件学院系列课程,请大家保护原创!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。