• 图的两种存储方式---邻接矩阵和邻接表


       图:图是一种数据结构,由顶点的有穷非空集合和顶点之间边的集合组成,表示为G(V,E),V表示为顶点的集

    合,E表示为边的集合。

       首先肯定是要对图进行存储,然后进行一系列的操作,下面对图的两种存储方式邻接矩阵和邻接表尽行介绍。

       (一)、邻接矩阵存储:

       用两个数组分别进行存储数据元素(顶点)的信息和数据元素之间的关系(边或弧)的信息。

       存储顶点:用一个连续的空间存储n个顶点。

       存储顶点之间的边:将由n个顶点组成的边用一个n*n的矩阵来存储,如果两个顶点之间有边,则表示为1,否

    则表示为0。

       下面用代码来实现邻接矩阵的存储:

    #define SIZE 10
    
    class Graph
    {
    public:
        Graph()
        {
            MaxVertices = SIZE;
            NumVertices = NumEdges = 0;
            VerticesList = new char[sizeof(char)*MaxVertices];
            Edge = new int*[sizeof(int*)*MaxVertices];
            int i,j;
            for(i = 0;i<MaxVertices;i++)
                Edge[i] = new int[sizeof(int)*MaxVertices];
            for(i = 0;i<MaxVertices;i++)
            {
                for(j = 0;j<MaxVertices;++j)
                    Edge[i][j] = 0;
            }
        }
        void ShowGraph()
        {
            int i,j;
            cout<<"  ";
            for(i = 0;i<NumVertices;i++)
                cout<<VerticesList[i]<<" ";
            cout<<endl;
            for(i = 0;i<NumVertices;i++)
            {
                cout<<VerticesList[i]<<" ";
                for(j = 0;j<NumVertices;j++)
                    cout<<Edge[i][j] <<" ";
                cout<<endl;
            }
            cout<<endl;
        }
        int GetVertexPos(char v)
        {
            int i;
            for(i = 0;i<NumVertices;i++)
            {
                if(VerticesList[i] == v)
                    return i;
            }
            return -1;
        }
        ~Graph()
        {
            Destroy();
        }
        void Insert(char v)
        {
            if(NumVertices < MaxVertices)
            {
                VerticesList[NumVertices] = v;
                NumVertices++;   
            }
        }
        void InsertEdge(char v1,char v2)  
        {
            int i,j;
            int p1 = GetVertexPos(v1);  
            int p2 = GetVertexPos(v2);
            if(p1 == -1 || p2 == -1)
                return ;
            Edge[p1][p2] = Edge[p2][p1] = 1;
            NumEdges++;  
        }
        void RemoveEdge(char v1,char v2)
        {
            int p1 = GetVertexPos(v1);
            int p2 = GetVertexPos(v2);
            if(p1 == -1 || p2== -1)
                return;
            if(Edge[p1][p2] == 0) 
                return;
            Edge[p1][p2] = Edge[p2][p1] = 0;
            NumEdges--;
        }
        void Destroy()
        {
            delete[] VerticesList;
            VerticesList = NULL;
            for(int i = 0;i<NumVertices;i++)
            {
                delete Edge[i];
                Edge[i] = NULL;
            }
            delete[] Edge;
            Edge = NULL;
    
            MaxVertices = NumVertices = 0;
        }
        void RemoveVertex(char v)
        {
            int i,j;
            int p = GetVertexPos(v);
            int reNum = 0;
            if(p == -1)
                return;
            for(i = p;i<NumVertices-1;i++) 
            {
                VerticesList[i] = VerticesList[i+1];
            }
            
            for(i = 0;i<NumVertices;i++)
            {
                if(Edge[p][i] != 0)
                    reNum++;
            }
            for(i = p;i<NumVertices-1;i++)
            {
                for(j = 0;j<NumVertices;j++)
                {
                    Edge[i][j] = Edge[i+1][j];
                }
            }
            for(i = p;i<NumVertices;i++)
            {
                for(j = 0;j<NumVertices;j++)
                    Edge[j][i] = Edge[j][i+1];
            }
    
            NumVertices--;
            NumEdges = NumEdges - reNum;
        }
    private:
        int MaxVertices;
        int NumVertices;
        int NumEdges;
        char *VerticesList;
        int **Edge;
    };

        上面的类中的数据有定义最大的顶点的个数(MaxVertices),当前顶点的个数(NumVertices),当前边的个数

    (NumEdges),保存顶点的数组,保存边的数组。其中的方法是(1)构造函数:对定义的数据进行初始化。(2)显

    示构造的图的信息的函数。(3)得到顶点位置信息的函数。(4)析构函数:调用销毁函数。(5)插入顶点函数。

    (6)插入边的函数。(7)删除边的函数。(8)销毁函数,释放开辟的内存空间。(9)删除顶点函数。

      (二)、邻接表存储:

       邻接表是图的一种链式存储结构。用数组存储顶点,用链表存储和顶点相关联的边,边值为当前顶点在数组中的下

    标。用如下图可以表示邻接表的存储方式:

                     

                      

       下面用代码来实现邻接表的存储:

    #define SIZE 10
    typedef char T;
    struct Edge
    {
        Edge(int v):destvalue(v),link(NULL){}
        int destvalue;
        Edge *link;
    };
    struct Vertex
    {
        Vertex():list(NULL){}
        T data;
        Edge *list;
    };
    class GraphLnk
    {
    public:
        GraphLnk();
        ~GraphLnk(){}
    
        void InsertVertex(T v);
        void InsertEdge(T v1,T v2);
        int GetVertexI(T v);
        void ShowGraph();
        void RemoveEdge(T v1,T v2);
        void RemoveVertex(T v);
        void DestroyGraph();
    private:
        int MaxVertex;
        int NumVertex;
        int NumEdge;
    
        Vertex *VertexTable;
    };
    
    GraphLnk::GraphLnk()
    {
        MaxVertex = SIZE;
        NumVertex = NumEdge = 0;
        VertexTable = new Vertex[MaxVertex];
    }
    void GraphLnk::InsertVertex(T v)
    {
        if(NumVertex >= MaxVertex)
            return;
        VertexTable[NumVertex++].data = v;
    }
    void GraphLnk::ShowGraph()
    {
        int i;
        for(i = 0;i<NumVertex;i++)
        {
            cout<<i<<" "<<VertexTable[i].data<<":->";
            Edge *p = VertexTable[i].list;
            while(p != NULL)
            {
                cout<<p->destvalue<<"->";
                p = p->link;
            }
            cout<<"nul"<<endl;
        }
        cout<<endl;
    }
    int GraphLnk::GetVertexI(T v)
    {
        int i;
        for(i = 0;i<NumVertex;i++)
        {
            if(VertexTable[i].data == v)
                return i;
        }
        return -1;
    }
    void GraphLnk::InsertEdge(T v1,T v2)
    {
        int p1 = GetVertexI(v1);
        int p2 = GetVertexI(v2);
        if(p1 == -1 || p2 == -1)
            return;
        //v1 -> v2
        Edge *ed = new Edge(p2);
        ed->link = VertexTable[p1].list;
        VertexTable[p1].list = ed;
        //v2 -> v1
        ed = new Edge(p1);
        ed->link = VertexTable[p2].list;
        VertexTable[p2].list = ed;
    
        NumEdge++;
    }
    
    void GraphLnk::RemoveEdge(T v1,T v2)
    {
        int p1 = GetVertexI(v1);
        int p2 = GetVertexI(v2);
        if(p1 == -1 || p2 == -1)
            return;
        Edge *q = NULL;              //标识要删除边链表的前一个
        Edge *p;
    
        p = VertexTable[p1].list;    //从链表开头开始
        //查找v2所在位置
        while(p != NULL && p->destvalue != p2)
        {
            q = p;
            p = p->link;
        }
        if(p == NULL)          //两个顶点之间没有边的存在¨
            return;
            //找到所要删除的边
            if(q == NULL)      //所要删除的为链表开头
            {
                VertexTable[p1].list = p->link;
            }
            else
            {
                q->link = p->link;
            }
            delete p;
            p = NULL;
     
            q = NULL;
            p = VertexTable[p2].list;
            while(p->destvalue != p1)
            {
                q = p;
                p = p->link;
            }
            if(q == NULL)
            {
                VertexTable[p2].list = p->link;
            }
            else
            {
                q->link = p->link;
            }
            delete p;
            p = NULL;
            NumEdge--;
    }
    
    void GraphLnk::RemoveVertex(T vertex)
    {
        int v  = GetVertexI(vertex);
        if(v == -1)
            return;
        //删除和结点相关的边
        Edge*p=VertexTable[v].list;
    
        Edge *s;
        Edge *t = NULL;
        int k;
        while(p != NULL)
        {
            k = p->destvalue;
            s = VertexTable[k].list;
            while(s != NULL&&s->destvalue != v)
            {
                t = s;
                s = s->link;
            }
            if(s != NULL)
            {
                if(t == NULL)     //第一个结点
                {
                    VertexTable[k].list = s->link;
                }
                else
                {
                    t->link = s->link;
                }
                delete s;
                s = NULL;
            }
            
            VertexTable[v].list = p->link;
    
            delete p;
            p = VertexTable[v].list;
        }
        /*删除结点,用最后一个结点覆盖要删除的结点,并把和
        最后一个结点相关联的边的下标改正
        */
        NumVertex--;
        VertexTable[v].data = VertexTable[NumVertex].data; 
        VertexTable[v].list = VertexTable[NumVertex].list;
    
        s = VertexTable[v].list;
        while(s != NULL)
        {
            k = s->destvalue;
            p = VertexTable[k].list;
            while(p != NULL)
            {
                if(p->destvalue == NumVertex)   
                {
                    p->destvalue = v;
                    break;
                }
                p = p->link;
            }
            s = s->link;
        }
    }
    
    void GraphLnk::DestroyGraph()
    {
        Edge *p;
        for(int i = 0;i<NumVertex;i++)
        {
            p = VertexTable[i].list;
            while(p != NULL)
            {
                VertexTable[i].list = p->link;
                delete p;
                p = VertexTable[i].list;
            }
        }
        delete []VertexTable;
        MaxVertex = NumEdge = NumVertex = 0;
    }

       在代码实现的过程中,先定义出顶点和边的数据结构,然后在类中定义了最大顶点数,当前顶点的个数,当前的

    边的个数,顶点类型的数组,还有在类中进行了函数的声明,包括(1)构造函数。(2)析构函数。(3)插入顶点

    函数。(4)插入边函数。(5)得到顶点位置的函数。(6)显示构造出的图的信息函数。(7)删除边函数。(8)

    删除顶点函数。(9)销毁函数。

  • 相关阅读:
    ctfhub技能树—文件上传—双写后缀
    ctfhub技能树—文件上传—00截断
    ctfhub技能树—文件上传—文件头检查
    ctfhub技能树—文件上传—MIME绕过
    ctfhub技能树—文件上传—.htaccess
    ctfhub技能树—文件上传—前端验证
    ctfhub技能树—文件上传—无验证
    ctfhub技能树—sql注入—Refer注入
    ctfhub技能树—sql注入—UA注入
    面试中被问到的知识点
  • 原文地址:https://www.cnblogs.com/XNQC1314/p/9076698.html
Copyright © 2020-2023  润新知