油克小学期(三)图搜索(2)基于递归的图的路径求解

本文描述了一个C语言程序,用于处理图的结构并实现深度优先搜索算法。程序包括了节点、邻接点的定义,图的构建,以及从文件读取图数据的功能。它还支持路径操作,如路径复制、倒序和显示,并能检测路径中的节点是否存在回路。
摘要由CSDN通过智能技术生成

 说明提示:

(1)用记事本建立文件“Graph”。

A
B/E/D
B
A/C
C
B/D/E
D
A/C/E
E
A/C/D

代码实现:

```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define Status int
#define TRUE 1
#define FALSE 0
#define NAMESIZE 20


//图的存储结构
typedef char Node[NAMESIZE];                            //节点名称类型
typedef struct NeighborNode                                     //节点类型
{
    Node node;                                          //节点
    struct NeighborNode *next;                          //邻接点
}NeighborNode, *Neighbor, *Path;                        //邻接点类型和路径类型相同

typedef struct ANode                                            //邻接点类型
{
    Node node;                                          //节点
    Neighbor adjacents;                                 //邻接点
} ANode, AnyNode;

typedef struct GraphNode
{
    AnyNode node;                                       //节点
    struct GraphNode *next;                             //其他节点
} GraphNode, *Graph;                                    //图的节点和图指针

typedef struct Paths
{
    Path path;                                          //当前路径
    struct Paths *next;                                 //下一条路径
} Paths, *PathsAggregate;                               //路径集合及其指针 */

typedef struct Paths *STACK;

//1.通用比较函数
int Equal(Node n1, Node n2, int fun(const Node n1, const Node n2));

//2.通用赋值函数
void Set(Node n1, Node n2, char *fun(Node n1, const Node n2));

//3.兄弟节点关系创建
Neighbor AddNeighbor(Neighbor br, Node node);

//3.1 兄弟显示
void priNeighbors(Neighbor Neighbor);


//4.双亲与子节点关系的构建
Graph Form_Pa_Ch(Node pa, Neighbor br);

//5.将带子节点的双亲节点加入到树种
Graph AddsubGraph(Graph graph, Graph subgraph);

//6.清除兄弟节点和树
Neighbor ClearNeighbors(Neighbor br);

//7.ClearGraph 清空树, 回收数据单元
Graph ClearGraph(Graph Graph);

//8.字符数组转换
void CreateStr(char *Neighborset);

//9.字符数组建立兄弟关系
Neighbor CreateNeighbors(Neighbor Neighbors, char *Neighborset);

//10.根据树文件建立树
Graph CreateGraph(Graph Graph, char *filename);

//树搜索实现

//1.复制子节点集
Neighbor CopyNeighbors(Neighbor children);

//2.扩展节点集
Neighbor ExpandNodes(Graph Graph, Node pa);

//路径

//1.节点加入路径
Path AddNodeToPath(Node node, Path path);

//2.复制路径与路径集合

//2.1复制一条路径
Path CopyPath(Path path);

//2.2复制路径集合
PathsAggregate CopyPaths(PathsAggregate paths);

//3.路径倒序与显示

//3.1路径倒序
void RevPath(Path path);

//3.2显示路径
void priPath(Path path);
//4.路径加入路径集合
PathsAggregate AddPathToPaths(Path path, PathsAggregate paths);

//5.节点集合与路径集合形成路径集合
PathsAggregate FormPathsFromNodes(Neighbor Neighbors, Path path, PathsAggregate paths);

//6.回收路径与路径集合空间

//6.1回收路径空间
Path ClearPath(Path path);

//6.2回收路径集合空间
PathsAggregate ClearPaths(PathsAggregate paths);

/* 1.判断节点是否在路径中。IsInPath用于判断节点是否在路径中,返回“真”、“假”,
用于回路的判断 */
Status IsInPath(Node Node, Path path); //节点在路径中

//1.通用比较函数
int Equal(Node n1, Node n2, int fun(const Node n1, const Node n2))
{
    return (int) fun(n1, n2);
}

//2.通用赋值函数
void Set(Node n1, Node n2, char *fun(Node n1, const Node n2))
{
    fun(n1, n2);
}


//3.邻接点关系创建
//AddANeighbor把一个节点加入到邻接点群中。
Neighbor AddANeighbor(Neighbor gr, Node node) //在gr邻接点群中增加一个邻接点node
{
    Neighbor b, pb; //邻接点变量
    b = (Neighbor) malloc(sizeof(struct NeighborNode)); //动态开辟一个邻接点单元
    strcpy(b->node, node); 
    b->next = NULL;
    if (gr == NULL) //没有邻接点的情况
    {
        gr = b; //node就是第一个邻接点
    } else {//有邻接点的情况,node就是最后一个邻接点
        pb = gr;
        while (pb->next) {
            pb = pb->next;
        }
        pb->next = b;
    }
    return gr; //返回邻接点
}
//3.1 邻接点显示
void priNeighbor(Neighbor neighbor) {
    while (neighbor) {
        printf("%s", neighbor->node);
        if (neighbor->next == NULL) {
            break;
        }
        printf("%s", "->");
        neighbor = neighbor->next;
    }
}

//4.原节点与邻接点关系的构建
//Form_Pa_Ch将邻接点(即所有邻接点)连接到原节点中,构成一个子图。
Graph Form_Pa_Ch(Node pa, Neighbor gr) //原节点与邻接点构成子图
{
    Graph graph;
    graph = (Graph) malloc(sizeof(struct GraphNode)); //创建原节点
    strcpy(graph->node.node, pa);
    graph->node.adjacents = gr; //邻接点与原节点构成子图
    graph->next = NULL; 
    return graph; //返回带邻接点的原节点,即子图
}
//5.将带邻接点的原节点加入到图中
Graph AddsubGraph(Graph tree, Graph subtree) //原节点加入图中
{
    Graph t = tree; //临时图
    if (tree == NULL) { //图不存在
        tree = subtree; //带邻接点的原节点即为图
    } else { //图存在
        while (t->next) {
            t = t->next;
        }
        t->next = subtree; //带邻接点的原节点加入图的最后
    }
    return tree; //返回图的指针
}
//6.清除邻接点和图
//ClearNeighbors清空邻接点群,回收数据单元
Neighbor ClearNeighbors(Neighbor gr) //回收邻接点变量
{
    Neighbor gr1 = gr; //临时邻接点变量
    while (gr) {
        gr1 = gr;
        gr = gr->next;
        free(gr1); //回收单元
        gr1 = NULL;
    }
    return gr; //返回NULL
}
//7.ClearGraph 清空图, 回收数据单元
Graph ClearGraph(Graph graph) //回收图空间
{
    Graph graph1 = graph; //临时图
    while (graph) {
        graph1 = graph;
        graph = graph->next;
        free(graph1); //回收单元
        graph1 = NULL;
    }
    return graph; //返回NULL
}

//8.字符数组转换
//CreateSt把字符串的字符‘/’变换为转义字符‘\0’(字符串结束标志),即在数组中存放多个节点名称(邻接点节点)的字符串。
void CreateStr(char *neighborset) //字符数组转换为多个字符串
{
    char *c = neighborset; //临时字符串
    while (*c) {
        if (*c == '/') {
            *c = '\0'; //插入字符串结束标记
        }
        c++;
    }
    c++;
    *c = '\0'; //多一个结束标记
}

//9.字符数组建立邻接点关系
//CreateNeighbors把字符串中含有多个邻接点节点名称建立图的邻接点节点群(链表)。
Neighbor CreateNeighbors(Neighbor neighbors, char *neighborset) //若干个邻接点构成邻接点
{
    char *p = neighborset; //多个节点,分隔符‘/’
    Node node;
    CreateStr(neighborset); //变为多个字符串
    while (*p) {
        strcpy(node, p);
        neighbors = AddANeighbor(neighbors, node); //加入邻接点关系中
        p += strlen(node) + 1; //下一个节点
    }
    return neighbors; //返回邻接点链表
}

//10.根据图文件建立图
//CreateGraph从图文件建立图(链表)
Graph CreateGraph(Graph graph, char *filename) //从文件中创建图
{
    Graph subgraph;
    Neighbor neighbors;
    FILE *fp;
    char graph1[200], neighborset[5000];
    fp = fopen(filename, "r");
    while (!feof(fp)) { //文件是否还存在图的节点名称
        fscanf(fp, "%s", graph1); //读入原节点
        fscanf(fp, "%s", neighborset);
        neighbors = NULL; //读入若干邻接点节点(邻接点),分隔符‘/’
        neighbors = CreateNeighbors(neighbors, neighborset); //构建原节点
        subgraph = Form_Pa_Ch(graph1, neighbors); //构建子图
        graph = AddsubGraph(graph, subgraph); //图中加入子图 
    }
    fclose(fp); //关闭文件
    return graph; //返回所建的图
}

//图搜索实现

//1.复制邻接点集
Neighbor CopyNeighbors(Neighbor children) //节点集复制
{
    Neighbor copyNode, lastNode, head = NULL; //没有邻接点
    while (children) { //分配节点空间
        copyNode = (Neighbor) malloc(sizeof(struct NeighborNode));
        strcpy(copyNode->node, children->node); //复制节点
        copyNode->next = NULL;
        if (head == NULL) //第一个节点
            head = copyNode;
        else //建立链接,复制邻接点集
            lastNode->next = copyNode;
        lastNode = copyNode;
        children = children->next; //下一个邻接点
    }
    return head; //返回头节点指针
}
//2.扩展节点集
Neighbor ExpandNodes(Graph graph, Node pa) //由节点获取邻接点
{
    Neighbor children = NULL; //孩邻接点
    Graph t = graph; //图
    while (t) { //节点不为空
        if (strcmp(t->node.node, pa) == 0) { //找到分支节点
            children = CopyNeighbors(t->node.adjacents);
            break;
        } 
        t = t->next; //下一个原节点
    }
    return children;
}
//路径

//1.节点加入路径
Path AddNodeToPath(Node node, Path path)
{
    Path p;
    p = (Neighbor)malloc(sizeof(NeighborNode));
    strcpy(p->node, node);
    if(path == NULL)                            //路径上的第一个节点
        p->next = NULL;
    else    {                                   //头插法
        p->next = path;
    }

    path = p;                                   //头插法第二步
    p = NULL;
    return path;                                //返回路径头部
}
//2.复制路径与路径集合

//2.1复制一条路径
Path CopyPath(Path path)
{
    Path tempath;
    tempath = (Path)CopyNeighbors((Neighbor)path);
    return tempath;
}
//2.2复制路径集合
PathsAggregate CopyPaths(PathsAggregate paths);


//3.路径倒序与显示

//3.1路径倒序
void RevPath(Path path)
{
    int num = 0, i;
    Node *nodes = NULL;                                //节点集
    Path p = path;                              //路径

    while(p)    {                               //统计路径条数
        p = p->next;
        num++;
    }
    

    nodes = (Node *) malloc(num*sizeof(Node));
    
    for (i = 0, p = path; p; p = p->next, i++) //读取路径节点置于数组中
        strcpy(nodes[i], p->node);
    for (i = num - 1, p = path; p; p = p->next, i--) //数组数据倒序置于路径中
        strcpy(p->node, nodes[i]);

    free(nodes);                                //释放路径集
    nodes = NULL;
}

//3.2显示路径
void priPath(Path path)
{
    priNeighbor((Neighbor)path);
}

//4.路径加入路径集合
PathsAggregate AddPathToPaths(Path path, PathsAggregate paths)
{
    Path copypath;
    PathsAggregate ps = NULL, p;

    if(path == NULL)                                //路径不存在,直接返回
        return paths;

    copypath = CopyPath(path);                      //路径存在则copy
    ps = (PathsAggregate)malloc(sizeof(Paths));
    ps->path = copypath;
    ps->next = NULL;
    if(paths == NULL)   {                           //第一条路径
        paths = ps;
    } else  {                                       //之后的路径
        p = paths;                                  //遍历,尾插法
        while(p)
            p = p->next;
        p->next = p;
    }

    free(copypath);
    free(ps);
    copypath = NULL;
    ps = NULL;

    return paths;                                   //返回路径集合
}


//5.节点集合与路径集合形成路径集合
PathsAggregate FormPathsFromNodes(Neighbor Neighbors, Path path, PathsAggregate paths)
{
    Path tempath;                                           //暂时存储路径
    while(Neighbors) {                                       //节点集不空
        tempath = CopyPath(path);                           //复制路径
        tempath = AddNodeToPath(Neighbors->node, tempath);   //将节点加入路径
        paths = AddPathToPaths(tempath, paths);             //将新路径加入路径集合
        Neighbors = Neighbors->next;                          //下一个节点
    }

    free(tempath);
    tempath = NULL;

    return paths;                                           //返回更新后的路径
}


//6.1回收路径空间
//ClearPath回收路径数据空间,复用了回收邻接点数据空间函数
Path ClearPath(Path path) //回收路径空间
{
    path = (Path) ClearNeighbors((Neighbor) path); //路径与邻接点节点集合形式相同
    return path;
}


//6.2回收路径集合空间
PathsAggregate ClearPaths(PathsAggregate paths)
{
    PathsAggregate paths1 = paths;            //暂时存放路径集合
    while(paths)    {                       //路径集合不空
        paths1 = paths;                     //指向相同空间
        ClearPath(paths1->path);            //释放路径集合
        paths = paths->next;                //下一条路径
        free(paths1);                       //释放指针
        paths1 = NULL;
    }

    return paths;                           //返回NULL
}

/* 1.判断节点是否在路径中。IsInPath用于判断节点是否在路径中,返回“真”、“假”,
用于回路的判断 */
Status IsInPath(Node Node, Path path)                   //节点在路径中
{
    Path p = path;
    Status flag = FALSE;                                //存在标记
    
    while(p)    {
        if(strcmp(Node, p->node) == 0)   {
            flag = TRUE;
            break;
        } else  {
            p = p->next;
        }
    }

    return flag;
}


//图的深度优先搜索,递归实现

//1.给定节点的子图
Graph GetSubGraph(Graph graph, Node node);

//2.搜索子图生成路径
Status SearchSubGraph(Graph graph, Graph subgraph,
                      Node end, Path *path, Path *searchpath);

//3.基于递归的搜索图生成路径
Status SearchPath(Graph graph, Node start, Node end, Path *path); 

//图的深度优先搜索,递归实现


//1.给定节点的子图
Graph GetSubGraph(Graph graph, Node node)
{
    Graph subgraph = graph;
    while(subgraph) {                                   //遍历
        if(strcmp(subgraph->node.node, node) == 0)      //找到退出
            break;
        else                
            subgraph = subgraph->next;                  //找不到下一个
    }

    return subgraph;
}

//2.搜索子图生成路径
Status SearchSubGraph(Graph graph, Graph subgraph,
                      Node end, Path *path, Path *searchpath)
{
    Graph subsubgraph = NULL;
    Neighbor adjacents;
    Status flag = FALSE;

    if(subgraph == NULL)    {
        *path = NULL;
        *searchpath = NULL;
        return flag;
    }

    if(strcmp(subgraph->node.node, end) == 0)   {
        flag = TRUE;
        *searchpath = AddNodeToPath(end, *searchpath);
        *path = AddNodeToPath(end, *path);
    } else if(IsInPath(subgraph->node.node, *searchpath) == FALSE)  {
        *searchpath = AddNodeToPath(subgraph->node.node, *searchpath);
        adjacents = subgraph->node.adjacents;
        while(adjacents)    {
            if(IsInPath(adjacents->node, *searchpath) == FALSE) {
                subsubgraph = GetSubGraph(subgraph, adjacents->node);
                flag = SearchSubGraph(subgraph, subsubgraph, end, path, searchpath);

                if(flag == TRUE)    {
                    *path = AddNodeToPath(adjacents->node, *path);
                    break;
                }
            }
            adjacents = adjacents->next;
        }
    }
    return flag;
}

//3.基于递归的搜索图生成路径
Status SearchPath(Graph graph, Node start, Node end, Path *path)
{
    Graph subgraph = NULL;
    Status flag = FALSE;
    Path searchpath = NULL;
    subgraph = GetSubGraph(graph, start);
    if(subgraph)    {
        flag = SearchSubGraph(graph, subgraph, end, path, &searchpath);
    }
    printf("Search %s from %s, Status = %d\n", end, start, flag);
    if(flag)    {
        printf("\n Success!\n");
        printf("Search Path = ");
        RevPath(searchpath);
        priPath(searchpath);
    } else  {
        printf("\nFailed!\n");
    }
    printf("\n==================\n");
    ClearPath(searchpath);

    return flag;
}

int main(int argc, char* argv[])
{
    Node start, end;
    Path path = NULL;
    Status flag = FALSE;
    Graph graph = NULL;
    char *filename = (char*)"Graph.txt";
    graph = CreateGraph(graph, filename);
    printf("The Start Node:");
    scanf("%s", start);
    printf("The End Node:");
    scanf("%s", end);
    flag = SearchPath(graph, start, end, &path);

    return 0; 
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

北城学神

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

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

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

打赏作者

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

抵扣说明:

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

余额充值