油克小学期(二)树搜索(3)基于递归的树的路径求解

说明提示:

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

a1
b2/c2/d2
c2
e3/f3
d2
g3
b2
/
e3
/
f3
/
g3
/

 代码实现:

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

#define Status int
#define EqualFun strcmp
#define SetValue strcpy
#define NAMESIZE 3
#define TRUE 1
#define FALSE 0

typedef char Node[NAMESIZE];

typedef struct BrotherNode                          
{
    Node node;
    struct BrotherNode *next;
} BrotherNode, *Brother;

typedef struct ParentNode
{
    Node node;
    Brother children;
} ParentNode, Parent;

typedef struct TreeNode
{
    Parent node;
    struct TreeNode *next;
} TreeNode, *Tree;

//路径结构设计
typedef struct BrotherNode *Path;

typedef struct Paths
{
    Path path;
    struct Paths *next;
} Paths, *PathsPointer;


//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.兄弟节点关系创建
Brother AddBrother(Brother br, Node node);

//3.1 兄弟显示
void priBrothers(Brother brother);


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

//5.将带子节点的双亲节点加入到树种
Tree AddsubTree(Tree tree, Tree subtree);

//6.清除兄弟节点和树
Brother ClearBrothers(Brother br);

//7.ClearTree 清空树, 回收数据单元
Tree ClearTree(Tree tree);

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

//9.字符数组建立兄弟关系
Brother CreateBrothers(Brother brothers, char *brotherset);

//10.根据树文件建立树
Tree CreateTree(Tree tree, char *filename);

//树搜索实现

//1.复制子节点集
Brother CopyBrothers(Brother children);

//2.扩展节点集
Brother ExpandNodes(Tree tree, Node pa);

//路径

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

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

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

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

//3.路径倒序与显示

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

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

//5.节点集合与路径集合形成路径集合
PathsPointer FormPathsFromNodes(Brother brothers, Path path, PathsPointer paths);

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

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

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

//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.兄弟节点关系创建
Brother AddBrother(Brother br, Node node)
{
    Brother b, pb;                                  //兄弟节点变量
    b = (Brother)malloc(sizeof(BrotherNode));       //动态分配
    Set(b->node, node, SetValue);                   //赋值
    b->next = NULL;                                 //b为新建节点,其下一个节点不存在

    if(br==NULL)                                    //首元结点创建
        br = b;
     else   {                                       //尾结点创建
         pb = br;                               
         while(pb->next)                            //单链表遍历至尾
             pb = pb->next;
         pb->next = b;                              //尾插操作
     }

     b = NULL;
     pb = NULL;
     return br;                                     //返回兄弟节点
}

//3.1 兄弟显示
void priBrothers(Brother brother)
{
    while(brother)  {
        printf("%s", brother->node);
        
        if(brother->next == NULL)
            break;
        printf("%s", "->");
        brother = brother->next;
    }
}

//4.双亲与子节点关系的构建
Tree Form_Pa_Ch(Node pa, Brother br)
{
    Tree parent;                                    //创建双亲结点
    parent = (Tree)malloc(sizeof(TreeNode));        //动态分配
    Set(parent->node.node, pa, SetValue);           //赋值(双亲结点的值)
    parent->node.children = br;                     //兄弟节点与双亲结点构成子树
    parent->next = NULL;                            //下一个置空

    return parent;                                  //返回
    parent = NULL;
}

//5.将带子节点的双亲节点加入到树中
Tree AddsubTree(Tree tree, Tree subtree)
{
    Tree t = tree;                                  //暂时存放原树      
    if(tree == NULL)    {                           //原树不存在
        tree = subtree;                             //直接添加
    } else  {                                       //尾插法    
        while(t->next)                              //遍历
            t = t->next;
        t->next = subtree;                          //尾插操作
      }

    return tree;                                    //返回树
}

//6.清除兄弟节点和树
Brother ClearBrothers(Brother br)
{
    Brother br1 = br;                               //临时节点
    while(br)   {                                   //遍历删除
        br1 = br;
        br = br->next;
        free(br1);
        br1 = NULL;
    }

    return br;                                      //返回NULL
}

//7.ClearTree 清空树, 回收数据单元
Tree ClearTree(Tree tree)
{
    Tree tree1 = tree;                              //临时节点标记前一个结点
    while(tree) {                                   //遍历删除
        tree1 = tree;
        tree = tree->next;
        free(tree1);
        tree1 = NULL;
    }

    return tree;                                    //返回NULL
}

//8.字符数组转换
void CreateStr(char *brotherset)
{
    char *c = brotherset;                           //临时字符串
    while(*c)   {                                   //遍历将'/'更改为'\0'
        if(*c == '/')
            *c = '\0';
        c++;
    }
    c++;
    *c = '\0';                                      //最后加一个结束标记
}

