图(Graph)是数据结构中的最后一个“堡垒”——攻下它,数据结构就结束了

21 阅读7分钟

图的储存

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 版权协议,转载请附上原文出处链接和本声明。
                    

原文链接:blog.csdn.net/qq_24700495…