数据结构第七章图

1.图:图G是由两个集合V(G)和E(G)组成的,记为G=(V,E);其中V(G)是顶点的非空有限集;E(G)是边的有限集合,边是顶点的有序对或无序对。
有向图中E(G)是有向边的有限集合,记为 < v,w >
无向图中V(G)是无向边的有限集合,记为(v,w)

性质:无向图最多有n(n-1)/2条边;有向图最多有n(n-1)条边
n个顶点的有向图有n(n-1)条边,则此图称为有向完全图
n个定点的无向图有n(n-1)/2条边,则此图称为无向完全图


2.
邻接点:若(vi,vj)是一条无向边,则成vi和vj互为邻接点
关联:vi和vj互为邻接点,称边(vi,vj)关联于顶点vi,vj
顶点的度 :一个顶点v的度是与它相关联的边的条数。记作TD(v)。
入度:顶点 v 的入度是以 v 为终点的有向边的条数, 记作 ID(v)。
出度:顶点 v 的出度是以 v 为始点的有向边的条数, 记作 OD(v)。
边数与度的关系:

  • ∑TD(vi)=2e ( i=1, 2, …, n ,e为图的边数)

性质:有向图各顶点入度之和等于出度之和,等于边数
子图——如果图G(V,E)和图G’(V’,E’),满足:V’属于V,E’属于E, 则称G’为G的子图
路径:在图 G=(V, E) 中, 若从顶点 vi 出发, 沿一些边经过一些顶点 vp1, vp2, …, vpm,到达顶点vj。则称顶点序列 (vi vp1 vp2 … vpm vj) 为从顶点vi 到顶点 vj 的路径。它经过的边(vi, vp1)、(vp1, vp2)、…、(vpm, vj) 应是属于E的边。
路径长度:非带权图的路径长度是指此路径上边的条数。带权图的路径长度是指路径上各边的权之和。
简单路径 :若路径上各顶点 v1,v2,…,vm 均不互相重复, 则称这样的路径为简单路径。
回路:若路径上第一个顶点 v1 与最后一个顶点vm 重合, 则称这样的路径为回路或环。
简单回路:在一个回路中,若除第一个与最后一个顶点外,其余顶点不重复出现的回路称为简单回路(简单环)。
连通图 :在无向图中, 若从顶点v1到顶点v2有路径, 则称顶点v1与v2是连通的。如果图中任意一对顶点都是连通的, 则称此图是连通图。
连通分量:非连通图的极大连通子图叫做连通分量。
“极大”的含义:指的是对子图再增加图G中的其它顶点,子图就不再连通。
考点:
(1)n个顶点的无向图最少需要多少条边,才能保证连通。答案:n-1条
(2)要保证n个顶点的无向图G在任何情况下都是连通的,至少需要多少条边?
答案:(n-1)(n-2)/2+1
强连通图与强连通分量:在有向图中, 若对于每一对顶点vi和vj, 都存在一条从vi到vj和从vj到vi的路径, 则称此图是强连通图。非强连通图的极大强连通子图叫做强连通分量。
考点:n个顶点的有向图是强连通图,至少需要多少条边?答案:n条(回路)
:与图的边或弧相关的数叫权
网(络):带权的图叫网
3.一个连通图的生成树是一个极小连通子图,它含有图中全部顶点,但只有足以构成一棵树的n-1条边。
一棵有n个顶点的生成树有且仅有n-1条边。
这里写图片描述——>这里写图片描述
4.无回路的图称为树或自由树或无根树
有向树:只有一个顶点的入度为0,其余顶点的入度为1的有向图。(有向树是弱连通的)


5.图的存储结构:

  • 邻接矩阵(数组表示)

图的任意两个结点(顶点)之间都可能有关系,因此用二维数组来表示;
(1)二维数组中元素aij= 0表示vi和vj不邻接; aij = 1表示vi和vj邻接。
(2)对于网,可以用aij表示权。若vi和vj不邻接,则可以用无限大∞表示权。

