油克小学期(二)树搜索(1)树的深度搜索

说明提示:

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

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

 (2)深度优先搜索算法流程图

代码实现:

```c
//树的深度搜索
#include <stdio.h>
#include <string.h> //包含字符串比较、拷贝等字符串处理函数
#include <malloc.h> //包含动态内存分配、回收函数等内存分配管理函数
#define EqualFun strcmp //符号常量,重命名比较大小,提高复用性
#define SetValue strcpy //符号常量,重命名赋值,提高复用性
#define NAMESIZE 3 //字符串(节点名称)长度
#define TRUE 1
#define FALSE 0
typedef int STATUS;
typedef char NODE[NAMESIZE]; //节点名称类型NODE,存节点名称
struct BROTHERNODE //兄弟节点类型,表示兄弟节点
{
    NODE node; //兄弟节点名称
    struct BROTHERNODE *next; //兄弟节点链接指针,链接其他兄弟
};
//重命名兄弟节点类型,表示若干兄弟节点
typedef struct BROTHERNODE *BROTHER;
struct PARENTNODE //双亲节点类型,表示子树树根节点
{
    NODE node; //双亲节点名称
    BROTHER children; //所有子节点
};
typedef struct PARENTNODE PARENT; //重命名双亲节点类型
struct TREENODE //树分支节点类型
{
    PARENT node; //双亲节点,表示子树树根节点
    struct TREENODE *next; //双亲节点指针指向其他双亲节点
};
//重命名所有双亲节点类型,表示树的存储结构
typedef struct TREENODE *TREE;
//定义堆栈类型,实际上也是节点链表
typedef struct BROTHERNODE *STACK;

//(1)通用比较函数
//Equal判断树的两个节点是否相同,相同为1,不同为0。
int Equal(NODE n1, NODE n2, int *fun()) //程序复用
{
    return (int) fun(n1, n2); //实现n1和n2的比较
}

//(2)通用赋值函数
//Set实现树节点的赋值。
void Set(NODE n1, NODE n2, void *fun()) //程序复用
{
    fun(n1, n2); //将n2的值赋给n2
}

//(3)兄弟节点关系构建
//AddABrother把一个节点加入到兄弟节点群中。
BROTHER AddABrother(BROTHER br, NODE node) //在br兄弟节点群中增加一个兄弟节点node
{
    BROTHER b, pb; //兄弟节点变量
    b = (BROTHER) malloc(sizeof(struct BROTHERNODE)); //动态开辟一个兄弟单元
    Set(b->node, node, SetValue);
    b->next = NULL;
    if (br == NULL) //没有兄弟节点的情况
    {
        br = b; //node就是第一个兄弟
    } else {//有兄弟节点的情况,node就是最后一个兄弟节点
        pb = br;
        while (pb->next) {
            pb = pb->next;
        }
        pb->next = b;
    }
    return br; //返回兄弟节点
}

//(4)双亲与子节点关系构建
//Form_Pa_Ch将兄弟节点(即所有子节点)连接到双亲节点中,构成一个子树。
//双亲节点和兄弟节点构成子树
TREE Form_Pa_Ch(NODE pa, BROTHER br) //双亲节点与兄弟节点构成子树
{
    TREE parent;
    parent = (TREE) malloc(sizeof(struct TREENODE)); //创建双亲节点
    Set(parent->node.node, pa, SetValue);
    parent->node.children = br; //兄弟节点与双亲节点构成子树
    parent->next = NULL;
    return parent; //返回带兄弟节点的双亲节点,即子树
}

//(5)带子节点的双亲加入树中
//AddAsubTree将子树(双亲节点)加入到树中。
TREE AddAsubTree(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)清除兄弟节点群
//ClearBrothers清空兄弟节点群,回收数据单元
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)字符数组转换
//CreateSt把字符串的字符‘/’变换为转义字符‘\0’(字符串结束标志),即在数组中存放多个节点名称(兄弟节点)的字符串。
void CreateStr(char *brotherset) //字符数组转换为多个字符串
{
    char *c = brotherset; //临时字符串
    while (*c) {
        if (*c == '/') {
            *c = '\0'; //插入字符串结束标记
        }
        c++;
    }
    c++;
    *c = '\0'; //多一个结束标记
}

//(9)字符数组建立兄弟关系
//CreateBrothers把字符串中含有多个兄弟节点名称建立树的兄弟节点群(链表)。
BROTHER CreateBrothers(BROTHER brothers, char *brotherset) //若干个子节点构成兄弟
{
    char *p = brotherset; //多个节点,分隔符‘/’
    NODE node;
    CreateStr(brotherset); //变为多个字符串
    while (*p) {
        Set(node, p, SetValue);
        brothers = AddABrother(brothers, node); //加入兄弟关系中
        p += strlen(node) + 1; //下一个节点
    }
    return brothers; //返回兄弟链表
}

//(10)从树文件建立树
//树文件为文本文件,其记录格式为单数行为双亲节点,偶数行为子节点(兄弟节点)集合,用字符‘/’分隔符分开。
TREE CreateTree(TREE tree, char *filename) //从文件中创建树
{
    TREE subtree;
    BROTHER brothers;
    FILE *fp;
    char parent[200], brotherset[5000];
    fp = fopen(filename, "r");
    while (!feof(fp)) { //文件是否还存在树的节点名称
        fscanf(fp, "%s", parent); //读入双亲节点
        fscanf(fp, "%s", brotherset);
        brothers = NULL; //读入若干兄弟节点(子节点),分隔符‘/’
        brothers = CreateBrothers(brothers, brotherset); //构建双亲节点
        subtree = Form_Pa_Ch(parent, brothers); //构建子树
        tree = AddAsubTree(tree, subtree); //树中加入子树
    }
    fclose(fp); //关闭文件
    return tree; //返回所建的树
}


//树搜索的实现
//(1)拷贝子节点集
//CopyBrothers拷贝所有节点(链表),返回拷贝结果的头指针。
BROTHER CopyBrothers(BROTHER children) //节点集复制
{
    BROTHER copynode, lastnode, head = NULL; //没有子节点
    while (children) { //分配节点空间
        copynode = (BROTHER) malloc(sizeof(struct BROTHERNODE));
        Set(copynode->node, children->node, SetValue); //复制节点
        copynode->next = NULL;
        if (head == NULL) //第一个节点
            head = copynode;
        else //建立链接,复制子节点集
            lastnode->next = copynode;
        lastnode = copynode;
        children = children->next; //下一个子节点
    }
    return head; //返回头节点指针
}

//(2)扩展节点集
//ExpandNodes根据树结构,从双亲节点找到所有孩子节点(即兄弟节点),并拷贝所有孩子节点,最后返回所有拷贝结果的头指针。
BROTHER ExpandNodes(TREE tree, NODE 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;
}

//(3)所有节点进栈
//PushChildren所有孩子节点进栈,返回堆栈指针。
STACK PushChildren(STACK stack, BROTHER children) //所有节点进栈
{
    BROTHER p, head;
    head = CopyBrothers(children); //复制所有节点
    p = head; //复制节点集并入栈
    if (p != NULL) {
        while (p->next) p = p->next; //存在孩子节点
        p->next = stack; //链表连接
        stack = head;
    }
    return stack;
}

//(4)出栈与回收堆栈
//PopAChild从堆栈中弹出一个节点(名称),返回堆栈指针
STACK PopAChild(STACK stack, NODE child) //出栈
{
    STACK p = stack;
    if (p != NULL) //堆栈不为空
    {
        Set(child, p->node, SetValue);
        stack = p->next; //栈顶后移
        free(p); //回收节点单元
    }
    return stack; //返回堆栈头指针
}

//(5)ClearStack清空堆栈并返回空指针
STACK ClearStack(STACK stack) //回收栈空间
{
    stack = ClearBrothers((BROTHER) stack); //清除所有节点,得到空指针
    return stack; //返回空指针
}

//(6)Seach针对具体的树存储,实现可回溯,盲目,深度优先搜索。
STATUS Search(TREE tree, NODE start, NODE end) //判断节点是否在树中
{//在tree中,判断节点end是否在以节点start为根的子树中
    TREE pnode; //树分支节点
    NODE node; //节点名称
    BROTHER children; //子节点集
    STACK stack; //堆栈
    STATUS flag = FALSE; //节点在子树中的标识
    if (tree == NULL) {
        return flag; //树不存在,不在子树中
    }
    stack = (STACK) malloc(sizeof(struct BROTHERNODE)); //开辟堆栈空间
    stack->next = NULL; //堆栈只有子树根节点start,开始节点进栈
    Set(stack->node, start, SetValue);
    while (stack) //堆栈不为空
    {
        stack = PopAChild(stack, node); //出栈,保留在node中
        if (Equal(end, node, EqualFun) == 0) {
            flag = TRUE; //找到目标节点,修改状态标识
            break; //结束查找
        }
        children = ExpandNodes(tree, node); //当前节点node的下一级的所有节点
        stack = PushChildren(stack, children); //所有节点进栈
    }
    ClearStack(stack); //回收堆栈数据空间
    return flag; //返回是否找到目标节点标识
}

int main() {
    NODE start, end;
    TREE tree = NULL;
    STATUS flag = FALSE;
    //在CLion工程中,编译文件放在/home/djw931017/dup/cmake-build-debug文件夹中,此时使用相对路径./a.txt是在cmake-build-debug文件夹下寻找文件。因此,需要回到代码文件同目录下的相对路径。
    tree = CreateTree(tree, "Tree.txt");
    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);
    printf("========================");
    ClearTree(tree);
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

北城学神

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

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

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

打赏作者

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

抵扣说明:

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

余额充值