c/c++常用算法(6) -- 数据结构(图)

一、概念


1.图、树、线性结构区别:

    (Graph)是一种比线性表和树更为复杂的数据结构。

    图结构:是研究数据元素之间的多对多的关系。在这种结构中,任意两个元素之间可能存在关系。即结点之间的关系可以是任意的,图中任意元素之间都可能相关。

    树结构:是研究数据元素之间的一对多的关系。在这种结构中,每个元素对下(层)可以有0个或多个元素相联系,对上(层)只有唯一的一个元素相关,数据元素之间有明显的层次关系。

    线性结构:是研究数据元素之间的 一对一关系。在这种结构中,除第一个和最后一个元素外,任何一个元素都有唯一的一个直接前驱和直接后继。


二、图的定义和术语


    一个图(G)定义为一个偶对(V,E) ,记为G=(V,E)。

    其中: V是顶点(Vertex)的非空有限集合,记为V(G);E是无序集V&V的一个子集,记为E(G) ,其元素是图的(Arc)。

    将顶点集合为空的图称为空图。


    其形式化定义为:

        G=(V ,E)

        V={v|vÎdataobject}  

        E={<v,w>|v,wÎV∧p(v,w)}

        P(v,w)表示从顶点v到顶点w有一条直接通路。

        在无向图中,若"<v,w>ÎE(G) ,有<w,v>ÎE(G) ,即E(G)是对称,则用无序对(v,w) 表示v和w之间的一条边(Edge),因此(v,w)和(w,v)代表的是同一条边。


例1:设有有向图G1和无向图G2,形式化定义分别是:


G1=(V1 ,E1)
V1={a,b,c,d,e}
E1={<a,b>,<a,c>, <a,e>,<c,d>,<c,e> ,<d,a>,<d,b>,<e,d>}
G2=(V2 ,E2)
V2={a,b,c,d}
E2={(a,b), (a,c), (a,d), (b,d), (b,c), (c,d)}

        它们所对应的图如图7-1所示




        完全无向图:对于无向图,若图中顶点数为n,用e表示边的数目,则eÎ[0,n(n-1)/2]。具有n(n-1)/2条边的无向图称为完全无向图。

完全无向图另外的定义是:

        对于无向图G=(V,E),若"vi,vjÎV,当vi≠vj时,有(vi ,vj)ÎE,即图中任意两个不同的顶点间都有一条无向边,这样的无向图称为完全无向图


        完全有向图:对于有向图,若图中顶点数为n ,用e表示弧的数目,则eÎ[0,n(n-1)] 。具有n(n-1)条边的有向图称为完全有向图。

完全有向图另外的定义是:

        对于有向图G=(V,E),若"vi,vjÎV,当vi≠vj时,有<vi ,vj>ÎE∧<vj,vi>ÎE ,即图中任意两个不同的顶点间都有一条弧,这样的有向图称为完全有向图

        有很少边或弧的图(e<n㏒n)的图称为稀疏图,反之称为稠密图


        (Weight):与图的边和弧相关的数。权可以表示从一个顶点到另一个顶点的距离或耗费。


        子图和生成子图:设有图G=(V,E)和G’=(V’,E’),若V’ÌV且E’ÌE,则称图G’是G的子图;若V’=V且E’ÌE,则称图G’是G的一个生成子图


       顶点的邻接(Adjacent):对于无向图G=(V,E),若边(v,w)ÎE,则称顶点v和w 互为邻接点,即v和w相邻接。边(v,w)依附(incident)与顶点v和w 。对于有向图G=(V,E),若有向弧<v,w>ÎE,则称顶点v“邻接到”顶点w,顶点w“邻接自”顶点v,弧<v,w>与顶点v和w“相关联” 。


       顶点的度、入度、出度:对于无向图G=(V,E),"viÎV,图G中依附于vi的边的数目称为顶点vi的(degree),记为TD(vi)。

显然,在无向图中,所有顶点度的和是图中边的2倍。即   ∑TD(vi)=2e      i=1, 2, …, n ,e为图的边数。


        对有向图G=(V,E),若"vi ÎV ,图G中vi作为起点的有向边(弧)的数目称为顶点vi的出度(Outdegree),记为OD(vi) ;vi作为终点的有向边(弧)的数目称为顶点vi的入度(Indegree),记为ID(vi) 。顶点vi的出度入度之和称为vi的,记为TD(vi) 。即

TD(vi)=OD(vi)+ID(vi)


        路径(Path)、路径长度、回路(Cycle) :对无向图G=(V,E),若从顶点vi经过若干条边能到达vj,称顶点vi和vj是连通的,又称顶点vi到vj有路径

        对有向图G=(V,E),从顶点vi到vj有有向路径,指的是从顶点vi经过若干条有向边(弧)能到达vj。


三、图的抽象数据类型定义


        图是一种数据结构,加上一组基本操作就构成了图的抽象数据类型。

         图的抽象数据类型定义如下:

ADTGraph{
    数据对象V:具有相同特性的数据元素的集合,称为顶点集。
    数据关系R:R={VR}
    VR={<v,w>|<v,w>|v,wÎV∧p(v,w),<v,w>表示从v到w的弧,P(v,w)定义了弧<v,w>的信息
}

四、实现代码:


Graph.h

#ifndef __CHelloWorld__Graph__
#define __CHelloWorld__Graph__

#include <iostream>
#define MaxNum 20       //图的最大顶点数
#define MaxValue 65535  //最大值(可设为一个最大整数)

typedef struct
{
    char Vertex[MaxNum]; //保存顶点信息(序号或字母)
    int GType;           //图的类弄(0:无向图,1:有向图)
    int VertexNum;       //顶点的数量
    int EdgeNum;         //边的数量
    int EdgeWeight[MaxNum][MaxNum]; //保存边的权
    int isTrav[MaxNum];  //遍历标志
}GraphMatrix;

class Graph
{
public:
    void CreateGraph(GraphMatrix *GM);  //创建邻接矩阵图
    void ClearGraph(GraphMatrix *GM);   //清空矩阵
    void OutGraph(GraphMatrix *GM);     //输出邻接矩阵
    void DeepTraOne(GraphMatrix *GM,int n); //从第n个结点开始,深度遍历图
    void DeepTraGraph(GraphMatrix *GM); //深度优化遍历
};
#endif /* defined(__CHelloWorld__Graph__) */
Graph.cpp

#include "Graph.h"

void Graph::CreateGraph(GraphMatrix *GM)
{
    int i,j,k;
    int weight;
    char EstartV,EendV;
    
    std::cout<<"输入图中各顶点信息\n";
    for (i = 0; i<GM->VertexNum; i++)
    {
        getchar();
        printf("第%d个顶点:",i+1);
        scanf("%c",&(GM->Vertex[i]));
    }
    std::cout<<"输入构成各边的顶点及权值:\n";
    for (k = 0; k <GM->EdgeNum; k++)
    {
        getchar();
        printf("第%d条边:",k+1);
        scanf("%c %c %d",&EstartV,&EendV,&weight);
        for ( i= 0; EstartV != GM->Vertex[i]; i++);
        for ( j= 0; EendV != GM->Vertex[j]; j++);
        GM->EdgeWeight[i][j] = weight;
        if (GM->GType == 0) //若是无向图
        {
            GM->EdgeWeight[j][i] = weight;
        }
    }
}

void Graph::ClearGraph(GraphMatrix *GM)
{
    int i,j;
    for (i=0; i<GM->VertexNum; i++)
    {
        for (j=0; j<GM->VertexNum; j++)
        {
            GM->EdgeWeight[i][j] = MaxValue;
        }
    }
}

void Graph::OutGraph(GraphMatrix *GM)
{
    int i,j;
    for (j=0; j<GM->VertexNum; j++)
    {
        printf("\t%c",GM->Vertex[j]);
    }
    std::cout<<"\n";
    
    for (i=0; i<GM->VertexNum; i++)
    {
        printf("%c",GM->Vertex[i]);
        for (j=0; j<GM->VertexNum; j++)
        {
            if (GM->EdgeWeight[i][j] == MaxValue)
            {
                printf("\tZ");  //以Z表示无穷大
            }
            else
            {
                printf("\t%d",GM->EdgeWeight[i][j]);  //输出边的权值
            }
        }
        std::cout<<"\n";
    }
}

void Graph::DeepTraOne(GraphMatrix *GM, int n)
{
    int i;
    GM->isTrav[n] = 1;  //标记该顶点已处理过
    printf("\t%c",GM->Vertex[n]);
    for (i=0; i<GM->VertexNum; i++)
    {
        if (GM->EdgeWeight[n][i] != MaxValue && !GM->isTrav[n])
        {
            DeepTraOne(GM, i);
        }
    }
}

void Graph::DeepTraGraph(GraphMatrix *GM)
{
    int i;
    for (i = 0; i<GM->VertexNum; i++)
    {
        GM->isTrav[i] = 0;
    }
    std::cout<<"深度优先遍历结点";
    for (i=0; i<GM->VertexNum; i++)
    {
        if (!GM->isTrav[i])
        {
            DeepTraOne(GM, i);
        }
    }
    std::cout<<"\n";
}
main.cpp

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

using namespace std;

int main(int argc, const char * argv[])
{
    GraphMatrix GM;
    Graph *myGraph = new Graph();
    std::cout<<"输入生成图的类型:";
    scanf("%d",&GM.GType);
    std::cout<<"输入生成图的数量:";
    scanf("%d",&GM.VertexNum);
    std::cout<<"输入图的边数量:";
    scanf("%d",&GM.EdgeNum);
    myGraph->ClearGraph(&GM);
    myGraph->CreateGraph(&GM);
    std::cout<<"该图的邻接矩阵数据如下:\n";
    myGraph->OutGraph(&GM);
    myGraph->DeepTraGraph(&GM);
    
    // std::cout << "Hello, World!\n";
    return 0;
}

演示效果图:


                


示例图:


                


参考书籍:《C/C++常用算法手册》  《数据结构-清华大学严蔚敏》

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

热血枫叶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值