typedef int EdgeType;
typedef char VertexType;
typedef struct{
    VertexType vexs[MaxNode];
    EdgeType arcs[MaxNode][MaxNode];
    int vexnum,arcnum;
}MGraph;
int find(MGraph G,VertexType c){
    for(int i=0;i<G.vexnum;i++){
        if(G.vexs[i]==c){
            return i;
        }
    }
    return -1;
}
void CreateGraph(MGraph &G){
    char v1,v2;
    scanf("%d%d",&G.vexnum,&G.arcnum);
    getchar();
    for(int i=0;i<G.vexnum;i++){
            scanf("%c",&G.vexs[i]);
            getchar();
    }
    memset(G.arcs,0,sizeof(G.arcs));
    for(int i=0;i<G.arcnum;i++){
        scanf("%c",&v1);
        getchar();
        scanf("%c",&v2);
        getchar();
        int k=find(G,v1);
        int t=find(G,v2);
        G.arcs[k][t]=G.arcs[t][k]=1;
    }
    for(int i=0;i<G.vexnum;i++){
        for(int j=0;j<G.vexnum;j++)
            printf("%d ",G.arcs[i][j]);
        printf("\n");
    }
}

对于无向图
邻接矩阵一定是一个对称矩阵,可压缩存储;有n个顶点的无向图需存储空间为n(n+1)/2
行(列)非零元素个数,表示度
对于有向图
矩阵不一定是一个对称矩阵,有n个顶点的有向图需存储空间为n²
行非零元素个数,表示出度
列非零元素个数,表示入度
邻接矩阵的存储空间只和顶点个数有关,和边数无关
邻接矩阵存储优点
容易实现图的操作,如:求某顶点的度、判断顶点之间是否有边(弧)、找顶点的邻接点等等。
邻接矩阵存储缺点
n个顶点需要n*n个单元存储边(弧);空间效率为O(n2)。 对稀疏图而言尤其浪费空间

  • 邻接表
    对图中每个顶点建立一个邻接关系的单链表,并将其表头指针用向量(一维数组)存储,该结构称为邻接表。
    无向图的第i个链表将图中与顶点vi相邻接的所有顶点链接起来。
    有向图的第i个链表,链接了以顶点vi为弧尾(射出)的所有顶点。
typedef int EdgeType;
typedef int VertexType;
typedef struct ArcNode{ //边(弧)结点的类型定义
    int  adjvex;   //边(弧)的另一顶点的在数组中的位置
    ArcNode *nextarc;//指向下一边(弧)结点的指针
}ArcNode,*ArcLink;

typedef struct Vnode{//顶点结点及其数组的类型定义
    VertexType data;    //顶点信息
    ArcNode * firstarc; //指向关联该顶点的边(弧)链表
} Vnode, AdjList[MaxNode];
typedef struct {
    AdjList  vertices;
    int  vexnum, arcnum;    //图的当前顶点数和弧数
} ALGraph;
void CreateAlGraph(ALGraph &G){
    int i,j,k;
    ArcLink s;
    scanf("%d%d",&G.vexnum,&G.arcnum);
    for(i=0;i<G.vexnum;i++){
        scanf("%d",&G.vertices[i].data);
        G.vertices[i].firstarc=NULL;
    }
    for(i=0;i<G.arcnum;i++){
        scanf("%d%d",&j,&k);
        s=(ArcLink)malloc(sizeof(ArcNode));
        s->adjvex=k;
        s->nextarc=G.vertices[j].firstarc;
        G.vertices[j].firstarc=s;
    }
}
  • 有向图的十字链表表示法
typedef struct ArcBox{
   int tailvex,headvex;//弧头和弧尾在表头数组中的位置
   struct ArcBox *hlink;//指向弧头相同的下一条弧
   struct ArcBox *tlink;//指向弧尾相同的下一条弧
}ArcBox,*Arclink;//弧结点
typedef struct VexNode{
   int data;
   Arclink fistin;//指向以该顶点为弧头的第一个弧结点
   Arclink fistout;//指向以该顶点为弧尾的第一个弧结点
}VexNode;//顶点结点
typedef struct{
   VexNode xlist[MaxNode];
   int vexnum,arcnum;
}OLGraph;
void CreateOLGraph(OLGraph &G){
    Arclink p;
    int k,j;
    scanf("%d%d",&G.vexnum,&G.arcnum);
    for(int i=0;i<G.vexnum;i++){
        scanf("%d",&G.xlist[i].data);
        G.xlist[i].firstin=NULL;
        G.xlist[i].firstout=NULL;
    }
    int i,j;
    for(int k=0;k<G.arcnum;k++){
        scanf("%d%d",&i,&j);
        p=(Arclink)malloc(sizeof(ArcBox));
        p->headvex=j;
        p->tailvex=i;
        p->hlink=G.xlist[j].firstin;
        p->tlink=G.xlist[i].firstout;
        G.xlist[i].firstout=p;
        G.xlist[j].firstin=p;
    }
}

