图总结

1.思维导图

image

2.重要概念的笔记

(1)邻接矩阵

顶点ii的出度:第ii行1的个数。顶点ii的入度,第ii列1的个数。

typedef enum {DG, DN, UNG, UDN} GraphKind;//有向图,有向网,无向图,无向网
typedef struct ArcCell {
    VRType adj;//无权图,用0,1表示;带权图,用权值类型表示
    InfoType *info;//弧相关信息的指针
}ArcCell, AdjMatrix[maxn][maxn];
typedef struct {
    VertexType vexs[maxn];//顶点信息
    AdjMatrix arcs;//建立邻接矩阵
    int vexnum, arcnum;//图的当前顶点数和弧数
    GraphKind kind;
}MGraph;

(2)无向图的邻接表

typedef struct ArcNode {//一般结点
    int adjvex;//该弧所指向的顶点的位置
    struct ArcNode *nextarc;//链域,指向下一条边或者弧
}ArcNode;
typedef struct tnode{//表头结点
    int vexdata;//存放顶点信息
    ArcNode *firstarc;//指向第一个邻接点
}VNode, ADjList[maxn];
typedef struct{//最终建立邻接表
    ADjList vertices;
    int vexnum, arcnum;
    int kind;
}ALGraph;

(3)有向图的十字链表表示法

typedef struct ArcBox{//建立弧结点
    int tailvex, headvex;//弧头、弧尾在表头数组中位置
    struct arcnode *hlink;//指向弧头相同的下一条弧
    struct arcnode *tlink;//指向弧尾相同的下一条弧
}ArcBox;
typedef struct VexNode{//顶点结点
    VertexType data;
    ArcBox *firstin;//指向以该顶点为弧头的第1个弧结点
    ArcBox *firstout;//指向以该顶点为弧尾的第1个弧结点
}VexNode;
VexNode OLGraph[M];

(4)无向图的邻接多重链表表示法

typedef struct node {//弧结点
    VisitIf mark;//标志域,记录是否已经搜索过
    int ivex, ijex;//该边依附的两个顶点在表头数组中位置
    struct EBox *ilink, *jlink;//分别指向依附于ivex和ijex的下一条边
}EBox;
typedef struct VexBox{//顶点结点
    VertexType data;//存与顶点有关的信息
    EBox *firstedge;//指向第一条依附于该顶点的边
} VexBox;
VexBox AMLGraph[M];

从图的某顶点v出发,进行深度优先遍历:

访问顶点v
对于v的所有邻接点w1、w2、w3…w1、w2、w3… ,若wiwi没有被访问,则从wiwi出发进行深度优先遍历。
由于没有规定访问邻接点的顺序,所以深度优先序列不唯一。

void DFSTraverse(Graph G, void(*visit)(VertexType e))//对G做深度优先遍历
{
    for(v = 0; v < G.vexnum; v++)
        visited[v] = flase;
    for(v = 0; v < G.vexnum; v++){
        //注意:若图为非连通图,只有这样才能完全深度优先遍历;若为连通图,则可以用一个DFS函数
        if(!visited[v])
            DFS(G, v, Visit);
    }
}
void DFS(Graph G, int v, void(*Visit)(VertexType e))//对每个顶点做深度优先遍历
{
    visited[v] = true;
    Visit(v);
    for(w = FirstAdjVex(G, v); w >= 0; w = NextAdjVex(G, v, w)){
        if(!visited[w])
            DFS(G, w, Visit);
    }
}

时间复杂度:
若图为邻接表表示为O(n+e)O(n+e),若为邻接矩阵表示为O(n+n2)=O(n2)O(n+n2)=O(n2) 。

从图中某顶点v出发:

访问顶点v
访问顶点v所有未被访问的邻接点、w1、w2…wn、w1、w2…wn,并用栈或队列存储
依次取出邻接点进行广度优先遍历
深度优先遍历是回溯算法,广度优先遍历时一种分层的顺序搜索过程,不是递归。

void BFSTraverse(Graph G, void(*visit)(VertexType))//对G做广度优先遍历
{
    for(v = 0; v < G.vexnum; v++)
        visited[v] = false;
    for(v = 0; v < G.vexnum; v++){
        if(!visited[v])
            BFS(G, v, Visit);
    }
}
void BFS(Graph G, int v, void(*Visit)(VertexType e))
{
    initqueue(Q);//在把每一个结点放到队列中前,先把它标记和处理
    visited[v] = true;
    Visit(v);
    push(v);
    while(!empty(Q)){
        v = pop(Q);
        for(w = FirstAdjvex(G, v); w >= 0; w = NextAdjvex(G, v, w)){
            if(!visited[w]){
                visited[w] = true;
                Visit(w);
                push(w);
            }
        }
    }  
}

(7)求一条从顶点v到顶点s的简单路径

方法:从v开始深度优先遍历,直到找到s。在对v深度优先遍历的过程中,首先将v添加到路径中,判断v的值是否为s,如果为s,返回真。如果不为s,则对v的邻接点进行递归,直到找到s为止。如果没找到,则将v从路径中删除,返回失败。

Status _DFSearch(Graph G, int v, VertexType s, SqList &Path)//递归函数
{
    visited[v] = true;
    ListAppend(v, Path);//将v添加到路径中
    for(w = FirstAdjvex(G, v); w >= 0; w = NextAdjvex(G, v, w)){//判断是否有路
        if(!visited[w]){
            if(_DFSearch(G, w, s, Path)) return true;
        }
    }
    ListDelete(v, Path);//将v从路径中删除
    return False;
}

