zoukankan      html  css  js  c++  java
  • 数据结构中Graph的邻接表实现的讨论

    最近和二姐讨论了下关于图论的实现问题,感觉有点用,怕以后找不到了,记录在此。代码原本是网上找到的,在原来的基础上做了些修改。

    Graph.h:

    /*因为师兄所给的那些链表里面存的都是Qstring类型所以我把NameType定义成了Qstring类型的应该就差不多了吧?*/

    #ifndef GRAPH_H
    #define GRAPH_H
    #include <iostream.h>
    #include <QString>
    #include <QStringList>
    #define DefaultSize 20
    #define NULL 0
    //typedef int NameType;
    typedef Qstring NameType;
    //typedef int DistType;因为构造的图不用权值信息,所以我把他去掉了
    //template<class NameType,class DistType>
    class Graph;
    //template<class DistType>
    class Edge
    {
    public:
    friend class Graph;
    Edge(){}
    Edge(int D):dest(D),link(NULL)
    {}
    //DistType 已经被你去掉了,这里也没有了
    /*//这里为什么要两个构造函数?
    ****************************************************************************
    两个构造函数,参数不一样,是构造函数的函数重载
    (如果不知道什么事重载的话,看书或者百度去)
    Edge(){}
    是没有带参数的构造函数,其实什么也没干
    如果一个类没有定义构造函数,
    一般编译器会默认地自动加入一个这样的构造函数,
    因为类是必须有构造函数的,
    否则对象没法初始化
    ****************************************************************************
    Edge(int D,DistType C):dest(D),cost(C),link(NULL)
    {}
    这个则是带参数D和C的构造函数, 将D赋值给dest,C赋值给cost,NULL赋值给link指针
    当你调用构造函数时,不带参数就调用第一个构造函数,
    带两个参数就调用第二个构造函数
    ****************************************************************************
    */
    int operator !=(Edge &E) const
    {//这个函数是C++中运算符 ' != ' 的重载函数
    return E.dest!=dest;
    }
    int Graph::AddListToGraph(QTlink &L)//这是我增加的函数,用于增加链表到图中
    //参数类型不一定对,只是算法思路
    private:
    int dest;//该弧指向的顶点的位置? Yes
    //DistType cost; //弧上的权值? Yes
    Edge *link;//指向下一条弧的指针 Yes
    };
    //template<class NameType,class DistType>
    class Vertex
    {
    friend class Graph;
    friend class Edge;
    NameType data;//顶点数据? Yes
    Edge *adj;//指向顶点的第一条弧的指针? No
    //是指向第一条依附于该顶点的弧的指针
    //如果是有向图的话,就是第一条从该顶点出发的弧的地址指针
    //通过该弧的link指针可以找个从该顶点出发的下一个弧的地址指针
    //依次往下,知道link==NULL,可以找出所有该顶点出发的弧
    //从这个顶点的所有出发的弧的dest量可以找出该顶点指向的其他顶点在顶点数组的下标
    };
    //template<class NameType,class DistType>
    class Graph
    {
    private:
    //每个变量的具体含义不知道理解得对不对
    Vertex *nodeTable;//这个就不知道具体是干嘛的
    /*
    这个要结合Graph的构造函数看,里面有
    nodeTable=new Vertex[L.size()+2];
    nodeTable存放的是图的顶点数组的首地址
    顶点数组是根据要求动态申请的(new),大小在构造的时候确定
    这里存首地址
    */
    int numVertexs;//当前顶点数目? Yes
    int maxNumVertexs;//最大定点数目? Yes
    int *status; //顶点数组指针? No不确定 顶点状态指针,不知道什么状态,好像还没用
    //估计应该是用来进行优先遍历的时候用的状态,表示某个顶点时候被访问了,相当于书上的visited数组
    int numEdges;//当前弧的数目? Yes
    int GetVertexPos(const NameType &ver);//确定顶点位置函数
    public:
    //Graph(int sz);因为要从链表中得到信息,因此改了下构造函数
    Graph( list &L);//因为构造图的时候信息是从链表获得的,所以我只传递了链表,初始化时见后面,顶点个数为链表长度+2,因为我想要实现就是所有
    //链表的头结点和尾节点如果没有相同的节点的话就连到两个公共的节点上去
    /*
    顶点个数设为链表长度+2尚有未解决的问题要处理
    你是要合并链表的,定点数只有一个链表的大小,那么新增加的节点没有地方放
    如果是这样定义的,那么你需要在后面增加顶点节点的时候,判断定点数时候用完了
    如果用完了,就要重新申请一个更大的内存空间,比如这次要比刚开始多申请10个顶点的大小
    然后将以前的顶点数组的内容全部拷贝过去(用memcpy函数)
    再将原来的顶点数组的内存空间手动释放(delete)
    再将数组的首地址赋值给nodeTable
    这些部分放在新增顶点的函数里面,我已经写进去了
    如果对空间利用率要求不高的话,你可以考虑先申请足够大的内存空间
    保证肯定够用
    */
    ~Graph();
    friend ostream& operator<<(ostream &out,Graph &ref);
    int GraphEmpty()const {return numVertexs==0;}//判断图是否为空
    int GraphFull()const {return numVertexs==maxNumVertexs;}//图是否已经满了
    int NumberOfVertexs()const {return numVertexs;}//得到图中当前顶点数的函数
    int NumberOfEdges() const {return numEdges;}//得到图中当前弧的数目的函数
    NameType GetValue(int i)//取出图中某个顶点的数据的函数
    {
    return i>=0 && i<numVertexs ? nodeTable[i].data : NULL;
    }
    void InsertVertex(const NameType &vertex);//插入顶点函数
    //void InsertEdge(int v1,int v2,DistType weight);//插入弧的函数
    void InsertEdge(int v1,int v2);//插入弧的函数
    void RemoveEdge(int v1,int v2);//移除弧的函数
    void RemoveVertex(int v);//移除顶点的函数
    //DistType GetWeight(int v1,int v2);//获取某弧的权重的函数
    int GetFirstNeighbor(int v);//获取某个顶点的第一个邻接点的函数
    int GetNextNeighbor(int v1,int v2);//获取某个顶点的下一个邻接点的函数
    void DFS(int start);//深度遍历函数
    //嗯,所有的信息都不是输入的,而是根据链表的情况设定的
    //DFS只是书上深度优先遍历算法的一半,在各个是从顶点start出发遍历图G
    //还有一个DFSTraverse 函数结合起来才能 完成对图的优先遍历

    };

    #endif


    Graph.c:

    // graph.cpp : Defines the entry point for the console application.
    //

    #include"Graph.h"
    #include<iostream.h>
    //template<class NameType,class DistType>
    Graph::Graph( QStringList &L):numVertexs(0),maxNumVertexs(L.size()+2),numEdges(0)
    {
    int n,e,j,k;
    //NameType name;
    NameType first_Vertex="first";
    status=new int[maxNumVertexs];
    for(int i=0;i<maxNumVertexs;i++)
    {
    status[i]=0;
    }
    nodeTable=new Vertex[maxNumVertexs];
    //先申请最大容许的数,不够再分配,增加最大容许数
    //这里有点问题,我不知道肿么把那个头结点和尾节点表示出来
    /*
    头结点放在数组的第一个位置
    尾节点不用管吧,一个图,一般没有头尾节点的概念了
    通过数组能够很快的访问到所有的节点,这个不够用吗?
    */
    for(i=0;i<L.size();i++)
    {
    InsertVertex(L.at(i)); //括弧里面是取得链表里面的值的函数,相当于name

    }
    //接下来的关于开始的弧的建立也米思路鸟,反正大致就是从头结点开始然后链表里面的数据是按顺序一个个用弧连接起来的,反正第一个链表是
    //这个样子转化为图的,接下来关于要肿么把其他的链表里的数据一个个添进来也米想清楚,暂时就这么多,晚上有课,反正你先看看把,我八点多
    //下课,嘿嘿
    /*
    插入顶点的同时也要将弧建立起来
    初始化部分弧建立很简单嘛,一个接一个0->1,1->2,...,L.size()-2->L.size()-1
    */
    for(i=0;i<L.size()-1;i++)
    {
    InsertEdge(i,i+1);
    }
    /*
    构造函数只能调用一次
    如果你要增加新的链表就不行了
    你必须能保证你能在构造的时候一次加完所有
    否则就另外写函数
    其他链表的加入,已经不是初始化了,需要另外写函数
    都放在构造函数里面有违常理
    我写的函数见AddListToGraph函数
    */


    }
    /*Graph::Graph(int sz):numVertexs(0),maxNumVertexs(sz),numEdges(0)
    {
    int n,e,j,k;
    NameType name;
    DistType weight;
    status=new int[maxNumVertexs];
    for(int i=0;i<maxNumVertexs;i++)
    status[i]=0;
    nodeTable=new Vertex[sz];

    cout<<"input the number of vertices you want to creat:";
    cin>>n;
    for( i=0;i<n;i++)
    {
    cout<<"the "<<i<<" vertices value:";
    cin>>name;
    InsertVertex(name);
    }
    cout<<"input the number of edges you want to creat:"<<endl;
    cin>>e;
    cout<<"the edge order is head tail weight,such as 3 5 67:\n";
    for(i=0;i<e;)
    {
    cout<<"the "<<i<<" edge:";
    cin>>j>>k>>weight;
    if(j==k)
    {
    cout<<"input error! head=tail is forbidden!"<<endl;
    continue;
    }
    i++;
    }
    }
    */
    void Graph::InsertVertex(const NameType &ver)
    {
    Vertex *pVertex;
    int *p;
    if(numVertexs<maxNumVertexs)
    {
    (nodeTable[numVertexs]).data=ver;
    (nodeTable[numVertexs]).adj=NULL;
    numVertexs++;
    }
    else
    {
    //重新申请空间
    cout<<"it is full!re malloc now"<<endl;
    pVertex=new Vertex[this->maxNumVertexs+10];//重新申请
    memcpy(pVertex,nodeTable,maxNumVertexs*sizeof(Vertex));//内容移到新申请空间
    delete []nodeTable;//释放旧空间 //这个语法有点忘了,好像是这样子
    nodeTable=pVertex;//修改指针
    p=new int[maxNumVertexs+10];//重新申请
    memcpy(p,status,maxNumVertexs*sizeof(int));//内容移到新申请空间
    delete []status; //释放旧空间 //这个语法有点忘了,好像是这样子
    status=p;//修改指针
    maxNumVertexs=maxNumVertexs+10;//最大空间要增加
    return;
    }
    }
    /*
    我把这个函数删掉了一半
    原来应该是无向图的,即增加v1到v2和v2到v1的弧
    现在应该是增加从v1到v2的弧
    */
    void Graph::InsertEdge(int v1,int v2)
    {
    Edge *ps=NULL;
    Edge *pEdge=new Edge(v2);
    ps=nodeTable[v1].adj;////指向顶点的第一条弧的指针
    if(ps==NULL)
    ps=pEdge;
    else
    {
    while( ps->link!=NULL)
    ps=ps->link; //下一条弧
    ps->link=pEdge;//从v1顶点出发的最后一条弧
    }
    }
    void Graph::RemoveEdge(int v1,int v2)
    {
    Edge *p1,*p2;
    p2=p1=nodeTable[v1].adj;
    while( p1!=NULL && p1->dest!=v2 )
    {
    p2=p1;
    p1=p1->link;
    }
    if(p1==nodeTable[v1].adj)//v1的第一条弧就指向了v2
    {
    nodeTable[v1].adj=p1->link;
    delete p1;
    }
    else
    {
    /*
    这段原来用了if else,看上去貌似不用,就删了判断部分
    */
    p2->link=p1->link;
    delete p1;
    }


    /*
    这个是对称的,应该不用了,不是无向图
    p2=p1=nodeTable[v2].adj;
    while( p1!=NULL && p1->dest!=v1 )
    {
    p2=p1;
    p1=p1->link;
    }
    if(p1==nodeTable[v2].adj)
    {
    nodeTable[v2].adj=p1->link;
    delete p1;
    }
    else
    {
    if(p1->link!=NULL)
    {
    p2->link=p1->link;
    delete p1;
    }
    else
    {
    p2->link=NULL;
    delete p1;
    }
    }
    */
    }
    Graph::~Graph()
    {
    Edge *p,*p1;
    for(int i=0;i<numVertexs;i++)
    {
    p=(nodeTable[i]).adj;
    while(p!=NULL)
    {
    p1=p;
    p=p->link;
    delete p1;
    }
    delete p;
    }
    delete []nodeTable;
    delete []status;//这个是我加的,这个也应该释放掉吧
    }
    void Graph::RemoveVertex(int v)
    {
    Edge *p1;
    int dst;
    p1=(nodeTable[v]).adj;
    while(p1!=NULL)
    {
    dst=p1->dest;
    this->RemoveEdge(v,dst);
    }
    for(int i=v;i<numVertexs;i++)
    nodeTable[i]=nodeTable[i+1];

    numVertexs--;
    }
    /*
    //貌似木有了
    DistType Graph::GetWeight(int v1,int v2)
    {
    Edge *p;
    p=nodeTable[v1].adj;
    while(p!=NULL&&p->dest!=v2)
    p=p->link;
    if(p==NULL)
    {
    cout<<"no edge exist!";
    return 0;
    }
    return p->cost;
    }
    */
    int Graph::GetNextNeighbor(int v1,int v2)
    {
    Edge *p=nodeTable[v1].adj;
    while(p!=NULL&&p->dest!=v2)
    p=p->link;
    if(p==NULL)
    {
    cout<<"no next neighbor!";
    return -1;
    }
    else
    {
    if(p->link!=NULL)
    return p->link->dest;
    else
    return -1;
    }
    }
    int Graph::GetVertexPos(const NameType &ver)
    {
    for(int i=0;i<numVertexs;i++)
    {
    if(nodeTable[i].data==ver)
    return i;
    }
    return -1;
    }
    int Graph::GetFirstNeighbor(int v)
    {
    if(nodeTable[v].adj!=NULL)
    return nodeTable[v].adj->dest;
    else return -1;
    }

    /*ostream& operator<<(ostream &out,Graph &ref)
    {
    for(int i=0;i<numVertexs;i++)
    out<<"vertices: "<<i<<" value: "<<nodeTable[i].data;
    for( i=0;i<numVertexs;i++)
    {
    Edge *p=(nodeTable[i]).adj;
    while()
    out<<"edge:"<<i

    return out;

    }
    */
    void Graph::DFS(int start)
    {

    if( start<0 && start>=numVertexs )
    {
    cout<<"end!"<<endl;
    return;
    }
    cout<<nodeTable[start].data<<endl;
    int v1=this->GetFirstNeighbor(start);
    while(v1!=-1)
    {
    cout<<this->GetValue(v1)<<endl;
    DFS(v1);
    v1=this->GetNextNeighbor(start,v1);

    }
    return;
    }



    int Graph::AddListToGraph(QTlink &L)
    {
    /* 假设考虑链表类型为QTlink
    要将这个函数也放在类里面,这样就可以方便调用类的内部函数和变量
    思路还是比较清晰的:
    增加链表到图中就是要完成两件事:
    1)增加节点(如果图中已经有这个节点了,就不用增加了)
    2)增加链表对应的弧,即链表的上一个节点到下一个节点间反映到图中是一条弧

    即先增加第一个节点,然后以后没增加一个节点,就再增加一个前节点到这个节点的弧
    所以保存好前后节点在顶点数组的位置,就可以很快的增加弧了
    唯一的例外就是前后两个节点都再图中出现过,就需要考察是否已经有他们的连接存在,如果有就不增加弧
    否则增加弧
    */
    QTlink *p;
    int index_l,index_c; //链表的前一个,后一个节点在顶点数组中的位置,用于增加弧时调用
    int flag_l,flag_c;//节点是否已经在图中的标志0:没有,1:有
    Edge *ps=NULL;
    int flag;//是否需要增加两顶点间弧的标志
    //增加第一个节点
    p=L->next;//L的头指针指向的第一个节点
    if(p==NULL)return 0;//如果链表是空的,则返回
    flag_l=0;//标志初始化为0
    for(i=0;i<numVertexs;i++)
    if(nodeTable[i].data==p->data)//该节点已经在图中
    {
    index_l=i;
    flag_l=1;
    break;
    }
    if(flag_l==0)//图中没有该节点
    {
    InsertVertex(p);//在图中新增加该节点
    index_l=numVertexs-1;//新节点在当前实际顶点数组的最后一个,下标为当前顶点数-1
    }
    //增加后面的节点
    while(p->next!=NULL)//后面还有节点
    {
    flag_c=0;//初始化清0
    p=p->next;//指向要增加的节点
    //插入节点
    for(int i=0;i<numVertexs;i++)
    {
    if(nodeTable[i].data==p->data)//该节点已经在图中)
    {
    index_l=i;
    flag_c=1;
    break;
    }
    }
    if(flag_c==0)//图中没有该节点
    {
    InsertVertex(p);//在图中新增加该节点
    index_c=numVertexs-1;//新节点在当前实际顶点数组的最后一个,下标为当前顶点数-1
    }
    //插入弧:V[index_l]------>V[index_c]
    if(flag_l&&flag_c)//如果前后两个节点都在原来的图中出现过
    {
    //需要先判断原来是否有这样的弧存在
    flag=0;//标志 清0
    ps=nodeTable[index_l].adj;////指向顶点的第一条弧的指针
    if(ps==NULL)//V[index_l]还没有弧
    flag=0;
    else
    {
    while( ps->link!=NULL)
    {
    ps=ps->link; //下一条弧
    if(ps->dest==index_c)flag=1;
    }
    }
    if(!flag)//两顶点间原来没有弧V[index_l]------>V[index_c],需要增加弧
    {
    InsertEdge(index_l,index_c);
    }
    }
    else
    InsertEdge(index_l,index_c);
    //更新
    index_l=index_c;
    flag_l=flag_c;
    }
    }



  • 相关阅读:
    DM6437 dsp系列之启动过程全析(2)—AIS文件解析
    DreamWeaver文件保存时,提示"发生共享违例"问题的解决方法
    再谈cacheAsBitmap
    as3 updateAfterEvent的作用
    导致flash屏幕重绘的几种方式及避免重绘的方法
    盒子模型&position定位
    [工作问题总结]MyEclipse 打开项目
    三周的苦逼学习,这点文字只为沧海之一粟
    偷个空,写个博客——各种沟通各种纠结
    Arbitrage HDU
  • 原文地址:https://www.cnblogs.com/followyourheart/p/graph1.html
Copyright © 2011-2022 走看看