这里写图片描述

6.图的遍历
图的遍历是从图中某个顶点出发,按照某种方式系统地访问图中的所有顶点,使每个顶点仅被访问一次。
图的遍历通常有两种方法:深度优先搜索和广度优先搜索。它们对有向图和无向图都适用。
深度优先遍历(DFS)

typedef struct ArcNode
{
    int adjvex;
    struct ArcNode * nextarc;
}ArcNode,*ArcLink;
typedef struct Vnode
{
    int data;
    int isfirst;
    ArcLink firstarc;
}Vnode,AdjList[MaxSize];
typedef struct
{
    AdjList vertices;
    int vexnum,arcnum;
}ALGraph;
void DFS(ALGraph &G,int v)
{
    ArcLink p;
    G.vertices[v].isfirst=1;
    printf("%d ",G.vertices[v].data);
    p=G.vertices[v].firstarc;
    while(p){
        if(G.vertices[p->adjvex].isfirst==0)
        DFS(G,p->adjvex);
        p=p->nextarc;
    }
}
void WholeDFS(ALGraph G)
{
    for(int i=0;i<G.vexnum;i++){
        if(G.vertices[i].isfirst==0){
            DFS(G,i);
        }
    }
    printf("\n");
}

广度优先遍历(BFS)

void BFS(ALGraph &G)
{
    ArcLink p;
    bool isfirst[MaxSize];
    SqQueue Q;
    int t;
    InitQueue(Q);
    for(int i=0;i<G.vexnum;i++)
        isfirst[i]=false;
    for(int i=0;i<G.vexnum;i++){
        if(!isfirst[i]){
            printf("%d ",G.vertices[i].data);
            EnQueue(Q,i);
            isfirst[i]=true;
            while(!Empty(Q)){
                DnQueue(Q,t);
                p=G.vertices[t].firstarc;
                while(p){
                    if(!isfirst[p->adjvex]){
                        printf("%d ",G.vertices[p->adjvex].data);
                        isfirst[p->adjvex]=true;
                        EnQueue(Q,p->adjvex);
                    }
                    p=p->nextarc;
                }
            }
        }
    }
}

Prime算法从联通网中找最小生成树(邻接矩阵)

typedef struct
{
    int weight[MaxSize][MaxSize];
    int vexnum,arcnum;
}ArlGraph;
typedef struct
{
    int adjvex;//边依附于U中顶点
    int lowcost;//该边的权值
}closedge[MaxSize];
typedef struct
{
    int vex1,vex2;
    int weight;
}MSTEdge,*MSTLink;//存储最小生成树的边
void Prime(ArlGraph &G,int u,MSTLink &TE)
{
    closedge ce;
    int min,t;
    for(int i=0;i<G.vexnum;i++){
        ce[i].adjvex=u;
        ce[i].lowcost=G.weight[i][u];
    }
    ce[u].lowcost=0;
    TE=(MSTLink)malloc(sizeof(MSTEdge)*(G.vexnum-1));
    for(int j=0;j<G.vexnum-1;j++){
        min=MaxSize;
        for(int k=0;k<G.vexnum;k++){
            if(ce[k].lowcost!=0&&ce[k].lowcost<min){
                min=ce[k].lowcost;
                t=k;
            }
        }
        TE[j].vex1=ce[t].adjvex;
        TE[j].vex2=t;
        TE[j].weight=min;
        ce[t].lowcost=0;
        for(int i=0;i<G.vexnum;i++){
            if(G.weight[i][t]<ce[i].lowcost){
                ce[i].lowcost=G.weight[i][t];
                ce[i].adjvex=t;
            }
        }
    }
}