(8)求两个顶点之间的一条路径长度最短的路径

由于广度优先搜索有路径渐增的性质,所以使用广度优先搜索,搜索到终点的路径即为最短路径。应该会需要用一个数组记录每个节点的父节点,便于还原路径。

(9)Prim算法:将顶点归并,与边数无关,适合稠密网 O(n2)O(n2)

设G=(V,GE)为一个具有n个顶点的连通网络,T=(U,TE)为构造的生成树。

初始时,U={u0u0},TE为空集
在所有的u∈Uu∈U且v∈V−Uv∈V−U的边(u,v)中选择一条权值最小的边(u,v)
将(u,v)加入TE,同时将v加入U
重复23步,直到U=V为止

//辅助数组closege[],对当前V-U集中的每个顶点,记录与顶点集U中顶点相连接的代价最小的边。
struct{
    VertexType Adjvex;//顶点v到子集U中权最小边关联的顶点u
    VRType lowcost;//顶点v到子集U中权最小边的权值
}closedge[maxn];
void MiniSpanTree_P(MGraph G, VertexType u)//从u出发构造G的最小生成树
{
    k = LocateVex(G, u);
    for(j = 0; j < G.vexnum; ++j){//辅助数组初始化
        if(j != k)
            closedge[j] = {u, G.arcs[k][j]};//各点到u的距离
    }
    for(i = 0; i < G.vexnum; i++){
        k = minimum(closedge);//选择距离最小且不为0的作为k
        printf(closedge[k].Adjvex, G.vexs[k]);//输出k对应的顶点和新加入的边权值
        closedge[k].lowcost = 0;//加入新的点
        for(j = 0; j < G.vexnum; j++){
            if(G.arcs[k][j] < closedge[j].lowcost)//更新V-U中点到U中点的距离
                closedge[j] = {G.vexs[k], G.arcs[k][j]};
        }
    }
}

(10)Kruskal算法:将边归并,适用于求稀疏网的最小生成树 O(eloge)O(eloge)

初始时最小生成树值包含图的n个顶点,每个顶点为一棵子树
选取权值较小且所关联的两个顶点不再同一连通分量的边,将此边加入最小生成树中
重复第二步n-1次,即得到包含n个顶点和n-1个条边的最小生成树

int find(int n)//找到点的树根
{
    while(n != find[n])
        n = find[n];
    return n;
}
void join(int n, int m)//将两棵树并起来
{
    int fn = find(n);//一定要找到两棵树的树根再并到一起
    int fm = find(m);
    find[fn] = fm;
}
int kruskal(int n, int m)
{
    int num = 0;
    for(i = 1; i <= n; i++)//初始化寻根函数
        find[i] = i;
    for(i = 0; i < m; i++){//此时A中边已经全部从小到大排好顺序
        if(find(A[i].u) != find(A[i].v)){//如果边的顶点不属于一棵树
            join(u, v);//并到一棵树
            cost += A[i].w;
            num++;//通过最后num是否达到n-1来判断是否有最小生成树
        }
    }
}

3.疑难问题及解决方案

(1)Dijkstra算法求单源最短路径

方法:S=v0S=v0,其余顶点T=其余顶点u其余顶点T=其余顶点u
从T中选择一个未标记的权值最小的顶点w,将w加入S,并对w进行标记。若所有点都被标记,则结束。
考察T中的所有顶点,对路径进行松弛
void SPath_Dij(MGraph G, int u0, int dis[], int Path[])
{
    for(i = 0; i < G.vexnum; i++){
        visited[i] = flase;
        dis[i] = G.arcs[u0][i];
        if(dis[i] < INFINITY) Path[i] = u0;//path用来记住前一个结点
        else Path[i] = -1;
    }
    dis[u0] = 0; visited[u0] = true;
    for(i = 0; i < G.vexnum; i++){
        k = selectmin(dis);
        visited[k] = true;//注意k此处应该被标记
        for(j = 0; j < G.vexnum; j++){
            if(dis[j] > dis[k] + G.arcs[k][j] && !visited[j]){
                //选出的j不应该被访问过
                dis[j] = dis[k] + G.arcs[k][j];
                Path[j] = k;//注意对Path进行更新
            }
        }
    }
}

(2)Floyd算法求每对顶点间最短路径

当然也可以重复执行Dijkstra算法n次。
弗洛伊德算法:从vivi到vjvj所有可能存在的路径中,选出一条长度最短的路径。
方法:
初始设置一个n阶方阵,令其对角线元素为0,若存在弧,则对应元素为权值,否则为无穷。
逐步试着在原直接路径中增加中间顶点,若加入中间点后路径变短,则修改。否则,维持原值。
所有顶点试探完毕,算法结束。

//核心算法类似于动态规划
for(i = 0; i < G.vertex; i++){
    for(j = 0; j < G.vertex; j++){
        for(k = 0; k < G.vertex; k++){
            if(A[i][j] > A[i][k] + A[k][j]){
                A[i][j] = A[i][k] + A[k][j];
                path[i][j] = k;//记录分段点
            }
        }
    }
}
posted @ 2021-05-31 19:53  Godzi11a  阅读(55)  评论(0)    收藏  举报