【algo&ds】5.图及其存储结构、遍历

1.什么是图

表示”多对多”的关系
包含
一组顶点:通常用 V(Vertex)表示顶点集合
一组边:通常用 E(Edge)表示边的集合
边是顶点对:(v,w)∈ E,其中 v,w ∈ V ,v—w
有向边 <v,w> 表示从 v 指向 w 的边(单行线) v→w
不考虑重边和自回路

常见术语
无向图:图中所有的边无所谓方向
有向图:图中的边可能是双向,也可能是单向的,方向是很重要的
权值:给图中每条边赋予的值,可能有各种各样的现实意义
网络:带权值的图
邻接点:有边直接相连的顶点
出度:从某顶点发出的边数
入度:指向某顶点的边数
稀疏图:顶点很多而边很少的图
稠密图:顶点多边也多的图
完全图:对于给定的一组顶点,顶点间都存在边

抽象数据类型定义
类型名称:图(Graph)
数据对象集:G(V,E)由一个非空的有限顶点集合 V 和一个有限边集合 E 组成
操作集:对于任意图 G ∈ Graph,以及 v ∈ V,e ∈ E
主要操作有:

Graph Crate():建立并返回空图
Graph InsertVertex(Graph G,Vertex v):将 v 插入 G
Graph InsertEdge(Graph G,Edge e):将 e 插入 G
void DFS(Graph G,Vertex v):从顶点 v 出发深度优先遍历图 G
void BFS(Graph G,Vertex v):从顶点 v 出发宽度优先遍历图 G

2.图的存储结构表示

2.1邻接矩阵表示

邻接矩阵 G[N][N]——N 个顶点从 0 到 N-1 编号

存在边<vi,vj>,则G[i][j]=1,否则为0

特征
对角线元素全 0
关于对角线对称

优点
直观、简单、好理解
方便检查任意一对顶点间是否存在边
方便找任一顶点的所有邻接点
方便计算任一顶点的度
无向图:对应行(或列)非 0 元素的个数
有向图:对应行非 0 元素的个数是出度;对应列非 0 元素的个数是入度

缺点
浪费空间——存稀疏图
浪费时间——统计稀疏图的边

代码实现

#include<stdio.h>
#include<stdlib.h>
#define MaxVertexNum 100
typedef int weightType;
typedef int Vertex;
typedef int DataType;
typedef struct GNode *ptrToGNode;
struct GNode{   // 图
    int Nv;   // 顶点数
    int Ne;   // 边数
    weightType G[MaxVertexNum][MaxVertexNum];
    DataType Data[MaxVertexNum]; // 存顶点的数据
};
typedef ptrToGNode MGraph;
typedef struct ENode *ptrToENode;
struct ENode{  // 边
    Vertex V1,V2;    // 有向边<V1,V2>
    weightType Weight;  // 权重
};
typedef ptrToENode Edge;

// 初始化图
MGraph Create(int VertexNum){
    Vertex v,w;
    MGraph Graph;

    Graph = (MGraph)malloc(sizeof(struct GNode));
    Graph->Nv = VertexNum;
    Graph->Ne = 0;

    for(v=0;v<VertexNum;v++)
        for(w=0;w<VertexNum;w++)
            Graph->G[v][w] = 0;
    return Graph;
}

// 插入边
MGraph Insert(MGraph Graph,Edge E){

    // 插入边 <V1,V2>
    Graph->G[E->V1][E->V2] = E->Weight;

    // 如果是无向图,还需要插入边 <V2,V1>
    Graph->G[E->V2][E->V1] = E->Weight;

} 

// 建图
MGraph BuildGraph(){
    MGraph Graph;
    Edge E;
    Vertex V;
    int Nv,i;
    scanf("%d",&Nv);   // 读入顶点数
    Graph = Create(Nv);
    scanf("%d",&(Graph->Ne));  // 读入边数
    if(Graph->Ne != 0){
        E = (Edge)malloc(sizeof(struct ENode));
        for(i=0;i<Graph->Ne;i++){
            scanf("%d %d %d",&E->V1,&E->V2,&E->Weight);  // 读入每个边的数据
            Insert(Graph,E);
        }
    }
    return Graph;
}

// 遍历图
void print(MGraph Graph){
    Vertex v,w;
    for(v=0;v<Graph->Nv;v++){
        for(w=0;w<Graph->Nv;w++)
            printf("%d ",Graph->G[v][w]);
        printf("\n");
    }
} 

int main(){
    MGraph Graph;
    Graph = BuildGraph();
    print(Graph);
    return 0;
}

2.2邻接表实现

特点:

  • 方便找任一顶点的所有邻接顶点
  • 节省稀疏图的空间
    • 需要 N 个头指针 + 2E 个结点(每个结点至少 2 个域)
  • 对于是否方便计算任一顶点的度
    • 无向图:方便
    • 有向图:只能计算出度
  • 不方便检查任意一对顶点间是否存在边

代码实现

#include<stdio.h>
#include<stdlib.h>
#define MaxVertexNum 100
typedef int Vertex;
typedef int DataType;
typedef int weightType;  

