邻接矩阵法中的残留问题
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:
// ...
};
- MatrixGraph 无法动态 添加 / 删除 顶点!!!
- 资源耗费大;N = 1000, 邻接矩阵的体积为 4 * 1000 * 1000 字节;因此,图对象创建时的体积约为 4MB !!!
基本思想
为了进一步提高空间使用率,可以考虑使用链表替代数组,将邻接矩阵换为邻接链表。
邻接链表法
- 图中的所有顶点按照编号存储于统一个链表中
- 每一个顶点对应一个链表,用于存储始发于该顶点的边
- 每一条边的信息包含:起点,终点,权值
邻接链表示例
设计与实现
边数据类型的设计
struct Edge : public Object
{
int b; // 起始顶点
int e; // 邻接顶点
E data; // 权值
// ...
};
顶点数据类型的设计
struct Vertex : public Object
{
V *data; // 顶点数据元素值
LinkList<Edge> edge; // 邻接于该顶点的边
// ...
}
动态增加/删除顶点
int addVertex();
- 增加新的顶点,返回顶点编号
int addVertex(const V &value);
- 增加新顶点的同时附加数据元素
void removeVertex();
- 删除最近增加的顶点
编程实验:图的邻接链表结构
文件:Graph.h
#ifndef GRAPH_H
#define GRAPH_H
#include "Object.h"
#include "SharedPointer.h"
#include "Array.h"
namespace DTLib
{
template <typename E>
struct Edge : public Object
{
int b;
int e;
E data;
Edge(int i=-1, int j=-1) : b(i), e(j)
{
}
Edge(int i, int j, const E &value) : b(i), e(j), data(value)
{
}
bool operator == (const Edge &obj)
{
return (b == obj.b) && (e == obj.e);
}
bool operator != (const Edge &obj)
{
return !(*this == obj);
}
};
template <typename V, typename E>
class Graph : public Object
{
public:
virtual V getVertex(int i) const = 0;
virtual bool getVertex(int i, V &value) const = 0;
virtual bool setVertex(int i, const V &value) = 0;
virtual SharedPointer<Array<int>> getAdjacent(int i) const = 0;
virtual E getEdge(int i, int j) const = 0;
virtual bool getEdge(int i, int j, E &value) const = 0;
virtual bool setEdge(int i, int j, const E &value) = 0;
virtual bool removeEdge(int i, int j) = 0;
virtual int vCount() const = 0;
virtual int eCount() = 0;
virtual int OD(int i) = 0;
virtual int ID(int i) = 0;
virtual int TD(int i)
{
return OD(i) + ID(i);
}
};
}
#endif // GRAPH_H
文件:ListGraph.h
#ifndef LISTGRAPH_H
#define LISTGRAPH_H
#include "Graph.h"
#include "Exception.h"
#include "LinkList.h"
#include "DynamicArray.h"
namespace DTLib
{
template <typename V, typename E>
class ListGraph : public Graph<V, E>
{
public:
ListGraph(unsigned int n = 0)
{
for (unsigned int i=0; i<n; ++i)
{
addVertex();
}
}
int addVertex()
{
int ret = -1;
Vertex *v = new Vertex();
if (v != nullptr)
{
m_list.insert(v);
ret = m_list.length() - 1;
}
else
{
THROW_EXCEPTION(NoEnoughMemoryException, "No memory to create vertex object ...");
}
return ret;
}
int addVertex(const V &value)
{
int ret = addVertex();
if (ret >= 0)
{
setVertex(ret, value);
}
return ret;
}
void removeVertex()
{
if (m_list.length() > 0)
{
int index = m_list.length() - 1;
Vertex *v = m_list.get(index);
if (m_list.remove(index))
{
for (int i=(m_list.move(0), 0); !m_list.end(); ++i, m_list.next())
{
int pos = m_list.current()->edge.find(Edge<E>(i, index));
if (pos >= 0)
{
m_list.current()->edge.remove(pos);
}
}
delete v->data;
delete v;
}
else
{
THROW_EXCEPTION(InvalidOpertionExcetion, "No vertex in current graph ...");
}
}
}
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)
{
Vertex *v = m_list.get(i);
if (v->data != nullptr)
{
value = *(v->data);
}
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)
{
Vertex *v = m_list.get(i);
V *data = v->data;
if (data == nullptr)
{
data = new V();
}
if (data != nullptr)
{
*data = value;
v->data = data; // 异常安全!!!
}
else
{
THROW_EXCEPTION(NoEnoughMemoryException, "No memory to create new vertex object ...");
}
}
return ret;
}
SharedPointer<Array<int>> getAdjacent(int i) const override
{
DynamicArray<int> *ret = nullptr;
if ((0 <= i) && (i < vCount()))
{
Vertex *v = m_list.get(i);
ret = new DynamicArray<int>(v->edge.length());
if (ret != nullptr)
{
for (int j=(v->edge.move(0), 0); !v->edge.end(); ++j, v->edge.next())
{
ret->set(j, v->edge.current().e);
}
}
else
{
THROW_EXCEPTION(NoEnoughMemoryException, "No memory to create ret object ...");
}
}
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)
{
Vertex *v = m_list.get(i);
int pos = v->edge.find(Edge<E>(i, j));
if (pos >= 0)
{
value = v->edge.get(pos).data;
}
else
{
THROW_EXCEPTION(InvalidOpertionExcetion, "No value assigned to this ret 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)
{
Vertex *v = m_list.get(i);
int pos = v->edge.find(Edge<E>(i, j));
if (pos >= 0)
{
ret = v->edge.set(pos, Edge<E>(i, j, value));
}
else
{
ret = v->edge.insert(0, Edge<E>(i, j, value));
}
}
return ret;
}
bool removeEdge(int i, int j) override
{
bool ret = ((0 <= i) && (i < vCount()))
&& ((0 <= j) && (j < vCount()));
if (ret)
{
Vertex *v = m_list.get(i);
int pos = v->edge.find(Edge<E>(i, j));
if (pos >= 0)
{
ret = v->edge.remove(pos);
}
}
return ret;
}
int vCount() const override
{
return m_list.length();
}
int eCount() override
{
int ret = 0;
for (m_list.move(0); !m_list.end(); m_list.next())
{
ret += m_list.current()->edge.length();
}
return ret;
}
int OD(int i) override
{
int ret = 0;
if ((0 <= i) && (i < vCount()))
{
ret = m_list.get(i)->edge.length();
}
else
{
THROW_EXCEPTION(InvalidParameterExcetion, "Parameter i is valid ...");
}
return ret;
}
int ID(int i) override
{
int ret = 0;
if ((0 <= i) && (i < vCount()))
{
for (m_list.move(0); !m_list.end(); m_list.next())
{
LinkList<Edge<E>> &edge = m_list.current()->edge;
for (edge.move(0); !edge.end(); edge.next())
{
if (edge.current().e == i)
{
++ret;
break;
}
}
}
}
else
{
THROW_EXCEPTION(InvalidParameterExcetion, "Parameter i is invalid ...");
}
return ret;
}
~ListGraph()
{
while (m_list.length() > 0)
{
Vertex *toDel = m_list.get(0);
m_list.remove(0);
delete toDel->data;
delete toDel;
}
}
protected:
struct Vertex : public Object
{
V *data = nullptr;
LinkList<Edge<E>> edge;
};
LinkList<Vertex*> m_list;
ListGraph(const ListGraph<V, E> &) = default;
ListGraph &operator = (const ListGraph<V, E> &) = default;
};
}
#endif // LISTGRAPH_H
文件:main.cpp
#include <iostream>
#include "ListGraph.h"
using namespace std;
using namespace DTLib;
int main()
{
ListGraph<char, int> g(4);
g.setVertex(0, 'A');
g.setVertex(1, 'B');
g.setVertex(2, 'C');
g.setVertex(3, 'D');
for (int i=0; i<g.vCount(); ++i)
{
cout << i << " : " << g.getVertex(i) << endl;
}
cout << "--------" << endl;
g.removeVertex();
g.removeVertex();
for (int i=0; i<g.vCount(); ++i)
{
cout << i << " : " << g.getVertex(i) << endl;
}
cout << "--------" << endl;
g.addVertex('C');
g.addVertex('D');
for (int i=0; i<g.vCount(); ++i)
{
cout << i << " : " << g.getVertex(i) << endl;
}
cout << "--------" << endl;
g.setEdge(0, 1, 5);
g.setEdge(0, 3, 6);
g.setEdge(1, 2, 8);
g.setEdge(2, 3, 2);
g.setEdge(3, 1, 9);
cout << "W(0, 1) : " << g.getEdge(0, 1) << endl;
cout << "W(0, 3) : " << g.getEdge(0, 3) << endl;
cout << "W(1, 2) : " << g.getEdge(1, 2) << endl;
cout << "W(2, 3) : " << g.getEdge(2, 3) << endl;
cout << "W(3, 1) : " << g.getEdge(3, 1) << endl;
cout << "--------" << endl;
cout << "vCount : " << g.vCount() << endl;
cout << "eCount : " << g.eCount() << endl;
cout << "--------" << endl;
SharedPointer<Array<int>> aj = g.getAdjacent(0);
for (int i=0; i<aj->length(); ++i)
{
cout << (*aj)[i] << " ";
}
cout << endl;
cout << "--------" << endl;
cout << "ID(1) : " << g.ID(1) << endl;
cout << "OD(1) : " << g.OD(1) << endl;
cout << "TD(1) : " << g.TD(1) << endl;
cout << "--------" << endl;
g.removeVertex();
cout << "vCount : " << g.vCount() << endl;
cout << "eCount : " << g.eCount() << endl;
return 0;
};
输出:
0 : A
1 : B
2 : C
3 : D
--------
0 : A
1 : B
--------
0 : A
1 : B
2 : C
3 : D
--------
W(0, 1) : 5
W(0, 3) : 6
W(1, 2) : 8
W(2, 3) : 2
W(3, 1) : 9
--------
vCount : 4
eCount : 5
--------
3 1
--------
ID(1) : 2
OD(1) : 1
TD(1) : 3
--------
vCount : 3
eCount : 2
小结
- 邻接链表法使用链表对图相关的数据进行存储
- 每一个顶点关联一个链表,用于存储相关的数据
- 所有顶点按照编号被组织在同一个链表中
- 邻接链表法实现的图能够支持动态添加/删除顶点
以上内容整理于狄泰软件学院系列课程,请大家保护原创!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。