【数据结构】第六章——图(详解)

前言:
本系列是笔者暑假自学数据结构的笔记整理而来,共126页,3w+字。现在正式开学上课,补充老师所讲内容,并且仔细勘误,根据老师的教学进度分章节发布在CSDN上。

教材使用的是王红梅等所著的数据结构——从概念到C++实现(第三版)。
暑假自学时期主要参考的网课:
青岛大学-王卓
武汉大学-李春葆

隔了好久,补发。

定义与概念

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

图的存储结构

在这里插入图片描述
在这里插入图片描述

/*
    图的基本运算
    InitGraph(&g):图初始化
    ClearGraph(&g):销毁图
    DFS(G, V):从顶点v出发深度优先遍历
    BFS(G, v):从顶点v出发广度优先遍历
*/

#define MAXV 100//最大顶点个数
#define InfoType int
typedef struct
{
    int no;//顶点编号
    InfoType info;//顶点其他信息
}VertexType;

typedef struct //图的定义
{
    int edge[MAXV][MAXV];//邻接矩阵
    int n, e;//顶点数边数
    VertexType vexs[MAXV];//存放顶点信息
}MGraph;

在这里插入图片描述
在这里插入图片描述

//图的邻接表存储类型定义如下:
typedef struct ANode
{
    int adjvect;//该边的终点编号
    struct ANode *nextare;//指向下一条边的指针
    InfoType info;//该边的权值等信息
}ArcNode;

typedef struct Vnode
{
    VertexType data;//顶点信息
    ArcNode *firstarc;//指向第一条便
}VNode;

typedef struct 
{
    VNode adjlist[MAXV];//邻接表
    int n, e;//图中顶点数n和边数e
}ALGraph;

在这里插入图片描述
在这里插入图片描述

图的遍历

在这里插入图片描述

//DFS,时间复杂度O(n+e)
void DFS(ALGraph *G, int v)
{
    ArcNode *p;
    int w;
    int visited[MAXV];
    visited[v] = 1;//用visited数组表示节点是否被访问过
    cout << v;//输出被访问顶点的编号
    p = G->adjlist[v].firstarc;//p指向顶点v的第一条边的边头节点
    while (p != nullptr)
    {
        w = p->adjvect;
        if (visited[w] == 0)
        {
            DFS(G, w);//若w顶点未访问,递归访问它
        }
        p = p->nextare;//p指向顶点v的下一条边的边头节点
    }
}

在这里插入图片描述
在这里插入图片描述

