邻接矩阵法中的残留问题
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:
    // ...
};
  1. MatrixGraph 无法动态 添加 / 删除 顶点!!!
  2. 资源耗费大;N = 1000, 邻接矩阵的体积为 4 * 1000 * 1000 字节;因此,图对象创建时的体积约为 4MB !!!
基本思想

为了进一步提高空间使用率,可以考虑使用链表替代数组,将邻接矩阵换为邻接链表。

邻接链表法

  • 图中的所有顶点按照编号存储于统一个链表中
  • 每一个顶点对应一个链表,用于存储始发于该顶点的边
  • 每一条边的信息包含:起点,终点,权值

image.png

邻接链表示例

image.png

设计与实现

image.png

边数据类型的设计

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

小结

  • 邻接链表法使用链表对图相关的数据进行存储
  • 每一个顶点关联一个链表,用于存储相关的数据
  • 所有顶点按照编号被组织在同一个链表中
  • 邻接链表法实现的图能够支持动态添加/删除顶点

以上内容整理于狄泰软件学院系列课程,请大家保护原创!


TianSong
734 声望138 粉丝

阿里山神木的种子在3000年前已经埋下,今天不过是看到当年注定的结果,为了未来的自己,今天就埋下一颗好种子吧