【數據結構】73_圖的鄰接鏈表法存儲結構

鄰接矩陣法中的殘留問題
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 !!!
基本思想

爲了進一步提升空間使用率,能夠考慮使用鏈表替代數組,將鄰接矩陣換爲鄰接鏈表。ios

鄰接鏈表法

  • 圖中的全部頂點按照編號存儲於統一個鏈表中
  • 每個頂點對應一個鏈表,用於存儲始發於該頂點的邊
  • 每一條邊的信息包含:起點,終點,權值

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.hide

#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.hthis

#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(NoEnoughMemoryException, "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.cppspa

#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

小結

  • 鄰接鏈表法使用鏈表對圖相關的數據進行存儲
  • 每個頂點關聯一個鏈表,用於存儲相關的數據
  • 全部頂點按照編號被組織在同一個鏈表中
  • 鄰接鏈表法實現的圖可以支持動態添加/刪除頂點

以上內容整理於狄泰軟件學院系列課程,請你們保護原創!3d

相關文章
相關標籤/搜索