首页 > 代码库 > 【算法与数据结构】图 -- 十字链表

【算法与数据结构】图 -- 十字链表

 

图的【十字链表】表示法是一种链式存储结构,可以看成是【邻接表】和【逆邻接表】的组合

本文中用到的有向图

 

 

 

/************************************************************************
有向图的存储:十字链表

有向图的十字链表存储结构,是有一种链式存储结构,可以看成是【邻接表】和【逆邻接表】
的结合。
图中每条弧对应一个【弧结点】,每个顶点对应一个【顶点结点】

弧结点
--------------------------------------------
| tailvex | headvex | hlink | tlink | info |
--------------------------------------------
talivex:以该弧为【弧尾】的结点在图中的位置
headvex:以该弧为【弧头】的结点在图中的位置
hlink:  下一条与该弧有【相同弧头的弧】
tlink:  下一条与该弧有【相同弧尾的弧】
info:   弧的相关信息,权值等

顶点结点
-----------------------------
| data | firstin | firstout |
-----------------------------
data:    该结点的数据
firstin: 第一条以该弧为弧头的【弧结点】
firstout:第一条以该弧为弧尾的【弧结点】
 

************************************************************************/

 

 

 

相关数据结构

//顶点结点最大数量
int const MAX_VERTEXNUM = 100;


//数据结构

typedef int InfoType;
typedef int Data;
 
//弧结点
typedef struct _tagArcBox
{
    int tailvex;              //该弧的弧尾结点在图中的位置
    int headvex;              //该弧的弧头结点在图中的位置
    struct _tagArcBox* hlink; //下一条与该弧有相同弧头结点的弧
    struct _tagArcBox* tlink; //下一条与该弧有相同弧尾结点的弧
    InfoType info;            //弧的相关信息
}ArcBox;

//顶点结点
typedef struct _tagArcNode
{
    Data data;         //数据
    ArcBox* firstin;   //第一条以该节点为弧尾的弧
    ArcBox* firstout;  //第一条以该结点为弧头的弧
}ArcNode;
 
 
//十字链表存储结构的有向图
typedef struct _tagOLGraph
{  
    ArcNode vertex[MAX_VERTEXNUM]; //顶点向量 

    int vexnum;       //顶点数
    int arcnum;       //弧树 

}OLGraph, *POLGraph;

 

 

 

从顶点向量中查找该顶点在图中的位置(下标)