typedef struct ENode *ptrToENode;
struct ENode{  // 边
    Vertex V1,V2;    // 有向边<V1,V2>
    weightType Weight;  // 权重
};
typedef ptrToENode Edge;

typedef struct AdjVNode *ptrToAdjVNode;
struct AdjVNode{  // 邻接表内元素
    Vertex AdjV;  // 邻接点下标
    weightType Weight;  // 权值
    ptrToAdjVNode Next;  // 下一个
};

typedef struct VNode{  // 邻接表头
    ptrToAdjVNode FirstEdge;  // 存每个顶点指针
    DataType Data;  // 顶点数据
}AdjList[MaxVertexNum];

typedef struct GNode *ptrToGNode;
struct GNode{  // 图
    int Nv;  // 顶点
    int Ne;  // 边数
    AdjList G; // 邻接表
};
typedef ptrToGNode LGraph;

// 初始化
LGraph create(int VertexNum){
    Vertex v,w;
    LGraph Graph;

    Graph = (LGraph)malloc(sizeof(struct GNode));
    Graph->Nv = VertexNum;  // 初始化边
    Graph->Ne = 0;   // 初始化点

    // 每条边的 FirstEdge 指向 NULL
    for(v=0;v<Graph->Nv;v++)
        Graph->G[v].FirstEdge = NULL;
    return Graph;
}

// 插入一条边到邻接表的顶点指针之后
void InsertEdge(LGraph Graph,Edge E){
    ptrToAdjVNode newNode; 

    /**************** 插入边<V1,V2> ******************/
    // 为 V2 建立新的结点
    newNode = (ptrToAdjVNode)malloc(sizeof(struct AdjVNode));
    newNode->AdjV = E->V2;
    newNode->Weight = E->Weight;

    // 将 V2 插入到邻接表头
    newNode->Next = Graph->G[E->V1].FirstEdge;
    Graph->G[E->V1].FirstEdge = newNode;

    /*************** 若为无向图,插入边<V2,V1> *************/
    newNode = (ptrToAdjVNode)malloc(sizeof(struct AdjVNode));
    newNode->AdjV = E->V1;
    newNode->Weight = E->Weight;

    newNode->Next = Graph->G[E->V2].FirstEdge;
    Graph->G[E->V2].FirstEdge = newNode;
} 

// 建图
LGraph BuildGraph(){
    LGraph Graph;
    Edge E;
    Vertex V;
    int Nv,i;
    scanf("%d",&Nv);
    Graph = create(Nv);
    scanf("%d",&(Graph->Ne));
    if(Graph->Ne != 0){
        for(i=0;i<Graph->Ne;i++){
            E = (Edge)malloc(sizeof(struct ENode));
            scanf("%d %d %d",&E->V1,&E->V2,&E->Weight);
            InsertEdge(Graph,E);
        }
    }
    return Graph;
} 

// 打印
void print(LGraph Graph){
    Vertex v;
    ptrToAdjVNode tmp;
    for(v=0;v<Graph->Nv;v++){
        tmp = Graph->G[v].FirstEdge;
        printf("%d ",v);
        while(tmp){
            printf("%d ",tmp->AdjV);
            tmp = tmp->Next;
        }
        printf("\n");
    }
}

int main(){
    LGraph Graph;
    Graph = BuildGraph();
    print(Graph);
    return 0;
}

3.图的遍历

3.1深度优先搜索DFS

void DFS ( Vertex V ){
    visited[ V ] = true;
    for ( V 的每个邻接点 W )
        if( !visited[ W ])
            DFS( W );
}

3.2广度优先搜索BFS

void BFS( Vertex V ){
    queue<Vertex> q;
    visited[V] = true;
    q.push(V);
    while(!q.empty()){
        V = q.front(); q.pop();
        for( V 的每个邻接点 W ){
            if( !visited[ W ]){
                visited[W] = true;
                q.push(W);
            }
        }
    }
}

原文地址:https://www.cnblogs.com/ericling/p/11917034.html

时间: 2024-08-26 00:35:03

【algo&ds】5.图及其存储结构、遍历的相关文章

(转)数据结构之图(存储结构、遍历)

一.图的存储结构 1.1 邻接矩阵 图的邻接矩阵存储方式是用两个数组来表示图.一个一维数组存储图中顶点信息,一个二维数组(邻接矩阵)存储图中的边或弧的信息. 设图G有n个顶点,则邻接矩阵是一个n*n的方阵,定义为: 看一个实例,下图左就是一个无向图. 从上面可以看出,无向图的边数组是一个对称矩阵.所谓对称矩阵就是n阶矩阵的元满足aij = aji.即从矩阵的左上角到右下角的主对角线为轴,右上角的元和左下角相对应的元全都是相等的. 从这个矩阵中,很容易知道图中的信息. (1)要判断任意两顶点是否有

图的存储结构及遍历

