图的储存
12.1 基本概念 图(Graph)是数据结构中的最后一个“堡垒”——攻下它,数据结构就结束了。但就像在打游戏的最终BOSS一样,BOSS肯定是最强的,图也一样,它比线性表和树都更为复杂。在线性表中,数据元素间仅有线性关系,每个数据元素只有一个直接前驱和一个直接后继,也就是“一对一”的关系;在树形结构中,数据元素之间有着比较明显的层次关系,并且每一层上的数据元素可能和下一层中多个元素(儿子)相关,但只能和上一层中的一个元素(父亲)相关,也就是它是“一对多”的关系。到了图形结构中,数据元素之间的关系就可以是任意的,图中任意两个数据元素之间都可能相关,即“多对多”的关系。因此,图在200多年的发展中,应用极其广泛。这也造成了大部分高级的算法分析都不可避免地要用到图的知识。
不管图形结构有多复杂,我们要做的第一步必定是要先把它用某种结构储存起来。关于这一点,我们在树里面已经有了体会——对树的学习,关键是学习如何建树以及排序。我们要透过现象看本质,别看书里唧歪了半天,列了好多种储存图的方法,但其核心其实只有一个,那就是邻接矩阵(Adjacency Matrix)。但邻接矩阵的缺点是它对空间的耗费比较大,因为它是用一个二维数组来储存图的顶点和边的信息的——如果有N个顶点,则需要有N ^ 2的空间来储存。因此,如果图是稀疏的,那么我们就可以用邻接表(Adjacency List)来储存它,充分发挥链表的动态规划空间的优点。
下面我就分别给出这两种结构的代码实现。其中,邻接表使用了前面所写的单链表的类,因此在具体的实现上并不会太困难。另外,由于图结构本身比较复杂的原因,我无法把基类写得十分具有通用性,但它们应该已经可以基本满足后面的学习的需要了。
12.2 邻接矩阵
///
//
// FileName : MatrixGraph.h
// Version : 0.10
// Author : Luo Cong
// Date : 2005-1-27 0:01:12
// Comment :
//
///
#ifndef MATRIX_GRAPH_H #define MATRIX_GRAPH_H
#include using namespace std;
#include <assert.h> #include <crtdbg.h>
#ifdef _DEBUG #define DEBUG_NEW new (_NORMAL_BLOCK, THIS_FILE, LINE) #endif
#ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = FILE; #endif
#ifdef _DEBUG #ifndef ASSERT #define ASSERT assert #endif #else // not _DEBUG #ifndef ASSERT #define ASSERT #endif #endif // _DEBUG
template<typename T_Vertex, typename T_Edge> class CMatrixGraph { friend ostream& operator<<(ostream &os, CMatrixGraph<T_Vertex, T_Edge> &g);
private: int m_nVertexNum; int m_nEdgeNum; int m_nMaxVertexNum; T_Vertex *m_Vertex; T_Edge **m_Edge; T_Vertex m_NoVertex; T_Edge m_NoEdge;
public: CMatrixGraph(const int nMaxVertexNum); ~CMatrixGraph();
public: int GetVertexNum() const; int GetEdgeNum() const; T_Vertex& GetVertexAt(const int n); T_Vertex GetVertexAt(const int n) const; T_Edge& GetEdgeAt(const int nVertexIndex, const int n); T_Edge GetEdgeAt(const int nVertexIndex, const int n) const; int Find(const T_Vertex &v, int *nIndex = NULL) const; int InsertVertex(const T_Vertex &v); int InsertEdge(const T_Vertex &v1, const T_Vertex &v2, const T_Edge &e); int GetFirstAdjVertexIndex(const int n) const; int GetNextAdjVertexIndex(const int n, const int nn) const; };
template<typename T_Vertex, typename T_Edge> inline CMatrixGraph<T_Vertex, T_Edge>::CMatrixGraph(const int nMaxVertexNum) : m_nVertexNum(0), m_nEdgeNum(0), m_nMaxVertexNum(nMaxVertexNum), m_NoVertex(0), // this can be customized m_NoEdge(0) // this can be customized { int i;
m_Edge = new T_Edge*[nMaxVertexNum];
if (NULL == m_Edge)
return;
for (i = 0; i < nMaxVertexNum; ++i)
{
m_Edge[i] = new T_Edge[nMaxVertexNum];
}
m_Vertex = new T_Vertex[nMaxVertexNum];
}
template<typename T_Vertex, typename T_Edge> inline CMatrixGraph<T_Vertex, T_Edge>::~CMatrixGraph() { int i;
delete[] m_Vertex;
for (i = 0; i < m_nMaxVertexNum; ++i)
{
delete[] m_Edge[i];
}
delete[] m_Edge;
}
template<typename T_Vertex, typename T_Edge> inline int CMatrixGraph<T_Vertex, T_Edge>::Find( const T_Vertex &v, int *nIndex ) const { int i; int nVertexNum = m_nVertexNum;
for (i = 0; i < nVertexNum; ++i)
{
if (v == m_Vertex[i])
{
if (nIndex)
*nIndex = i;
return 1;
}
}
return 0;
}
template<typename T_Vertex, typename T_Edge> inline int CMatrixGraph<T_Vertex, T_Edge>::InsertVertex(const T_Vertex &v) { int i;
if ((m_nVertexNum >= m_nMaxVertexNum) || Find(v))
return 0;
m_Vertex[m_nVertexNum] = v;
for (i = 0; i < m_nMaxVertexNum; ++i)
m_Edge[m_nVertexNum][i] = m_NoEdge;
++m_nVertexNum;
return 1;
}
template<typename T_Vertex, typename T_Edge> inline int CMatrixGraph<T_Vertex, T_Edge>::InsertEdge( const T_Vertex &v1, const T_Vertex &v2, const T_Edge &e ) { int nIndexV1; int nIndexV2;
if (
(v1 == v2) ||
(!Find(v1, &nIndexV1)) ||
(!Find(v2, &nIndexV2)) ||
(m_Edge[nIndexV1][nIndexV2] != m_NoEdge)
)
return 0;
m_Edge[nIndexV1][nIndexV2] = e;
++m_nEdgeNum;
return 1;
}
template<typename T_Vertex, typename T_Edge> inline T_Edge& CMatrixGraph<T_Vertex, T_Edge>::GetEdgeAt( const int nVertexIndex, const int n ) { if ((0 > nVertexIndex) || (nVertexIndex >= m_nMaxVertexNum)) return m_NoEdge;
if ((0 > n) || (n >= m_nMaxVertexNum))
return m_NoEdge;
return *(&m_Edge[nVertexIndex][n]);
}
template<typename T_Vertex, typename T_Edge> inline T_Edge CMatrixGraph<T_Vertex, T_Edge>::GetEdgeAt( const int nVertexIndex, const int n ) const { if ((0 > nVertexIndex) || (nVertexIndex >= m_nMaxVertexNum)) return m_NoEdge;
if ((0 > n) || (n >= m_nMaxVertexNum))
return m_NoEdge;
return m_Edge[nVertexIndex][n];
}
template<typename T_Vertex, typename T_Edge> inline T_Vertex& CMatrixGraph<T_Vertex, T_Edge>::GetVertexAt(const int n) { if ((0 > n) || (n >= m_nMaxVertexNum)) return m_NoVertex; else return *(&m_Vertex[n]); }
template<typename T_Vertex, typename T_Edge> inline T_Vertex CMatrixGraph<T_Vertex, T_Edge>::GetVertexAt(const int n) const { if ((0 > n) || (n >= m_nMaxVertexNum)) return m_NoVertex; else return m_Vertex[n]; }
template<typename T_Vertex, typename T_Edge> inline int CMatrixGraph<T_Vertex, T_Edge>::GetVertexNum() const { return m_nVertexNum; }
template<typename T_Vertex, typename T_Edge> inline int CMatrixGraph<T_Vertex, T_Edge>::GetEdgeNum() const { return m_nEdgeNum; }
template<typename T_Vertex, typename T_Edge> inline int CMatrixGraph<T_Vertex, T_Edge>::GetFirstAdjVertexIndex( const int n ) const { int i;
for (i = 0; i < m_nVertexNum; ++i)
{
if (m_Edge[n][i] != m_NoEdge)
return i;
}
return -1;
}
template<typename T_Vertex, typename T_Edge> inline int CMatrixGraph<T_Vertex, T_Edge>::GetNextAdjVertexIndex( const int n, const int nn ) const { int i;
for (i = nn + 1; i < m_nVertexNum; ++i)
{
if (m_Edge[n][i] != m_NoEdge)
return i;
}
return -1;
}
template<typename T_Vertex, typename T_Edge> inline ostream &operator<<(ostream &os, CMatrixGraph<T_Vertex, T_Edge> &g) { int i; int j; int nVertexNum;
nVertexNum = g.GetVertexNum();
for (i = 0; i < nVertexNum; ++i)
{
for (j = 0; j < nVertexNum; ++j)
{
os << g.GetEdgeAt(i, j) << ' ';
}
os << endl;
}
return os;
}
#endif // MATRIX_GRAPH_H 测试代码:
///
//
// FileName : MatrixGraph.cpp
// Version : 0.10
// Author : Luo Cong
// Date : 2005-1-27 0:02:03
// Comment :
//
///
#include "MatrixGraph.h"
int main() { CMatrixGraph<int, int> mgraph(4);
#ifdef _DEBUG _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); #endif
// (1)-->(2)
// |↖
// | ﹨
// ↓ ﹨
// (3)-->(4)
mgraph.InsertVertex(1);
mgraph.InsertVertex(2);
mgraph.InsertVertex(3);
mgraph.InsertVertex(4);
mgraph.InsertEdge(1, 2, 1);
mgraph.InsertEdge(1, 3, 1);
mgraph.InsertEdge(3, 4, 1);
mgraph.InsertEdge(4, 1, 1);
cout << mgraph << endl;
}
12.3 邻接链表
///
//
// FileName : ListGraph.h
// Version : 0.10
// Author : Luo Cong
// Date : 2005-1-27 10:26:20
// Comment :
//
///
#ifndef LIST_GRAPH_H #define LIST_GRAPH_H
#include using namespace std;
#include "../../slist/src/slist.h"
template<typename T_Vertex, typename T_Edge> class CListGraph { friend ostream& operator<<(ostream &os, CListGraph<T_Vertex, T_Edge> &g);
private: typedef struct tagLGEdge { int nextvertexindex; T_Edge edata; } LGEdge;
typedef struct tagLGVertex
{
T_Vertex vdata;
CSList<LGEdge> *edgelist;
} LGVertex;
int m_nVertexNum;
int m_nEdgeNum;
CSList<LGVertex> m_Vertex;
public: CListGraph(); ~CListGraph(); void Output(ostream &os) const;
private: int GetVertexAt(const int n, LGVertex *vertex) const; int GetEdgeAt(const int nVertexIndex, const int n, LGEdge *edge) const;
public: int GetVertexNum() const; int GetEdgeNum() const; int GetVertexAt(const int n, T_Vertex *v) const; int GetEdgeAt(const int nVertexIndex, const int n, T_Edge *e) const; T_Edge GetEdgeAt(const int nVertexIndex, const int n) const; int Find(const T_Vertex &v, int *nIndex = NULL) const; int InsertVertex(const T_Vertex &v); int InsertEdge(const T_Vertex &v1, const T_Vertex &v2, const T_Edge &e); int GetFirstAdjVertexIndex(const int n) const; int GetNextAdjVertexIndex(const int n, const int nn) const; };
template<typename T_Vertex, typename T_Edge> inline CListGraph<T_Vertex, T_Edge>::CListGraph() : m_nVertexNum(0), m_nEdgeNum(0) { }
template<typename T_Vertex, typename T_Edge> inline CListGraph<T_Vertex, T_Edge>::~CListGraph() { int i; int nVertexNum = m_nVertexNum; CSList *edgelist;
for (i = 0; i < nVertexNum; ++i)
{
edgelist = m_Vertex.GetAt(i + 1).edgelist;
if (edgelist)
{
edgelist->RemoveAll();
delete edgelist;
}
}
}
template<typename T_Vertex, typename T_Edge> inline int CListGraph<T_Vertex, T_Edge>::GetVertexNum() const { return m_nVertexNum; }
template<typename T_Vertex, typename T_Edge> inline int CListGraph<T_Vertex, T_Edge>::GetEdgeNum() const { return m_nEdgeNum; }
template<typename T_Vertex, typename T_Edge> inline int CListGraph<T_Vertex, T_Edge>::GetVertexAt( const int n, LGVertex *vertex ) const { ASSERT(vertex);
if ((0 > n) || (n >= m_Vertex.GetCount()))
return 0;
*vertex = m_Vertex.GetAt(n + 1);
return 1;
}
template<typename T_Vertex, typename T_Edge> inline int CListGraph<T_Vertex, T_Edge>::GetVertexAt( const int n, T_Vertex *v ) const { ASSERT(v);
LGVertex vertex;
if (GetVertexAt(n, &vertex))
{
*v = vertex.vdata;
return 1;
}
else
return 0;
}
template<typename T_Vertex, typename T_Edge> inline int CListGraph<T_Vertex, T_Edge>::GetEdgeAt( const int nVertexIndex, const int n, LGEdge *edge ) const { ASSERT(edge);
LGVertex vertex;
int nVertexEdgelistCount;
if (0 == GetVertexAt(nVertexIndex, &vertex))
return 0;
if (vertex.edgelist)
nVertexEdgelistCount = vertex.edgelist->GetCount();
else
return 0;
if (
(0 > n) ||
(n >= nVertexEdgelistCount)
)
return 0;
*edge = vertex.edgelist->GetAt(n + 1);
return 1;
}
template<typename T_Vertex, typename T_Edge> inline int CListGraph<T_Vertex, T_Edge>::GetEdgeAt( const int nVertexIndex, const int n, T_Edge *e ) const { ASSERT(e);
LGEdge edge;
if (GetEdgeAt(nVertexIndex, n, &edge))
{
*e = edge.edata;
return 1;
}
else
return 0;
}
template<typename T_Vertex, typename T_Edge> inline int CListGraph<T_Vertex, T_Edge>::Find( const T_Vertex &v, int *nIndex ) const { int i; int nVertexNum = m_nVertexNum; LGVertex vertex;
for (i = 0; i < nVertexNum; ++i)
{
vertex = m_Vertex.GetAt(i + 1);
if (v == vertex.vdata)
{
if (nIndex)
*nIndex = i;
return 1;
}
}
return 0;
}
template<typename T_Vertex, typename T_Edge> inline int CListGraph<T_Vertex, T_Edge>::InsertVertex(const T_Vertex &v) { LGVertex vertex;
if (Find(v))
return 0;
vertex.vdata = v;
vertex.edgelist = NULL;
m_Vertex.AddTail(vertex);
++m_nVertexNum;
return 1;
}
template<typename T_Vertex, typename T_Edge> inline int CListGraph<T_Vertex, T_Edge>::InsertEdge( const T_Vertex &v1, const T_Vertex &v2, const T_Edge &e ) { int i; int nIndexV1; int nIndexV2; LGEdge edge; CSList *edgelist; int nVertexEdgelistCount;
if (
(v1 == v2) ||
(!Find(v1, &nIndexV1)) ||
(!Find(v2, &nIndexV2))
)
return 0;
// if there's no edges, let's create it first
edgelist = m_Vertex.GetAt(nIndexV1 + 1).edgelist;
if (NULL == edgelist)
{
edgelist = new CSList<LGEdge>;
m_Vertex.GetAt(nIndexV1 + 1).edgelist = edgelist;
}
// is there an edge between v1 and v2 already?
nVertexEdgelistCount = edgelist->GetCount();
for (i = 0; i < nVertexEdgelistCount; ++i)
{
edge = edgelist->GetAt(i + 1);
if (
(edge.edata == e) &&
(edge.nextvertexindex == nIndexV2)
)
return 0;
}
// new edge's data
edge.edata = e;
edge.nextvertexindex = nIndexV2;
edgelist->AddTail(edge);
++m_nEdgeNum;
return 1;
}
template<typename T_Vertex, typename T_Edge> inline int CListGraph<T_Vertex, T_Edge>::GetFirstAdjVertexIndex( const int n ) const { LGVertex vertex;
if (0 == GetVertexAt(n, &vertex))
return -1;
if (vertex.edgelist)
return vertex.edgelist->GetHead().nextvertexindex;
return -1;
}
template<typename T_Vertex, typename T_Edge> inline int CListGraph<T_Vertex, T_Edge>::GetNextAdjVertexIndex( const int n, const int nn ) const { LGEdge edge; LGVertex vertex; int nVertexEdgelistCount;
if (0 == GetVertexAt(n, &vertex))
return -1;
if (vertex.edgelist)
nVertexEdgelistCount = vertex.edgelist->GetCount();
else
return -1;
if (
(0 > nn) ||
((nn + 1) >= nVertexEdgelistCount)
)
return -1;
edge = vertex.edgelist->GetAt((nn + 1) + 1);
return edge.nextvertexindex;
}
template<typename T_Vertex, typename T_Edge> inline void CListGraph<T_Vertex, T_Edge>::Output(ostream &os) const { int i; int j; LGEdge edge; LGVertex vertex; int nVertexNum; int nVertexEdgelistCount;
nVertexNum = GetVertexNum();
for (i = 0; i < nVertexNum; ++i)
{
if (0 == GetVertexAt(i, &vertex))
return ;
os << "(V" << i + 1 << ") ";
os << 'V' << vertex.vdata;
if (vertex.edgelist)
nVertexEdgelistCount = vertex.edgelist->GetCount();
else
nVertexEdgelistCount = 0;
for (j = 0; j < nVertexEdgelistCount; ++j)
{
os << " --> ";
edge = vertex.edgelist->GetAt(j + 1);
os << 'V' << edge.nextvertexindex + 1;
}
os << endl;
}
}
template<typename T_Vertex, typename T_Edge> inline ostream& operator<<(ostream &os, CListGraph<T_Vertex, T_Edge> &g) { g.Output(os); return os; }
#endif // LIST_GRAPH_H 测试代码:
///
//
// FileName : ListGraph.cpp
// Version : 0.10
// Author : Luo Cong
// Date : 2005-1-27 10:27:55
// Comment :
//
///
#include "ListGraph.h"
int main() { CListGraph<int, int> lgraph;
#ifdef _DEBUG _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); #endif
// (1)-->(2)
// |↖
// | ﹨
// ↓ ﹨
// (3)-->(4)
lgraph.InsertVertex(1);
lgraph.InsertVertex(2);
lgraph.InsertVertex(3);
lgraph.InsertVertex(4);
lgraph.InsertEdge(1, 2, 1);
lgraph.InsertEdge(1, 3, 1);
lgraph.InsertEdge(3, 4, 1);
lgraph.InsertEdge(4, 1, 1);
cout << lgraph << endl;
} ————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。