//输入图的【顶点向量】和某个顶点的数据
//获取此顶点在顶点向量中的位置(下标)
int LocateNode(ArcNode* pNodesArr, Data data, int length)
{
    if( NULL == pNodesArr ) return NULL; 

    for (int i = 0; i < length; ++ i)
    {
        if (pNodesArr[i].data =http://www.mamicode.com/= data) return i;
    }

    return -1; 
}

//输入【图】和某顶点的数据
//获取该顶点在顶点向量中的位置(下标)
int LocateNode(POLGraph& pOLGraph, Data data)
{
    return (NULL == pOLGraph ? 
            NULL : LocateNode(pOLGraph->vertex, data, pOLGraph->vexnum) );
}

 

 

有向图的创建

/************************************************************************
当使用这种函数原型时:
void CreateOLGraph(POLGPraph&  pOLGraph)
{
    pOLGraph = new OLGraph();
    //codes
    pOLGraph->vexnum = num;  //这里报运行时错误! why?
}
************************************************************************/
//创建十字链表有向图 
POLGraph CreateOLGraph()
{
    POLGraph pOlGraph = NULL;

    __try
    {
        pOlGraph = new OLGraph(); 

        if(NULL == pOlGraph) {cerr << "申请图结点失败!\n"; return NULL;}

        int num = 0;
        cout << "请输入图的顶点数量:";
        cin >> num;
        pOlGraph->vexnum = num; 

        cout << endl;

        cout << "请输入图的弧的数量:";
        cin >> num;
        pOlGraph->arcnum = num;

        cout << endl;

        Data data = 0;

        cout << endl << "--------开始 初始化顶点向量-----------"<<endl;
        for (int i = 0; i < pOlGraph->vexnum; ++i)
        {
            cout << "请输入结点的值:";
            cin >> data;

            pOlGraph->vertex[i].data =http://www.mamicode.com/ data; 
            pOlGraph->vertex[i].firstin = NULL;
            pOlGraph->vertex[i].firstout = NULL;
            cout<<endl;

        }  //for
        cout <<endl<<"------------结束 初始化顶点向量------------"<<endl;

        cout<<endl<<endl;

        cout << "************开始 初始化弧结点 **************"<<endl;
        for(int i = 0; i < pOlGraph->arcnum; ++ i)
        {
            cout << "请输入弧的弧尾结点:";
            cin >> data;
            int begin = LocateNode(pOlGraph->vertex, data, pOlGraph->arcnum);
            if(-1 == begin) {cerr << "您输入的弧尾结点不在图中,请重新输入"<<endl; --i; continue;}

            cout << "请输入弧的弧头结点:";
            cin >> data;
            int end = LocateNode(pOlGraph->vertex, data, pOlGraph->arcnum);
            if(-1 == end) {cerr << "您输入的弧头结点不在图中,请重新输入"<<endl; -- i; continue;}

            cout << "请输入弧的权值:";
            cin >> data;

            cout<<endl<<endl;

            ArcBox* pArcBox = new ArcBox();
            if(NULL == pArcBox) {cerr << "申请弧结点失败!"<<endl; -- i; continue;}

            pArcBox->tailvex = begin;                           //该弧的弧尾在图中的位置
            pArcBox->headvex = end;                             //该弧的弧头在图中的位置
            pArcBox->hlink = pOlGraph->vertex[end].firstin;     //下一条与该弧有相同弧尾的弧结点
            pArcBox->tlink = pOlGraph->vertex[begin].firstout;  //下一条与该弧有相同弧头的弧结点
            pArcBox->info = data;                               //权值

            pOlGraph->vertex[begin].firstout = pOlGraph->vertex[end].firstin = pArcBox;
        
        } //for

    } //__try

    __except(1)
    {
        cerr << endl<<"有异常发生"<<endl;
    } 

    return pOlGraph;
}

 

 

运行情况:

 

 

 

 

出度和入度 

//求图的出度
//先根据输入的顶点的值,求得该点所在的顶点向量的分量
//然后得到该点的firstout,然后再得到所有与该弧有相同弧尾
//结点的弧(的条数)
int OutDegree(POLGraph& pOLGraph, Data data)
{
    int nCount = 0;

    //根据结点的值定位该点在图的顶点向量中的位置(下标)
    int nIndex = LocateNode(pOLGraph, data);
    if(-1 == nIndex) {cerr << "该点不在图中,所以没有出度!\n"<<endl; return -1;}

    //得到该结点指针
    ArcNode* pArcNode = &(pOLGraph->vertex[nIndex]);
    if(NULL == pArcNode) {cerr << "在图中查找该顶点出错!\n"<<endl; return -1;}

    //第一条该顶点为弧尾的弧(该顶点结点的第一条出边)
    ArcBox* pArcBox = pArcNode->firstout;
    
    //查找所有以该【顶点结点】为【弧尾】的【弧结点】
    while(NULL != pArcBox)
    {
        ++ nCount;
        pArcBox = pArcBox->tlink;
    }
    
    return nCount;
}

//定点的入度
int InDegree(POLGraph& pOLGraph, Data data)
{
    int nCount = 0;

    //定位该顶点结点在顶点向量中的位置(下标)
    int nIndex = LocateNode(pOLGraph, data);
    if(-1 == nIndex){cerr << "该点不在图中,所以没有入度!\n"<<endl; return -1;}

    //得到该顶点结点的指针
    ArcNode* pArcNode = &(pOLGraph->vertex[nIndex]);
    if(NULL == pArcNode) {cerr << "在图中查找该点出错!"<<endl; return -1;}

    //第一条以该顶点结点为弧头的弧(该顶点结点的第一条入边)
    ArcBox* pArcBox = pArcNode->firstin;

    //查找所有以该【顶点结点】为【弧头】的【弧结点】
    while(NULL != pArcBox)
    {
        ++nCount;
        pArcBox = pArcBox->hlink;
    }

    return nCount;
}

 

 

 查找出度 / 入度

    POLGraph pGraph = CreateOLGraph();

    int num = 0;
    for(int i = 0; i < pGraph->vexnum + 3; ++ i)
    {
        cout << "请输入带查的顶点的值:";
        cin >> num;
    
        cout<<"结点 "<< num << " 的出度OutDegree为:"<<OutDegree(pGraph, num);
        cout<<endl;
        cout<<"结点 "<< num << " 的入度InDegree为:"<<InDegree(pGraph, num); 

        cout<<endl<<endl;
    }

 

 

 

 

 

 

 

 深度优先遍历(DFS)  

/************************************************************************
深度优先遍历
从某顶点出发,访问之,然后获得该顶点的第一条出边(firstout),如果该出边
不为空,则获得该条出边的【弧头】结点在图中的位置(下标),查看此下标的结点
是否被访问过,如果没有则根据此下标获取该结点,然后递归访问之;如果此结点
被访问过了,则说明出现回路,及此条弧指向了之前访问过的结点,需要跳出循环,
否则出现死循环。

************************************************************************/

//顶点结点是否遍历过标志数组
bool* pVisited = NULL;

void DFS(POLGraph& pOLGraph, ArcNode* pArcNode)
{
    int nIndex = LocateNode(pOLGraph, pArcNode->data);
    pVisited[nIndex] = true;
    cout << "the node is "<<pArcNode->data<<endl;

    ArcBox* pArcBox = pArcNode->firstout;

    while(NULL != pArcBox)
    {
        //该弧的弧头在图中的位置
        int nHeadVex = pArcBox->headvex;

        if (pVisited[nHeadVex] == false)
        {
            ArcNode* pHeadNode = &(pOLGraph->vertex[nHeadVex]);
            DFS(pOLGraph, pHeadNode);
        }  

        //如果某条弧的弧头结点已经被访问过时,则说明已经有了回路,此时要跳出循环
        //否则会在while中死循环
        else
        {
            break;
        }
    } 
}


//有向图的深度优先遍历
void DFSTraverse(POLGraph& pOLGraph)
{
    if(NULL == pOLGraph) {cerr << "该图为空!"; return;}

    pVisited = new bool[pOLGraph->vexnum]();
    for (int i = 0; i < pOLGraph->vexnum; ++ i) pVisited[i] = false;

    for (int i = 0; i < pOLGraph->vexnum; ++ i)
    {
        if (! pVisited[i])
        {
            DFS(pOLGraph, &pOLGraph->vertex[i]);
        }
    } 
}

 

 

深度优先遍历结果 

 

 

 

 

广度优先遍历(BFS)