一.图的存储结构 1.1 邻接矩阵 图的邻接矩阵存储方式是用两个数组来表示图.一个一维数组存储图中顶点信息,一个二维数组(邻接矩阵)存储图中的边或弧的信息. 设图G有n个顶点,则邻接矩阵是一个n*n的方阵,定义为: 看一个实例,下图左就是一个无向图. 从上面可以看出,无向图的边数组是一个对称矩阵.所谓对称矩阵就是n阶矩阵的元满足aij = aji.即从矩阵的左上角到右下角的主对角线为轴,右上角的元和左下角相对应的元全都是相等的. 从这个矩阵中,很容易知道图中的信息. (1)要判断任意两顶点是否有

java 数据结构 图中使用的一些常用算法 图的存储结构 邻接矩阵:图的邻接矩阵存储方式是用两个数组来标示图。一个一位数组存储图顶点的信息,一个二维数组(称为邻接矩阵)存储图中边或者弧的信息。 设图G有n个顶点,则邻接矩阵是一个n*n的方阵,定义为: 实例如下,左图是一个无向图。右图是邻接矩阵表示:

以下内容主要来自大话数据结构之中,部分内容参考互联网中其他前辈的博客. 图的定义 图是由顶点的有穷非空集合和顶点之间边的集合组成,通过表示为G(V,E),其中,G标示一个图,V是图G中顶点的集合,E是图G中边的集合. 无边图:若顶点Vi到Vj之间的边没有方向,则称这条边为无项边(Edge),用序偶对(Vi,Vj)标示. 对于下图无向图G1来说,G1=(V1, {E1}),其中顶点集合V1={A,B,C,D}:边集合E1={(A,B),(B,C),(C,D),(D,A),(A,C)}: 有向图:若

数据结构 - 图的存储结构

图的抽象数据类型定义 图是一种数据结构,加上一组基本操作就构成了图的抽象数据类型. 图的抽象数据类型定义如下: ADT Graph{ 数据对象V:具有相同特性的数据元素的集合,称为顶点集. 数据关系R:R={VR} VR={<v,w>|<v,w>| v,w?V∧p(v,w) ,<v,w>表示 从v到w的弧,P(v,w)定义了弧<v,w>的信息 } 基本操作P: Create_Graph() : 图的创建操作. 初始条件:无. 操作结果:生成一个没有顶点的空图

数据结构算法之图的存储与遍历(Java)

一:图的分类 1:无向图 即两个顶点之间没有明确的指向关系,只有一条边相连,例如,A顶点和B顶点之间可以表示为 <A, B> 也可以表示为<B, A>,如下所示 2:有向图 顶点之间是有方向性的,例如A和B顶点之间,A指向了B,B也指向了A,两者是不同的,如果给边赋予权重,那么这种异同便更加显著了 =============================================================================================

数据结构(五)图---图的存储结构5种

一:图的抽象数据类型 ADT 图(Graph) Data 顶点的有穷非空集合和边的集合 Operation CreateGraph(*G,V,VR):按照顶点集V和边弧集VR的定义构造图G DestroyGraph(*G):图G存在则销毁 LocateVex(G,u):若图G中存在顶点u,则返回图中位置 GetVex(G,v):返回图中顶点v的值 PutVex(G,v,value):将图G中顶点v赋值给value FirstAdjVex(G,*v):返回顶点v的一个邻接顶点,若顶点在G中无邻接顶

数据结构之图(一)图的存储结构

图的存储结构相对于线性表和树来说更为复杂,因为图中的顶点具有相对概念,没有固定的位置.那我们怎么存储图的数据结构呢?我们知道,图是由(V, E)来表示的,对于无向图来说,其中 V = (v0, v1, ... , vn),E = { (vi,vj) (0 <=  i, j <=  n且i 不等于j)},对于有向图,E = { < vi,vj > (0 <=  i, j <=  n且i 不等于j)}.V是顶点的集合,E是边的集合.所以我们只要把顶点和边的集合储存起来,那么

《大话数据结构》笔记(7-2)--图:存储结构

第七章  图 图的存储结构 图不能用简单的顺序存储结构来表示. 而多重链表的方式,即以一个数据域和多个指针域组成的结点表示图中的一个顶点,尽管可以实现图结构,但是会有问题,比如若各个顶点的度数相差很大,按度数最大的顶点设计结点结构会造成很多存储单元的浪费,而若按每个顶点自己的度数设计不同的顶点结构,又带来操作的不便. 对于图来说,如何对它实现物理存储是个难题.图有以下五种不同的存储结构. 邻接矩阵 图的邻接矩阵(Adjacency Matrix)存储方式使用过两个数组来表示图.一个一维数组存储图

图的存储结构(邻接矩阵)

图的存储结构(邻接矩阵) 让编程改变世界 Change the world by program 图的存储结构 图的存储结构相比较线性表与树来说就复杂很多. 我们回顾下,对于线性表来说,是一对一的关系,所以用数组或者链表均可简单存放.树结构是一对多的关系,所以我们要将数组和链表的特性结合在一起才能更好的存放. 那么我们的图,是多对多的情况,另外图上的任何一个顶点都可以被看作是第一个顶点,任一顶点的邻接点之间也不存在次序关系. 我们仔细观察以下几张图,然后深刻领悟一下: 因为任意两个顶点之间都可能