//9.字符数组建立兄弟关系
Brother CreateBrothers(Brother brothers, char *brotherset)
{
    char *p = brotherset;                           //节点值的指针
    Node node;                  
    CreateStr(brotherset);                          //转换为字符串
    
    while(*p)   {                                   //遍历
        Set(node, p, SetValue);                     //赋值,将p的值赋给node
        brothers = AddBrother(brothers, node);      //将node的值加入brothers
        p += strlen(node) + 1;                      //指针+1,指向下一个节点
    }

    return brothers;                                //返回兄弟节点链表
}

//10.根据树文件建立树
Tree CreateTree(Tree tree, char *filename)
{
    Tree subtree;
    Brother brothers;
    FILE *fp;
    char parent[100], brotherset[5000];
    fp = fopen(filename, "rt");

    while(!feof(fp))    {                           //文件中是否还存在树的节点名称
        fscanf(fp, "%s", parent);                   //读取双亲节点
        fscanf(fp, "%s", brotherset);               
        brothers = NULL;                            //读取若干个孩子节点
        brothers = CreateBrothers(brothers, brotherset);    //构建双亲节点
        subtree = Form_Pa_Ch(parent, brothers);     //构建子树
        tree = AddsubTree(tree, subtree);           //树种加入子树
    }

    fclose(fp);                                     //关闭文件
    return tree;                                    //返回所建的树
}


//树搜索实现

//1.复制子节点集
Brother CopyBrothers(Brother children)
{
    Brother copynode, lastnode, head = NULL;            //没有子节点

    while(children) {
        copynode = (Brother)malloc(sizeof(Brother));
        Set(copynode->node, children->node, SetValue);  //复制节点
        copynode->next = NULL;

        if(head == NULL)    {                           //首元结点
            head = copynode;
        } else  {                                       //建立链表,复制子节点集
            lastnode->next = copynode;
        }
        lastnode = copynode;
        children = children->next;                      //下一个子节点
    }

    free(copynode);
    copynode = NULL;

    return head;
}

//2.扩展节点集
Brother ExpandNodes(Tree tree, Node pa)
{
    //pa是树根,根据树的结构,每个纵向节点都会保留根节点
    Brother children = NULL;                                //孩子节点
    Tree t = tree;                                          //树
    while(t)    {                                           //节点不为空
        if(Equal(t->node.node, pa, EqualFun) == 0)   {      
            children = CopyBrothers(t->node.children);      //找到分支节点
            break;
        }

        t = t->next;                                        //下一个双亲节点
    }

    return children;                                        //返回所有的孩子
}



//路径

//1.节点加入路径
Path AddNodeToPath(Node node, Path path)
{
    Path p;
    p = (Brother)malloc(sizeof(BrotherNode));
    Set(p->node, node, SetValue);
    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)CopyBrothers((Brother)path);
    return tempath;
}

//2.2复制路径集合
PathsPointer CopyPaths(PathsPointer paths)
{
    PathsPointer copynode, lastnode, head = NULL;
    
    while(paths)    {
        copynode = (PathsPointer)malloc(sizeof(Paths));
        copynode->path = CopyPath(paths->path);             //复制一条路径
        copynode->next = NULL;

        if(head == NULL)    {                               //首元路径
            head = copynode;
        } else  {                                           //其他路径
            lastnode->next = copynode;
        }
        lastnode = copynode;                                //加入路径集合
        paths = paths->next;
    }

    free(copynode);
    copynode = NULL;

    return 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++) //读取路径节点置于数组中
        SetValue(nodes[i], p->node);
    for (i = num - 1, p = path; p; p = p->next, i--) //数组数据倒序置于路径中
        SetValue(p->node, nodes[i]);

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


//3.2显示路径
void priPath(Path path)
{
    priBrothers((Brother)path);
}


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

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

    copypath = CopyPath(path);                      //路径存在则copy
    ps = (PathsPointer)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.节点集合与路径集合形成路径集合
PathsPointer FormPathsFromNodes(Brother brothers, Path path, PathsPointer paths)
{
    Path tempath;                                           //暂时存储路径
    while(brothers) {                                       //节点集不空
        tempath = CopyPath(path);                           //复制路径
        tempath = AddNodeToPath(brothers->node, tempath);   //将节点加入路径
        paths = AddPathToPaths(tempath, paths);             //将新路径加入路径集合
        brothers = brothers->next;                          //下一个节点
    }

    free(tempath);
    tempath = NULL;

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

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

//6.1回收路径空间
Path ClearPath(Path path)
{
    path = (Path)ClearBrothers((Brother)path);
    return path;
}

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

    return paths;                           //返回NULL
}

