有向图的简单路径求解问题(C/C++)

题目:有向图的简单路径求解问题

一、课题内容和要求
给定一个有向图G两个顶点a和b,试编写算法求a到b的简单路径的数量,并分别输出最短的简单路径和最长的简单路径。
在这里插入图片描述

头文件:
#ifndef PATH_HEADER_
#define PATH_HEADER_
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#define ERROR 0
#define OK 1
#define Overflow 2   //Overflow表示上溢出
#define Underflow 3   //Underflow表示下溢出
#define NotPresent 4  //NotPresent表示元素不存在
#define Duplicate 5     //Duplicate 表示有重复的元素
#define STLEN 5
static int sum = 0;  //统计有几条简单路径
static int min_number = 100;  //假设此图最多包含100个顶点
typedef int Status;
typedef int ElemType;
typedef struct eNode
{
    int adjVex;   //与任意顶点u相邻接的顶点
    ElemType w;     //边的权值
    struct eNode* nextArc;  //指向下一个边节点
}ENode;
typedef struct lGraph
{
    int n;      //图的当前顶点数
    int e;          //图的当前边数
    ENode **a;      //指向一维指针数组
}LGraph;
 Status Init(LGraph *lg, int nSize);
 void Destroy(LGraph *lg);
 Status Exist(LGraph *lg, int u, int v);
 Status Insert(LGraph *lg, int u, int v,ElemType w); 
 Status Remove(LGraph *lg, int u, int v);
 void DFS(int v, int length, int visited[],LGraph *Lg);
 void DFSGraph(LGraph *Lg);
#endif

在这里插入图片描述

头文件实现部分

#include <stdio.h>
#include "pathheader.h"
int min = INT_MAX;
int max = INT_MIN;
/**
 * 1、初始化
 * 邻接表的初始化运算是构造一个有n个顶点,但不包含边的邻接表。使用动态分配数组空间方式构造一个长度为
 * n的一维指针数组a
 * 
 * 算法步骤:
 *  1、对n、e的值进行初始化
 *  2、动态分配一维指针数组a,若失败,则返回出错信息;否则,对数组a中每个元素赋初值NULL。并且返回OK
 */
Status Init(LGraph *lg, int nSize)
{
    lg->n = nSize;
    lg->e = 0;
    lg->a = (ENode **)malloc(nSize * sizeof(ENode *)); //动态生成长度为n的一维指针数组
    if (!lg->a)
    {
        return ERROR;
    }
    else
    {
        for (int i = 0; i < lg->n; i++)
        {
            lg->a[i] = NULL;
        }
        return OK;
    }
}
/**
 * 2、撤销
 * 撤销运算的主要功能是首先释放邻接表中每条单链表的所有边节点,然后释放一维指针数组a的存储空间,以防止
 * 内存泄露
 * 
 * 算法步骤:
 *  1、释放邻接表中每条单链表的所有边节点,即相当于lg->a[i] = NULL;
 *  2、释放一维指针数组a的存储空间
 */
void Destroy(LGraph *lg)
{
    ENode *p, *q;
    for (int i = 0; i < lg->n; i++)
    {
        p = lg->a[i];
        q = p;
        while (p)
        {
            p = p->nextArc;
            free(q);
            q = p;
        }
    }
    free(lg->a);
}

/**
 * 3、边的搜索
 * 若参数u,v无效,则返回ERROR,否则,从a[u]指示的顶点u的单链表的第一个边节点开始,搜索adjVex的值为v
 * 边节点,若找到此边,但会OK,否则返回ERROR
 * 
 * 算法步骤:
 *  1、若参数u、v无效,则返回ERROR
 *  2、指针p指向顶点u的单链表的第一个边节点
 *  3、搜索u的单链表中是否存在adjVex的值为v的边节点,若找到此边,返回OK,否则返回ERROR 
 */