拓扑排序(检验图中是否存在环,若输出个数与顶点个数相同则无环)
邻接表实现,可用栈,可用队列

int Toplogic_Sort(ALGraph G)
{
    SqStack S;S.top=-1;
    int t,count=0;
    ArcLink p;
    int indegree[MaxSize];
    getIndegree(G,indegree);
    for(int i=0;i<G.vexnum;i++)
        if(indegree[i]==0)
            S.data[++S.top]=i;
            while(S.top!=-1){
                t=S.data[S.top--];
                printf("%d ",G.vertices[t].data);
                count++;
                p=G.vertices[t].firstarc;
                while(p){
                    indegree[p->adjvex]--;
                    if(indegree[p->adjvex]==0)
                        S.data[++S.top]=p->adjvex;
                    p=p->nextarc;
                }
            }
    if(count<G.vexnum)
        return -1;
    return 0;
}

关键路径

int ve[MaxSize],vl[MaxSize];
int ToplogicOrder(ALGraph G,SqStack &T)
{
    SqStack S;S.top=-1;
    T.top=-1;
    int t,count=0;
    ArcLink s;
    int indegree[MaxSize];
    getIndegree(G,indegree);
    for(int i=0;i<G.vexnum;i++){
        if(indegree[i]==0)
         Push(S,i);
        ve[i]=0;
    }
    while(!IsEmpty(S)){
        Pop(S,t);
        printf("%d ",G.vertices[t].data);
        Push(T,t);
        count++;
        s=G.vertices[t].firstarc;
        while(s){
            if(!--indegree[s->adjvex])
                Push(S,s->adjvex);
            if(ve[t]+s->weight>ve[s->adjvex])
                ve[s->adjvex]=ve[t]+s->weight;
            s=s->nextarc;
        }
    }
    if(count<G.vexnum)
        return -1;
    return 0;
}
int criticalPath(ALGraph &G,SqStack &T)
{
    ArcLink p;
    int t,ee,el;
    char tag;
    if(ToplogicOrder(G,T)==-1)return -1;
    for(int i=0;i<G.vexnum;i++)
        vl[i]=ve[G.vexnum-1];
    while(!IsEmpty(T)){
        Pop(T,t);
        p=G.vertices[t].firstarc;
        while(p){
            if(vl[p->adjvex]-p->weight<vl[t])
                vl[t]=vl[p->adjvex]-p->weight;
            p=p->nextarc;
        }
    }
    for(int i=0;i<G.vexnum;i++){
        printf("\n%d %d",ve[i],vl[i]);
    }
    for(int j=0;j<G.vexnum;j++){
        for(p=G.vertices[j].firstarc;p;p=p->nextarc){
            ee=ve[j];el=vl[p->adjvex]-p->weight;
            tag=(ee==el)?'*':'X';
            printf("\n%d-%d=%c",j,p->adjvex,tag);
        }
    }
    return 0;
}

最短路径Dijkstra

int D[MaxSize];//从vs到vi的当前最短路径
int P[MaxSize];//从vs到其他顶点的最短路径
bool final[MaxSize];//标识一个顶点是否已加入S中
void Dijkstra(MGraph G,int v0)
{
    int min,v;
    for(int i=0;i<G.vexnum;i++){
        P[i]=v0;final[i]=false;
        D[i]=G.arcs[v0][i];
        D[v0]=0;final[v0]=true;
    }
    for(int i=1;i<G.vexnum;i++){
        min=MaxSize;
        for(int j=0;j<G.vexnum;j++){
            if(!final[j]&&D[j]<min){
                min=D[j];v=j;
            }
        }
        final[v]=true;
        for(int j=0;j<G.vexnum;j++){
            if(!final[j]&&(D[v]+G.arcs[v][j]<D[j])){
                D[j]=D[v]+G.arcs[v][j];
                P[j]=v;
            }
        }
    }
}
void ShowPath(int P[],int v0,int vt)
{
    if(v0==vt) return;
    else{
        printf("%d ",P[vt]);
        ShowPath(P,v0,P[vt]);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值