图(基于十字链表)实现

1.Gragh抽象类 (gragh.h)

 

#ifndef _GRAGH_H

#define _GRAGH_H

 

template <typename E>    //E为标号的类型

class Graph{

public:

    Graph() {}           //构造函数

    virtual ~Graph() {}  //析构

 

    //返回顶点和边的数量

    virtual int n() = 0;  //顶点

    virtual int e() = 0;  //

 

    //返回v的第一个相邻顶点的下标

    virtual int firstOut(int v) = 0;  //出边

    virtual int firstIn(int v) = 0;  //入边

 

    //返回v的下一个相邻顶点的下标

    virtual int nextOut(int v) = 0;

    virtual int nextIn(int v) = 0;

 

    //查找,设置,获取顶点的标号

    virtual int locateVex(E u) =0; //查找标号

    virtual E getVex(int v)=0;

    virtual void putVex(int v,E value) =0;

 

    //建立,删除边,获取边的权值

    virtual void setEdge(int v1,int v2,int wght) = 0;

    virtual void delEdge(int v1,int v2) = 0;

    virtual int weight(int v1, int v2) =0;

 

    //设置,获取标记

    virtual int getMark(int v) =0;

    virtual void setMark(int v, int value) =0;

};

 

#endif

 

2.边类 (edge.h)

 

#ifndef _EDGE_H

#define _EDGE_H

 

class Edge            //边类

{

private:

    int tailnum,headnum;//弧头弧尾的下标

    int _weight;        //权值

    Edge *tailp;        //指向tail相同的下一条边

    Edge *headp;        //指向head相同的下一条边

public:

    Edge(int tl,int hd,Edge *t0p,Edge *hp,int wgt=1)

    {

        tailnum=tl;

        headnum=hd;

        tailp=t0p;

        headp=hp;

        _weight=wgt;

    }

    void setWeight(int wgt) { _weight=wgt; } //更改权值

    void setTailp(Edge *t0p) { tailp=t0p; }  //重设tail的下一条出边

    void setHeadp(Edge *hp) { headp=hp; }    //重设head的下一条入边

    int weight() { return _weight; }         //返回权值

    int tail() { return tailnum; }           //返回弧尾

    int head() { return headnum; }           //返回弧头

    Edge* tailNext() { return tailp; }       //弧尾的下一条出边

    Edge* headNext() { return headp; }       //弧头的下一条入边

};

 

#endif

 

3.顶点类 (vexnode.h)

 

#ifndef _VEXNODE_H

#define _VEXNODE_H

 

template <typename E>   //顶点类

class VexNode

{

public:

    E vex;           //标号

    Edge *outedge;   //储存出边

    Edge *inedge;    //储存入边

    VexNode()

    {

        outedge=inedge=NULL;

    }

};

 

#endif

 

4.图类 (OLgragh.h)

 

#ifndef _OLGRAGH_H

#define _OLGRAGH_H

 

#include"gragh.h"

#include"edge.h"

#include"vexnode.h"

 

 

template <typename E>

class OLGragh : public Graph<E> //十字链表实现

{

private:

    int numVertex,numEdge;  //顶点和边的数量

    VexNode<E> *vexarray;   //顶点数组

    int *mark;              //标记数组

    Edge **outCurr;         //保存出边链表的当前位置

    Edge **inCurr;          //保存入边链表的当前位置

 

    void Init(int n)        //初始化一个含n个顶点的图

    {                       //由于顶点标号类型不确定,故未初始化顶点数组,请根据标号类型手动初始化顶点数组

        numEdge=0;

        numVertex=n;

        outCurr = new Edge* [n];

        inCurr = new Edge* [n];

        mark = new int[n];

        vexarray = new VexNode<E>[n];

        for(int i=0;i<n;i++)

        {

            mark[i]=0;

            inCurr[i]=outCurr[i]=NULL;

        }

    }

 

public:

    OLGragh(int n) { Init(n); }

    ~OLGragh()