//判断节点end是否在树中
Status DepthSearch(Tree tree, Node end);

//1.定位子树位置
Tree GetSubTree(Tree tree, Node pa);


/* 
//2.基于递归搜索子树
Status SearchSubTree(Tree tree, Tree subtree, Node end);

//3.搜索树
Status Search(Tree tree, Node start, Node end);
 */


//1.定位子树位置
Tree GetSubTree(Tree tree, Node pa)
{
    Tree subtree = tree;
    while(subtree)  {                           //遍历
        if(Equal(subtree->node.node, pa, EqualFun) == 0)
            break;                              //找到子树退出
        else    {
            subtree = subtree->next;
        }
    }

    return subtree;                             //返回子树
}

/*
//2.基于递归搜索子树
Status SearchSubTree(Tree tree, Tree subtree, Node end)
{
    Tree subsubtree;                                        //子树的子树
    Brother children;                                       //所有子节点
    Status flag = FALSE;                                    //存在标记
    
    if(subtree == NULL)
        return flag;
    
    
    //先序遍历,从根开始搜索
    if(Equal(subtree->node.node, end, EqualFun) == 0)   {   //子树的根节点是被搜索节点
        flag = TRUE;
    } else  {                                               //子根不是
        children = subtree->node.children;
        
        while(children) {                                   //深度优先搜索
            subsubtree = GetSubTree(tree, children->node);  //获得子树
            flag = SearchSubTree(tree, subsubtree, end);    //搜索子树
            if(flag == TRUE)
                break;
            children = children->next;
        }
    }

    return flag;
}

//3.搜索树
Status Search(Tree tree, Node start, Node end)
{
    Tree subtree;
    Status flag = FALSE;
    subtree = GetSubTree(tree, start);
    if(subtree) {
        flag = SearchSubTree(tree, subtree, end);
    } 
    
    return flag;
} 
/*
 int main(int argc, char* argv[])
{
    Tree tree = NULL;
    Node start, end;
    Status flag = FALSE;
    char *filename = "Tree.txt";
    tree = CreateTree(tree, filename);

    printf("==================\n");
    printf("The Start Node:");
    scanf("%s", start);
    printf("The End Node:");
    scanf("%s", end);

    flag = Search(tree, start, end);
    printf("Search %s from %s, Status = %d\n", end, start, flag);

    if(flag == TRUE)    {
        printf("\n Success!\n");
    } else {
        printf("\n Failed!\n");
    }
    printf("==================\n");

    ClearTree(tree);

    return 0;
} 
*/

//基于递归搜索路径

//1.基于递归搜索子树路径
Status SearchSubTree(Tree tree, Tree subtree, Node end, Path *path)
{
    Tree subsubtree;
    Brother children;
    Status flag = FALSE;
    
    if(!subtree)    {
        *path = NULL;
        return flag;
    }

    if(Equal(subtree->node.node, end, EqualFun) == 0)   {
        flag = TRUE;
        *path = AddNodeToPath(subtree->node.node, *path);
    } else  {
        children = subtree->node.children;
        
        while(children) {
            subsubtree = GetSubTree(subtree, children->node);
            flag = SearchSubTree(subtree, subsubtree, end, path);
            if(flag == TRUE)    {
                *path = AddNodeToPath(subtree->node.node, *path);
                break;
            }
            children = children->next;
        }
    }

    return flag;
}


//2基于递归的完整路径求解--深度优先搜索
Status SearchPath(Tree tree, Node start, Node end, Path *path)
{
    Tree subtree;
    Status flag = FALSE;
    subtree = GetSubTree(tree, start);
    if(subtree)
        flag = SearchSubTree(tree, subtree, end, path);
    
    return flag;
}

int main(int argc, char* argv[])
{
    Tree tree = NULL;
    Node start, end;
    Path path = NULL;
    Status flag = FALSE;
    char *filename = "Tree.txt";
    tree = CreateTree(tree, filename);
    printf("====================\n");
    printf("The Start Node:");
    scanf("%s", start);
    printf("The End Node:");
    scanf("%s", end);
    flag = SearchPath(tree, start, end, &path);
    printf("Search %s from %s, Status = %d\n", end, start, flag);
    printf("Path = ");
    //创建路径时采用了头插法,不需要逆序,原来的路径就是正序。
    //RevPath(path);
    //RevPath(path);
    priPath(path);
    printf("\n====================\n");
    ClearPath(path);
    ClearTree(tree);

    return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

北城学神

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

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

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

打赏作者

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

抵扣说明:

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

余额充值