Status Exist(LGraph *lg, int u, int v)
{
    ENode *p;
    if (u < 0 || v < 0 || u > lg->n - 1 || v > lg->n - 1 || u == v)
    {
        return ERROR;
    }
    p = lg->a[u];
    while (p && p->adjVex != v)
    {
        p = p->nextArc;
    }
    if (!p)
    {
        return ERROR;
    }
    else
    {
        return OK;
    }
}
/**
 * 4、边的插入
 * 若参数u、v无效,返回ERROR,反之,从a[u]指示的边节点开始搜索adjVex的值为v的边节点,若找到,说明边节点
 * <u,v>存在,返回Duplicate,否则,创建边节点并在u的单链表的最前面插入此边节点。
 * 
 * 算法步骤:
 *  1、若参数u、v无效,返回ERROR
 *  2、搜索u的单链表中是否存在adjVex值为v的边节点,若找到<u,v>,则返回Duplicate。
 *  3、为新的边节点分配存储空间,将此节点插入u的单链表的最前面
 *  4、图中边数+1,返回OK
 */
Status Insert(LGraph *lg, int u, int v, ElemType w)
{
    ENode *p;
    if (u < 0 || v < 0 || u > lg->n - 1 || v > lg->n - 1 || u == v)
    {
        return ERROR;
    }
    if (Exist(lg, u, v))
    {
        return Duplicate;
    }
    p = (ENode *)malloc(sizeof(ENode *)); //为新的节点分配内存
    p->adjVex = v;
    p->w = w;
    p->nextArc = lg->a[u]; //将新的边节点插入单链表的最前面
    lg->a[u] = p;
    lg->e++;
    return OK;
}
/**
 * 5、边的删除
 * 若参数u、v无效,返回ERROR,否则,从a[u]指示边节点开始搜索adjVex值为v的边节点,若找到边<u,v>对应的
 * 边节点,删除之。
 * 
 * 算法步骤:
 *  1、若参数u、v无效,返回ERROR
 *  2、指针p指向顶点u的单链表的第一个边节点
 *  3、搜索u的单链表中是否存在adjVex的值为v的边节点,若没有找到边<u,v>,则返回NotPresent
 * ,否则删除。
 *  4、图中边数-1,返回OK
 */
Status Remove(LGraph *lg, int u, int v)
{
    ENode *p, *q;
    if (u < 0 || v < 0 || u > lg->n - 1 || v > lg->n - 1 || u == v)
    {
        return ERROR;
    }
    p = lg->a[u], q = NULL;
    while (p && p->adjVex != v)
    {
        q = p;
        p = p->nextArc;
    }
    if (!p)
    {
        return NotPresent; //p为空,待删除边不存在
    }
    if (q)
    {
        q->nextArc = p->nextArc; //从单链表删除此边
    }
    else
    {
        lg->a[u] = p->nextArc; //此时为第一个边节点就是待删除边的情况
    }
    free(p);
    lg->e--;
    return OK;
}
/**
 * 在此采用邻接表作为图的存储结构,下面分别给出了优先深度遍历的递归函数DFS和深度优先遍历整个图的DFSGraph
 * 函数。后者调用前者完成对整个图的深度优先遍历。若是非连通图,执行一次DFS之后,图中还有顶点未被访问,需要
 * 再次从未被访问的顶点出发,重复执行深度优先遍历,直至图中所有顶点均被访问过。
 * 
 * 算法步骤:
 *  1、从顶点v出发,访问顶点v,并且令visited[v] = 1;
 *  2、依次查找v的所有邻接点w,若visited[w]的值为0,则从w出发,深度优先遍历G
 */
int a, b;
void DFS(int v, int length, int visited[], LGraph *Lg)
{
    ENode *p = NULL;
    if (v == b)
    {
        sum++;
        if (length < min)
        {
            min = length;
        }
        if (length > max)
        {
            max = length;
        }
        return;
    }
    for (p = Lg->a[v]; p; p = p->nextArc)
    {
        if (!visited[p->adjVex] && p)
        {
            visited[p->adjVex] = 1;
            DFS(p->adjVex, length + p->w, visited, Lg);
            visited[p->adjVex] = 0;
        }
    }
}
void DFSGraph(LGraph *Lg)
{
    int *visited = (int *)malloc(Lg->n * sizeof(int)); //动态生成标记数组visited
    for (int i = 0; i < Lg->n; i++)
    {
        visited[i] = 0; //初始化visited数组,令所有顶点都未被访问过
    }
    printf("请输入顶点a,b的值:");
    scanf("%d %d", &a, &b); //输入始点和终点
    visited[a] = 1;         //为此时此刻加入路径标记为1,即路径不可再走此点
    DFS(a, 0, visited, Lg);
    printf("一共有%d条简单路径,其中最长的路径长度是%d, 最短的路径是%d\n", sum, max, min);
    free(visited);
}