    {

        delete [] mark;

        delete [] outCurr;

        delete [] inCurr;

        for(int i=0;i<numVertex;i++)

        {

            Edge *next=vexarray[i].outedge;

            Edge *curr=NULL;

            while(next != NULL)

            {

                curr=next;

                next=curr->tailNext();

                delete curr;

            }

            delete curr;

        }

        delete [] vexarray;

    }

 

    //返回顶点和边的数量

    int n(){ return numVertex; }

    int e(){ return numEdge; }

 

    //返回v的第一个出边相邻顶点

    int firstOut(int v)

    {

        outCurr[v] = vexarray[v].outedge;

        if(outCurr[v])

            return outCurr[v]->head() ;

        return -1;   //无边的情况

    }

 

    //返回v的下一个出边相邻顶点

    int nextOut(int v)

    {

        if(!outCurr[v])

            return -1;

        outCurr[v]=outCurr[v]->tailNext();

        if(outCurr[v])

            return outCurr[v]->head();

        return -1;   //已经是最后一个顶点

    }

 

    //返回v的第一个入边相邻顶点

    int firstIn(int v)

    {

        inCurr[v]=vexarray[v].inedge;

        if(inCurr[v])

            return inCurr[v]->tail();

        return -1;   //无边的情况

    }

 

    //返回v的下一个入边相邻顶点

    int nextIn(int v)

    {

        if(!inCurr[v])

            return -1;

        inCurr[v]=inCurr[v]->headNext();

        if(inCurr[v])

            return inCurr[v]->tail();

        return -1;   //已经是最后一个顶点

    }

 

    int locateVex(E u)  //查找标号

    {

        int i;

        for(i=0;i<numVertex;i++)

        {

            if(vexarray[i].vex == u) return i;

        }

        return -1;     //查找失败的情况

    }

 

    E getVex(int v) //返回标号

    {

        return vexarray[v].vex;

    }

 

    void putVex(int v,E value) //设置标号

    {

        vexarray[v].vex=value;

    }

 

    //建立,删除边

    void setEdge(int v1,int v2,int wght=1)

    {

        vexarray[v1].outedge=vexarray[v2].inedge=new Edge(v1,v2,vexarray[v1].outedge,vexarray[v2].inedge,wght);

        numEdge++;

    }

 

    void delEdge(int v1,int v2)   //删除边后对应顶点的tmp指针失效,next函数不能返回正确的结果,应重新使用first

    {

        outCurr[v1]=inCurr[v2]=NULL; //防止出现野指针

        Edge *del=NULL;

        Edge *curr=NULL;

        Edge *next=vexarray[v1].outedge;

        while(next != NULL)       //v1的边中删除

        {

            if(next->head()==v2)

            {

                del=next;

                if(curr==NULL)  //<v1,v2>v1的第一条出边的情况

                    vexarray[v1].outedge=del->tailNext();

                else

                    curr->setTailp(del->tailNext());

            }

            curr=next;

            next=curr->tailNext();

        }

        curr=NULL;

        next=vexarray[v2].inedge;

        while(next != NULL)       //v2的边中删除

        {

            if(next->tail()==v1)

            {

                del=next;

                if(curr==NULL)  //<v1,v2>v2的第一条入边的情况

                    vexarray[v2].inedge=del->headNext();

                else

                    curr->setHeadp(del->headNext());

            }

            curr=next;

            next=curr->headNext();

        }

        if(del != NULL)

            numEdge--;

        delete del;

    }

 

    int weight(int v1, int v2)  //获取给定边的权值

    {

        Edge *curr=vexarray[v1].outedge;

        while(curr!=NULL)

        {

            if(curr->head() == v2)

                return curr->weight();

            curr=curr->tailNext();

        }

        return 0;

    }

 

    virtual int getMark(int v) //获取标记

    {

        return mark[v];

    }

 

    virtual void setMark(int v, int value) //设置标记

    {

        mark[v]=value;

    }

};

 

#endif

 

5.测试程序(OLgragh.cpp)

