第72课 - 图的存储结构(上)

1、基本思想 

        -用一维数组存储顶点:描述顶点相关的数据 

        -用二维数组存储边:描述顶点间的关系和权


2、邻接矩阵法 

            -设图A = (V, E)是一个有n个顶点的图,图的邻接矩阵为 

                Edge[n] [n] , 则: 

                    

                    注:

                            解决工程问题时,习惯于对图中的每个顶点进行编号 

                            当不需要权值时,取W非空表示结点间有连接 



        邻接矩阵法示例一 

            -无向图的邻接矩阵是对称的 

       

        邻接矩阵法示例二 

             - 有向图的邻接矩阵可能是不对称的

                


3、设计与实现 

                        

                    问题: 

                            如何具体表示顶点集数组? 

                            如何具体表示边集数组?


4、实现方式一

         - 直接使用数组表示顶点集和边集 (不推荐)

                       

        分析下面代码的效率

                    

        问题 

            -构造效率低下 

                    图对象初始化时,频繁调用顶点类型和边类型的构造函数 

            -空间使用率低下 

                    图对象占用大量空间,而大多数空间处于闲置状态 

            -无法表示空值 

                    无法用统一的方式表示边为空的情形 


5、实现方式二

         - 使用指针数组表示顶点集和边集 

                    

        问题的解决 

            -构造效率 

                    初始化图对象时,只需要将数组元素赋值为空 

            -空间使用率 

                    顶点数据元素和边数据元素在需要时动态创建 

            -空值的表示 

                    任意的顶点类型和边类型都使用NULL表示空值


6、编程实验 

图的邻接矩阵结构     class MatrixGraph;

#ifndef MATRIXGRAPH_H
#define MATRIXGRAPH_H

#include "Graph.h"
#include "Exception.h"
#include "DynamicArray.h"

namespace  DTLib
{

template < int N, typename V, typename E >  //N : 图中顶点的个数   V : 与顶点关联数据元素类型     //E : 权值类型
class MatrixGraph : public Graph<V,E>
{
protected:
    V* m_vertexes[N];   //每一个成员指向与顶点相关联数据元素
    E* m_edges[N][N];      //邻接矩阵
    int m_eCount;   //当前图的边数
public:
    MatrixGraph()  //当前图没有任何顶点与数据元素相关联
    {
        for(int i=0;i<vCount();i++)
        {
            m_vertexes[i] = NULL;

            for(int j=0;j<vCount();j++)
            {
                m_edges[i][j] = NULL;
            }
        }

        m_eCount = 0;
    }

    V getVertex(int i)  //获取顶点i相关联数据元素值
    {
        V ret;

        if( !getVertex(i,ret) )
        {
            THROW_EXCEPTION(InvalidParameterException,"Index i is invalid ...");
        }

        return ret;
    }

    bool getVertex(int i,V& value)
    {
        bool ret = (0 <= i) &&  (i < vCount());

        if(ret)
        {
            if(m_vertexes[i] != NULL)
            {
                value = *(m_vertexes[i]);
            }
            else
            {
                THROW_EXCEPTION(InvalidOperationException,"No value assigned to this vertex ...");
            }
        }

        return ret;
    }

    bool setVertex(int i,const V& value)
    {
        bool ret = (0 <= i) && (i < vCount());

        if( ret )
        {
            V* data = m_vertexes[i];    //保证图对象的内部状态不改变

            if(data == NULL)
            {
                data = new V();
            }

            if(data != NULL)
            {
                *data = value;

                m_vertexes[i] = data;
            }
            else
            {
                THROW_EXCEPTION(NoEnoughMemoryException,"No memory to store new vertex value ...");
            }

        }

        return ret;
    }

    SharedPointer< Array<int> > getAdjacent(int i)//数组存的为与i邻接的顶点编号
    {
        DynamicArray<int>* ret = NULL;

        if( (0 <= i) && (i < vCount()) )
        {
            int n = 0;

            for(int j=0;j<vCount();j++)
            {
                if(m_edges[i][j] != NULL)   //对于i的邻接顶点,可以遍历i行所有元素,注意i和j的含义
                {
                    n++;
                }
            }

            ret = new DynamicArray<int>(n);

            if(ret != NULL)
            {
                for(int j=0,k=0;j<vCount();j++)
                {
                    if(m_edges[i][j] != NULL)
                    {
                        ret->set(k++,j);    //就为邻接顶点的编号
                    }
                }
            }
            else
            {
                THROW_EXCEPTION(NoEnoughMemoryException,"No memory to create ret object ...");
            }
        }
        else
        {
            THROW_EXCEPTION(InvalidParameterException,"Index i is invalid ...");
        }

        return ret;

    }