在这里插入图片描述

主函数部分
/**
 * 2020-06-08 pm:20:01
 * 有向图的简单路径求解问题:在一个有向图中,寻找给定的两个顶点a、b,求a到b的简单路径的数量,并且
 * 给出最短和最长的路径
 * 关键字:有向图、简单路径、最短、最长
 * 参考书:《数据结构》(C语言)慕课版、《大话数据结构》、《啊哈!算法》、《C Primer Plus》
 * 主要功能:初始化图、为图增加边及其权值、删除误增加的边、进行深度优先遍历实现简单路径的查询
 * 主要算法:深度优先遍历、图的存储结构之邻接表、单链表的遍历、增加、删除
 */ 
#include <stdio.h>
#include "pathheader.h"
 int main()
 {
    LGraph Lg;  //声明一个图,名字为Lg
    int n;  //此图顶点的个数
    printf("请输入需要此图需要的顶点数:");
    scanf("%d",&n);
    getchar();
    int status = Init(&Lg, n);  //初始化图,为每个顶点分配内存空间,初始化成功,status为1,反之返回0
    if(!status)
    {
        printf("没有成功动态生成长度为%d的一维指针数组,程序终止运行!\n\n",n);
    }
    else
    {
        printf("带有%d个顶点的图已经初始化完成,请插入边<u,v>及权值w,以构成完整的图\n\n",n);
    }
    int u, v, w;   //表示顶点u、v和权值w
    int i = 1;  //统计第几条边
    char flag[5];//接收输入
    printf("是否准备为初始化的图插入边(yes/no):");
    gets(flag);
    while(strcmp(flag, "yes") == 0)
    {
        printf("请插入第%d条边<u,v>及权值w:",i);
        scanf("%d %d %d",&u,&v,&w);
        getchar();
        status = Insert(&Lg, u, v, w);  //检查插入操作是否成功
        if(status == 5)  
        {
            printf("您输入的顶点已存在,请重新输入!\n\n");
            continue;
        }
        else if(status == 0)
        {
            printf("参数u=%d、v=%d无效,请重新输入!\n\n",u, v);
            continue;
        }
        else
        {
            printf("第%d条边已经成功插入完成!\n\n",i);
        }
        i++;  //为即将插入的边做好准备
        printf("是否继续插入边(yes/no):");
        gets(flag);  //接收输入
        if(strcmp(flag, "yes") != 0)
        {
            printf("带有%d个顶点的图一共成功插入%d条边\n\n",n, i-1);
            break;
        }
    }
    int m = i-1;   //为了统计还剩下几条边
    printf("是否错误插入某条边,请删除!(yes/no):");
    gets(flag);
    i = 1;
    while(strcmp(flag, "yes") == 0)
    {
        printf("请输入要删除第%d条边<u,v>:",i);
        scanf("%d %d",&u,&v);
        getchar();
        status = Remove(&Lg, u, v);  //调用remove操作,返回删除操作的状态
        if(status == 4)
        {
            printf("您输入的顶点不存在,请重新输入!\n\n");
            continue;
        }
        else if(status == 0)
        {
            printf("参数u=%d、v=%d无效,请重新输入!\n\n",u, v);
            continue;
        }
        else
        {
            printf("第%d条边已经成功删除完成!\n\n",i);
        }
        i++;//为继续删除边做好准备
        printf("是否继续删除边(yes/no):");
        gets(flag);
        if(strcmp(flag, "yes") != 0)
        {
            printf("带有%d个顶点的图一共删除了%d条边,还剩下%d条边\n\n",n, i-1, m-(i-1));
            break;
        }
    }
    DFSGraph(&Lg);  //调用此图的深度优先遍历函数

    Destroy(&Lg);  //释放内存空间
    return 0;
 }


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

请需要的小伙伴关注一下动态!你的随手鼓励,是我不断创作的最大动力!感谢🙏

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Ocodotial

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

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

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

打赏作者

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

抵扣说明:

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

余额充值