#include<iostream>

#include"gragh.h"

#include"OLgragh.h"

using namespace std;

 

//dfs:打印顶点标号和所有出,入边的权之和

void DFS(int v1,OLGragh<char> &G)

{

    if(G.getMark(v1))

        return ;

    G.setMark(v1,1);

    int s1,s2;

    s1=s2=0;

    int p=G.firstOut(v1);

    while(p!=-1)

    {

        s1+=G.weight(v1,p);

        p=G.nextOut(v1);

    }

    p=G.firstIn(v1);

    while(p!=-1)

    {

        s2+=G.weight(p,v1);

        p=G.nextIn(v1);

    }

    cout << G.getVex(v1)<<"的出边权之和(无权图的出度):"<<s1

    <<"  入边权之和(无权图的入度):"<<s2<<endl;

    p=G.firstOut(v1);

    while(p!=-1)

    {

        DFS(p,G);

        p=G.nextOut(v1);

    }

}

 

bool flag1;      //保存图是否带权

void INIT(OLGragh<char>& G)  //建图

{

    cout<<"此图是否为带权图(是请输入1,不是请输入0):";

    cin >> flag1;

    int n=G.n();

    char a,b;

    cout <<"请输入初始状态时,图中各顶点的标号(以空格分隔)\n";

    for(int i=0;i<n;i++)

    {

        cin >> a;

        G.putVex(i,a);

    }

    cout <<"请输入初始状态时,图中边的数量:";

    cin >> n;

    cout <<"请输入初始状态时,图中边连接的顶点的标号\n"

    <<"每行输入两个字母,以空格分隔\n"

    <<"弧尾在前,弧头在后(带权图后加空格和权值)\n"

    <<"例如:A B (带权:A B 3)A指向B的边\n";

    for(int i=0;i<n;i++)

    {

        int c=1;

        cin >> a >> b;

        int x=G.locateVex(a);

        int y=G.locateVex(b);

        if(flag1)

            cin >>c;

        G.setEdge(x,y,c);

    }

}

 

void menu(OLGragh<char>& G)  //菜单

{

    int x,y,c;

    char a,b;

    int flag = 1;

    while(flag)

    {

        cout <<"----------------------------------\n";

        int t;

        cout<<"0.退出程序\n"

            <<"1.插入边\n"

            <<"2.删除边\n"

            <<"3.DFS输出顶点和出、入边的权之和(无权图则为出、入度)\n"

            <<"请输入所需操作的序号:";

        cin >> t;

        switch(t)

        {

        case 0:

            flag=0;

            break;

        case 1:

            cout <<"请输入插入边的端点标号(和权)格式参照建图时边的输入的格式:";

            c=1;

            cin >> a >> b;

            x=G.locateVex(a);

            y=G.locateVex(b);

            if(flag1)

                cin >>c;

            G.setEdge(x,y,c);

            break;

        case 2:

            cout <<"请输入删除边的端点标号:";

            cin >> a >> b;

            x=G.locateVex(a);

            y=G.locateVex(b);

            G.delEdge(x,y);

            break;

        case 3:

            cout <<"dfs输出结果为:\n";

            for(int i=0;i<G.n();i++)  //初始化标记数组

                G.setMark(i,0);

            for(int i=0;i<G.n();i++)  //对非连通图的遍历

            {

                if(!G.getMark(i))

                    DFS(i,G);

            }

            break;

        default:

            cout <<"请输入正确的指令!!";

            break;

        }

        cout <<"----------------------------------\n\n";

    }

}

 

int main()

{

    int n;

    cout <<"本程序只支持有向图的储存,图的标号类型为字符\n";

    cout <<"请输入本次建立图的顶点数量:";

    cin >> n;

    OLGragh<char> G(n);

    INIT(G);

    menu(G);

    return 0;

}

 

7.测试结果示例

 

 

********** diagram (based on cross-linked table) implementation **********
posted @ 2023-01-04 17:49  陌初  阅读(68)  评论(0)    收藏  举报