    E getEdge(int i,int j)  //获取顶点i,j间边权值
    {
        E ret;

        if( !getEdge(i,j,ret) )
        {
            THROW_EXCEPTION(InvalidParameterException,"Index <i,j> is invalid ...");
        }

        return ret;
    }

    bool getEdge(int i,int j,E& value)
    {
        bool ret = (0 <= i)&&(i < vCount())&&
                   (0 <= j)&&(j < vCount());

        if( ret )
        {
            if(m_edges[i][j] != NULL)   //有权值
            {
                    value = *(m_edges[i][j]);
            }
            else
            {
                THROW_EXCEPTION(InvalidOperationException,"No value assigned to this edge ...");
            }
        }

         return ret;
    }

    bool setEdge(int i,int j,const E& value)
    {
        bool ret = (0 <= i)&&(i < vCount())&&
                   (0 <= j)&&(j < vCount());

        if( ret )
        {
            E* ne = m_edges[i][j];

            if(m_edges[i][j] == NULL)   
            {
                ne = new E();

                if(ne)
                {
                    *ne = value;
                    m_edges[i][j] = ne;
                    m_eCount++;
                }
                else
                {
                    THROW_EXCEPTION(NoEnoughMemoryException,"No memory store new edge ...");
                }

            }
            else
            {
                *ne = value;
            }
        }

        return ret;
    }

    bool removeEdge(int i,int j)
    {
        bool ret = (0 <= i)&&(i < vCount())&&
                   (0 <= j)&&(j < vCount());

        if( ret )
        {
             E* toDel = m_edges[i][j];

             m_edges[i][j] = NULL;

             if(toDel != NULL)
             {
                 m_eCount--;

                 delete toDel;
             }
        }

        return ret;
    }

    int vCount()
    {
        return N;
    }

    int eCount()
    {
        return m_eCount;
    }

    int OD(int i)//i行不为空的数据元素即为出度
    {
        int ret = 0;

        if( (0 <= i) && (i < vCount()) )
        {
            for(int j=0;j<vCount();j++)
            {
                if(m_edges[i][j] != NULL)
                {
                    ret++;
                }
            }
        }
        else
        {
            THROW_EXCEPTION(InvalidParameterException,"Index i is invalid ...");
        }

        return ret;
    }

    int ID(int i)  //i列数据元素不为0即为入度
    {
        int ret = 0;

        if( (0 <= i) && (i < vCount()) )
        {
            for(int j=0;j<vCount();j++)
            {
                if(m_edges[j][i] != NULL)
                {
                    ret++;
                }
            }
        }
        else
        {
            THROW_EXCEPTION(InvalidParameterException,"Index i is invalid ...");
        }

        return ret;
    }

    ~MatrixGraph()//将每条边权值释放,与顶点关联数据元素释放
    {
        for(int i=0;i<vCount();i++)
        {

            for(int j=0;j<vCount();j++)
            {
                delete m_edges[i][j];
            }

            delete m_vertexes[i];
        }
    }


};
}


#endif // MATRIXGRAPH_H

main.cpp

#include <iostream>
#include "MatrixGraph.h"

using namespace std;
using namespace DTLib;

int main()
{
    MatrixGraph<3,int,int> g;

    g.setEdge(0,1,1);
    g.setEdge(1,0,2);
    g.setEdge(1,2,3);

    cout << "vCount : " << g.vCount() << endl;
    cout << "eCount : " << g.eCount() << endl;
    cout << "ID(1) : " << g.ID(1) << endl;
    cout << "OD(1) : " << g.OD(1) << endl;
    cout << "TD(1) : " << g.TD(1) << endl;

    cout << "W(0,1) : " << g.getEdge(0,1) << endl;
    cout << "W(1,0) : " << g.getEdge(1,0) << endl;
    cout << "W(1,2) : " << g.getEdge(1,2) << endl;

    SharedPointer< Array<int> > aj = g.getAdjacent(1);

    for(int i=0;i<aj->length();i++)
    {
        cout << (*aj)[i] << " ";
    }

    cout << "Delete Edge : " <<endl;

    g.removeEdge(0,1);

    cout << "eCount : " << g.eCount() << endl;

    g.setVertex(0,100);

    cout << "V(0) : " << g.getVertex(0) <<endl;

    cout << "W(0,1) : " << g.getEdge(0,1) << endl;

    return 0;
}


    



7、小结 

            邻接矩阵法使用数组对图相关的数据进行存储 

            一维数组存储顶点相关的数据(空表示无相关数据) 

            二维数组存储边相关的数据(空表示顶点间无连接) 

            代码实现时 使用指针数组进行数据的存储(提高效率)



猜你喜欢

转载自blog.csdn.net/qq_39654127/article/details/80546177