//BFS,时间复杂度为O(n+e)
void BFS(ALGraph *G, int v)
{
    ArcNode *p;
    int w, i;
    int queue[MAXV], front = 0, rear = 0;//定义循环队列
    int visited[MAXV];
    for (int i = 0; i < G->n; i++)
    {
        visited[i] = 0;//访问标志数组初始化
    }
    cout << v;//输出被访问顶点的编号
    visited[v] = 1;//置已被访问顶点的编号
    rear = (rear + 1)%MAXV;
    queue[rear] = v;//v进队
    while (front != rear)//队列不空
    {
        front = (front + 1)%MAXV;
        w = queue[front];//出队并赋给w
        p = G->adjlist[w].firstarc;//找w的第一个邻接点
        while (p != nullptr)
        {
            if (visited[p->adjvect] == 0)
            {
                cout << p->adjvect;//访问之
                visited[p->adjvect] = 1;
                rear = (rear + 1)%MAXV;//相邻顶点进队
                queue[rear] = p->adjvect;
            }
            p = p->nextare;//找下一个邻接顶点
        }
    }
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

void DFS1(ALGraph *G)
{
    int i;
    for (int i = 0; i < G->n; i++)
    {
        if (visited[i] == 0)
        {
            DFS(G, i);
        }
    }
}

在这里插入图片描述

void BFS1(ALGraph *G)
{
    int i;
    for (int i = 0; i < G->n; i++)
    {
        if (visited[i] == 0)
        {
            BFS(G, i)
        }
    }
}

在这里插入图片描述

int visited[MAXV];
bool Connect(ALGraph *G)//判断无向图G的连通性
{
    int i;
    bool flag = true;
    for (int i = 0; i < G->n; i++)
    {
        visited[i] = 0;//visited[]数组置初值
    }
    DFS(G, 0);//从顶点0开始深度优先遍历
    for (int i = 0; i < G->n; i++)
    {
        if (visited[i] == 0)
        {
            flag = false;
            break;
        }
    }
    return flag;
}

在这里插入图片描述

void ExistPath(ALGraph *G, int u, int v, bool &has)
{
    //has表示u到v是否有路径
    int w;
    ArcNode *p;
    visited[u] = 1;//置已访问标记
    if (u == v)//找到一条路径
    {
        has = true;//置has为true并结束算法
        return;
    }
    p = G->adjlist[u].firstarc;//p指向顶点u的第一个相邻点
    while (p != nullptr)
    {
        w = p->adjvect;//w为顶点u的相邻顶点
        if (visited[w] == 0)//若w顶点未访问,递归访问它
        {
            ExistPath(G, w, v, has);
        }
        p = p->nextare;//p指向顶点u的下一个相邻点
    }
}

在这里插入图片描述

void FindaPath(ALGraph *G, int u, int v, int path[], int d)
{
    //d表示path中的路径长度,初值为-1
    int w, i;
    ArcNode *p;
    visited[u] = 1;
    d++;//路径长度d增1,顶点u加入到路径中
    path[d] = u;
    if (u == v)//找到一条路径后输出并返回
    {
        cout << "一条简单路径为:";
        for (int i = 0; i <= d; i++)
        {
            cout << path[i];
        }
        cout << '\n';
        return;//找到一条路径后返回
    }
    p = G->adjlist[u].firstarc;//p指向顶点u的第一个相邻点
    while (p != nullptr)
    {
        w = p->adjvect;//相邻点的编号为w
        if (visited[w] == 0)
        {
            FindaPath(G, w, v, path, d);
        }
        p = p->nextare;//p指向顶点u的下一个相邻点
    }
}

在这里插入图片描述

void PathAll(ALGraph *G, int u, int v, int l, int path[], int d)
{
    //d是到当前为止已走过的路径长度,调用时初值为-1
    int w, i;
    ArcNode *p;
    visited[u] = 1;//路径长度增1
    d++;
    path[d] = u;//将当前顶点添加到路径中
    if (u == v && d==l)//输出一条路径
    {
        for (int i = 0; i <= d; i++)
        {
            cout << path[i] << '\n';
        }
    }
    p = G->adjlist[u].firstarc;//p指向u的第一条边的边头节点
    while (p != nullptr)
    {
        w = p->adjvect;//w为u的邻接顶点
        if (visited[w] == 0)//若顶点未标记访问,则递归访问之
        {
            PathAll(G, w, v, l, path, d)
        }
        p = p->nextare;//找u的下一个邻接顶点
    }
    visited[u] = 0; //恢复环境,使该顶点可重新使用
}

在这里插入图片描述

typedef struct 
{
    int data;//顶点编号
    int parent;//前一个顶点的位置
}Quere;

void ShortPath(ALGraph *G, int u, int v)
{
    //输出从顶点u到顶点v的最短逆路径
    ArcNode *p;
    int w, i;
    Quere qu[MAXV];//定义非循环队列
    int front = -1, rear = -1;//队列的头、尾指针
    int visited[MAXV];
    for (int i = 0; i < G->n; i++)//访问标记置初值0
    {
        visited[i] = 0;
    }
    rear++;//顶点u进队
    qu[rear].data = u;
    qu[rear].parent = -1;
    visited[u] = 1;
    while (front != rear)//队不空时循环
    {
        front++;//出队顶点w
        w = qu[front].data;
        if (w == v)//输出逆路径
        {
            i = front;
            while (qu[i].parent != -1)
            {
                cout << qu[i].data;
                i = qu[i].parent;
            }
            cout << qu[i].data;
            break;
        }
        p = G->adjlist[w].firstarc;//找w的第一个邻接点
        while (p != nullptr)
        {
            if (visited[p->adjvect] == 0)
            {
                visited[p->adjvect] = 1;
                rear++;//将w的未访问过的邻接点进队
                qu[rear].data = p->adjvect;
                qu[rear].parent = front;
            }
            p = p->nextare;//找w的下一个邻接点
        }
    }
}

生成树

在这里插入图片描述

最小生成树

在这里插入图片描述
在这里插入图片描述

#define INF 32767
void Prim(MGraph g, int v)
{
    int lowcost[MAXV];
    int min;
    int closet[MAXV], i, j, k;
    for (int i = 0; i < g.n; i++)
    {
        lowcost[i] = g.edge[v][i];
        closet[i] = v;
    }
    for (int i = 1; i < g.n; i++)
    {
        min = INF;
        for (int j = 0; j < g.n; j++)
        {
            if (lowcast[j] != 0 && lowcost[j] < min)
            {
                min = lowcost[j];
                k = j;
            }
        }
        cout << closet[k] << k << min;
        lowcast[k] = 0;
        for (int j = 0; j < g.n; j++)
        {
            if (lowcast[j] != 0 && g.edges[k][j] < lowcast[j])
            {
                lowcost[j] = g.edge[k][j];
                closet[j] = k;
            }
        }
    }
}

在这里插入图片描述
在这里插入图片描述

typedef struct 
{
    int u;//边的起始顶点
    int v;//边的终止顶点
    int w;//边的权值
}Edge;

void Kruskal(MGraph g)
{
    int i, j, u1, v1, sn1, sn2, k;
    int vset[MAXV];
    Edge E[MAXV];//存放所在边
    k = 0;//E数组的下标从0开始计
    for (int i = 0; i < g.n; i++)//由g产生的边集E
    {
        for (int j = 0; j < g.n; j++)
        {
            if (g.edge[i][j] != 0 && g.edge[i][j] != INF)
            {
                E[k].u = i;
                E[k].v = j;
                E[k].w = g.edge[i][j];
                k++;
            }
            
        }
    }
    InsertSort(E, g, e);//用直接插入排序对E数组按权值递增排序
    for (int i = 0; i < g.n; i++)//初始化辅助数组
    {
        vset[i] = i;
        k = 1;//k表示当前构造生成树的第几条边
        j = 0;//E中边的下标,初值为0
        while (k < g.n)//生成的边数小于n时循环
        {
            u1 = E[j].u;//取一条边的头尾顶点
            v1 = E[j].v;
            sn1 = vset[u1];
            sn2 = vset[v1];//分别得到两个顶点所属的集合编号
            if (sn1 != sn2)//两顶点属于不同的集合
            {
                cout << u1 << v1 << E[j].w;
                k++;//生成边数增1
                for (int i = 0; i < g.n; i++)//两个集合统一编号
                {
                    if (vset[i] == sn2)//集合编号为sn2的改为sn1
                    {
                        vset[i] = sn1;
                    }
                }
            }
            j++;//扫描下一条边
        }
    }
}

在这里插入图片描述

最短路径

在这里插入图片描述

void Dijkstra(MGraph g, int v)
{
    int dist[MAXV], path[MAXV];
    int s[MAXV];
    int mindis, i, j, u;
    for (int i = 0; i < g.n; i++)
    {
        dist[i] = g.edge[v][i];//距离初始化
        s[i] = 0;//s[]置空
        if (g.edge[v][i] < INF)//路径初始化
        {
            path[i] = v;//顶点v到i有边时
        }
        else
        {
            path[i] = -1;//顶点v到i没边时
        }
    }
    s[v] = 1;//源点v放入s中
    for (int i = 0; i < g.n; i++)//循环n-1次
    {
        mindis = INF;
        for (int j = 0; j < g.n; j++)
        {
            if (s[j] == 0 && dist[j] < mindis)
            {
                u = j;
                mindis = dist[j];
            }
        }
        s[u] = 1;//顶点u放入s中
        for (int j = 0; j < g.n; j++)//修改不在s中的顶点的距离
        {
            if (s[j] == 0)
            {
                if (g.edge[u][j] < INF && dist[u] + g.edge[u][j] < dist[j])
                {
                    dist[j] = dist[u] + g.edge[u][j];
                    path[j] = u;
                }
            }
        }
    }
    Dispath(dist, path, s, g.n, v);//输出最短路径
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

void Floyd(MGraph g)//求每对顶点之间的最短路径
{
    int A[MAXV][MAXV];//建立数组A
    int path[MAXV][MAXV];//建立path数组
    int i, j, k;
    for (int i = 0; i < g.n; i++)
    {
        for (int j = 0; j < g.n; j++)
        {
            A[i][j] = g.edge[i][j];
            if (i != j && g.edge[i][j] < INF)
            {
                path[i][j] = i;//i和j顶点之间有一条边时
            }
            else
            {
                path[i][j] = -1;//i和j顶点之间没有一条边时
            }
            
        }
        
    }
    for(k = 0; k < g.n; k++)//求Ak[i][j]
    {
        for(i = 0; i < g.n; i++)
        {
            for(j = 0; j < g.n; j++)
            {
                if (A[i][j] > A[i][k] + A[i][j])//找到更短路径
                {
                    A[i][j] = A[i][k] + A[i][j];//修改路径长度
                    path[i][j] = path[k][j];//修改最短路径为经过顶点k
                }
            }
        }
    }
}

在这里插入图片描述

typedef struct//表头节点类型
{
    VertexType data;//顶点信息
    int count;//存放顶点入度
    ArcNode *firstarc;//指向第一条边
}VNode;
void TopSort(Vnode adj[], int n)
{
    int i, j;
    int St[MAXV], top = -1;//栈的指针为top
    ArcNode *p;
    for (int i = 0; i < n; i++)
    {
        if (adj[i].count == 0)//入度为0的顶点进栈
        {
            top++;
            St[top] = i;
        }
    }
    while (top > -1)//栈不空时循环
    {
        i = St[top];//出栈
        top--;
        cout << i;
        p = adj[i].firstarc;
        while (p != nullptr)
        {
            j = p->adjvect;
            adj[j].count--;
            if (adj[j].count == 0)
            {
                top++;
                St[top] = j;
            }
            p = p->nextare;//找下一个相邻顶点
        }
    }
}

AOE网和关键路径

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
以上 如果此篇博客对您有帮助欢迎点赞与转发 有疑问请留言或私信 2021/1/12

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值