数据结构之多叉树c实现DFS和BFS

原文地址:
http://www.cnblogs.com/unixfy/p/3486179.html

文本文件内容:

aA 4 g cC z bBbB
z 2 f i
g 1 d
d 3 x e j
#include<stdio.h>
#include <stdlib.h>
#include <string.h>

#define MaxLenOfName 101

//定义多叉书结点结构体
typedef struct node
{
    //节点名
    char* name;
    //子节点个数
    int n_children;
    //记录该节点在多叉树中的层数
    int level;
    // 指向其自身的子节点,children一个数组,该数组中的元素时node_t*指针
    struct node** children;
}NODE;// 对结构体重命名

      // 实现一个栈,用于后续操作
typedef struct stack_t
{
    NODE** array; // array是个数组,其元素为NODE*型指针
    int    index; // 指示栈顶元素
    int    size;  // 栈的大小
} STACK; // 重命名

         // 实现一个队列,用于后续操作
typedef struct queue_t
{
    NODE** array; // array是个数组,其内部元素为NODE*型指针
    int    head;  // 队列的头
    int    tail;  // 队列的尾
    int    num;   // 队列中元素的个数
    int    size;  // 队列的大小
} QUEUE;

// 这里的栈和队列,都是用动态数组实现的,另一种实现方式是用链表


// 内存分配函数
void* util_malloc(int size)
{
    void* ptr = malloc(size);

    if (ptr == NULL) // 如果分配失败,则终止程序
    {
        printf("Memory allocation error!\n");
        exit(EXIT_FAILURE);
    }

    // 分配成功,则返回
    return ptr;
}

// 字符串赋值函数
// 对strdup函数的封装,strdup函数直接进行字符串赋值,不用对被赋值指针分配空间
// 比strcpy用起来方便,但其不是标准库里面的函数
// 用strdup函数赋值的指针,在最后也是需要free掉的
char* util_strdup(char* src)
{
    char* dst = _strdup(src);

    if (dst == NULL) // 如果赋值失败,则终止程序
    {
        printf("Memroy allocation error!\n");
        exit(EXIT_FAILURE);
    }

    // 赋值成功,返回
    return dst;
}

// 对fopen函数封装
FILE* util_fopen(char* name, char* access)
{
    FILE* fp = fopen(name, access);

    if (fp == NULL) // 如果打开文件失败,终止程序
    {
        printf("Error opening file %s!\n", name);
        exit(EXIT_FAILURE);
    }

    // 打开成功,返回
    return  fp;
}

// 实现一些栈操作

// 栈的初始化
STACK* STACKinit(int size) // 初始化栈大小为size
{
    STACK* sp;

    sp = (STACK*)util_malloc(sizeof(STACK));
    sp->size = size;
    sp->index = 0;
    sp->array = (NODE**)util_malloc(size * sizeof(NODE*));

    return sp;
}

// 检测栈是否为空
// 如果为空返回1,否则返回0
int STACKempty(STACK* sp)
{
    if (sp == NULL || sp->index <= 0) // 空
    {
        return 1;
    }
    return 0;
}

// 压栈操作
int STACKpush(STACK* sp, NODE* data)
{
    if (sp == NULL || sp->index >= sp->size) // sp没有被初始化,或者已满
    {
        return 0; // 压栈失败
    }

    sp->array[sp->index++] = data; // 压栈
    return 1;
}

// 弹栈操作
int STACKpop(STACK* sp, NODE** data_ptr)
{
    if (sp == NULL || sp->index <= 0) // sp为初始化,或者为空没有元素
    {
        return 0;
    }

    *data_ptr = sp->array[--sp->index]; // 弹栈
    return 1;
}

// 将栈消毁
void STACKdestroy(STACK* sp)
{
    free(sp->array);
    free(sp);
}
// 以上是栈的操作

// 实现队列的操作
QUEUE* QUEUEinit(int size)
{
    QUEUE* qp;

    qp = (QUEUE*)util_malloc(sizeof(QUEUE));
    qp->size = size;
    qp->head = qp->tail = qp->num = 0;
    qp->array = (NODE**)util_malloc(size * sizeof(NODE*));

    return qp;
}

// 入队列
int QUEUEenqueue(QUEUE* qp, NODE* data)
{
    if (qp == NULL || qp->num >= qp->size) // qp未初始化或已满
    {
        return 0; // 入队失败
    }

    qp->array[qp->tail] = data; // 入队,tail一直指向最后一个元素的下一个位置
    qp->tail = (qp->tail + 1) % (qp->size); // 循环队列
    ++qp->num;
    return 1;
}

// 出队列
int QUEUEdequeue(QUEUE* qp, NODE** data_ptr)
{
    if (qp == NULL || qp->num <= 0) // qp未初始化或队列内无元素
    {
        return 0;
    }

    *data_ptr = qp->array[qp->head]; // 出队
    qp->head = (qp->head + 1) % (qp->size); // 循环队列
    --qp->num;

    return 1;
}

// 检测队列是否为空
int QUEUEempty(QUEUE* qp)
{
    if (qp == NULL || qp->num <= 0)
    {
        return 1;
    }

    return 0;
}

// 销毁队列
void QUEUEdestroy(QUEUE* qp)
{
    free(qp->array);
    free(qp);
}
// 以上是队列的有关操作实现

// 生成多叉树节点
NODE* create_node()
{
    NODE* q;

    q = (NODE*)util_malloc(sizeof(NODE));
    q->n_children = 0;
    q->level = -1;
    q->children = NULL;

    return q;
}

// 按节点名字查找
NODE* search_node_r(char name[MaxLenOfName], NODE* head)
{
    NODE* temp = NULL;
    int i = 0;

    if (head != NULL)
    {
        if (strcmp(name, head->name) == 0) // 如果名字匹配
        {
            temp = head;
        }
        else // 如果不匹配,则查找其子节点
        {
            for (i = 0; i < head->n_children && temp == NULL/*如果temp不为空,则结束查找*/; ++i)
            {
                temp = search_node_r(name, head->children[i]); // 递归查找子节点
            }
        }
    }

    return temp; // 将查找到的节点指针返回,也有可能没有找到,此时temp为NULL
}


// 从文件中读取多叉树数据,并建立多叉树
void read_file(NODE** head, char* filename)
{
    NODE* temp = NULL;
    int i = 0, n = 0;
    char name[MaxLenOfName], child[MaxLenOfName];
    FILE* fp;

    fp = util_fopen(filename, "r"); // 打开文件

    while (fscanf(fp, "%s %d", name, &n) != EOF) // 先读取节点名字和当前节点的子节点个数
    {
        if (*head == NULL) // 若为空
        {
            temp = *head = create_node();   // 生成一个新节点
            temp->name = util_strdup(name); // 赋名
        }
        else
        {
            temp = search_node_r(name, *head); // 根据name找到节点
                                               // 这里默认数据文件是正确的,一定可以找到与name匹配的节点
                                               // 如果不匹配,那么应该忽略本行数据
        }
        // 找到节点后,对子节点进行处理
        temp->n_children = n;
        temp->children = (NODE**)malloc(n * sizeof(NODE*));
        if (temp->children == NULL) // 分配内存失败
        {
            fprintf(stderr, "Dynamic allocation error!\n");
            exit(EXIT_FAILURE);
        }

        // 如果分配成功,则读取后面的子节点,并保存
        for (i = 0; i < n; ++i)
        {
            fscanf(fp, "%s", child); // 读取子节点
            temp->children[i] = create_node(); // 生成子节点
            temp->children[i]->name = util_strdup(child); // 读子节点赋名
        }
    }

    // 读取完毕,关闭文件
    fclose(fp);
}

// 实现函数1
// 将多叉树中的节点,按照深度进行输出
// 实质上实现的是层次优先遍历
void f1(NODE* head)
{
    NODE* p = NULL;
    QUEUE* q = NULL; // 定义一个队列
    STACK* s = NULL; // 定义一个栈
    int i = 0;

    q = QUEUEinit(100); // 将队列初始化大小为100
    s = STACKinit(100); // 将栈初始化大小为100

    head->level = 0; // 根节点的深度为0

                     // 将根节点入队列
    QUEUEenqueue(q, head);

    // 对多叉树中的节点的深度值level进行赋值
    // 采用层次优先遍历方法,借助于队列
    while (QUEUEempty(q) == 0) // 如果队列q不为空
    {
        QUEUEdequeue(q, &p); // 出队列
        for (i = 0; i < p->n_children; ++i)
        {
            p->children[i]->level = p->level + 1; // 对子节点深度进行赋值:父节点深度加1
            QUEUEenqueue(q, p->children[i]);      // 将子节点入队列
        }
        STACKpush(s, p); // 将p入栈
    }

    while (STACKempty(s) == 0) // 不为空
    {
        STACKpop(s, &p); // 弹栈
        fprintf(stdout, "   %d %s\n", p->level, p->name);
    }

    QUEUEdestroy(q); // 消毁队列
    STACKdestroy(s); // 消毁栈
}

// 我的实现函数1
// 将多叉树中的节点,按照深度进行输出
// 实质上实现的是层次优先遍历
void myf1(NODE* head)
{
    NODE* p = NULL;
    QUEUE* q = NULL; // 定义一个队列
    QUEUE* myQ = NULL;
    //STACK* s = NULL; // 定义一个栈
    int i = 0;

    q = QUEUEinit(100); // 将队列初始化大小为100
    myQ = QUEUEinit(100);
    //s = STACKinit(100); // 将栈初始化大小为100

    head->level = 0; // 根节点的深度为0

                     // 将根节点入队列
    QUEUEenqueue(q, head);

    // 对多叉树中的节点的深度值level进行赋值
    // 采用层次优先遍历方法,借助于队列
    while (QUEUEempty(q) == 0) // 如果队列q不为空
    {
        QUEUEdequeue(q, &p); // 出队列
        for (i = 0; i < p->n_children; ++i)
        {
            p->children[i]->level = p->level + 1; // 对子节点深度进行赋值:父节点深度加1
            QUEUEenqueue(q, p->children[i]);      // 将子节点入队列
        }
        QUEUEenqueue(myQ, p);
        //STACKpush(s, p); // 将p入栈
    }

    while (!QUEUEempty(myQ))
    {
        QUEUEdequeue(myQ, &p);
        fprintf(stdout, "   %d %s\n", p->level, p->name);
    }
    //while (STACKempty(s) == 0) // 不为空
    //{
    //  STACKpop(s, &p); // 弹栈
    //  fprintf(stdout, "   %d %s\n", p->level, p->name);
    //}

    QUEUEdestroy(q); // 消毁队列
    QUEUEdestroy(myQ);
    //STACKdestroy(s); // 消毁栈
}

// 实现函数2
// 找到从根节点到叶子节点路径上节点名字字母个数最大的路径
// 实质上实现的是深度优先遍历
void f2(NODE* head, char* str, char** strBest, int level)
{
    int   i = 0;
    char* tmp = NULL;

    if (head == NULL)
    {
        return;
    }

    tmp = (char*)util_malloc((strlen(str) + strlen(head->name) + 1/*原程序中未加1*/) * sizeof(char));
    sprintf(tmp, "%s%s", str, head->name);

    if (head->n_children == 0)
    {
        if (*strBest == NULL || strlen(tmp) > strlen(*strBest))
        {
            free(*strBest);
            *strBest = util_strdup(tmp);
        }
    }

    for (i = 0; i < head->n_children; ++i)
    {
        f2(head->children[i], tmp, strBest, level + 1);
    }

    free(tmp);
}

// 消毁树
void free_tree_r(NODE* head)
{
    int i = 0;
    if (head == NULL)
    {
        return;
    }

    for (i = 0; i < head->n_children; ++i)
    {
        free_tree_r(head->children[i]);
    }

    free(head->name);
    // free(head->children); // 消毁子节点指针数组
    free(head);
}

int main(int argc, char* argv[])
{
    NODE* head = NULL;
    char* strBest = NULL;

    /*if (argc != 2)
    {
        fprintf(stderr, "Missing parameters!\n");
        exit(EXIT_FAILURE);
    }*/

    //read_file(&head, argv[1]);
    read_file(&head, "C:\\Users\\单凯\\Desktop\\data.txt");

    fprintf(stdout, "f1:\n");
    f1(head);

    fprintf(stdout, "myf1:\n");
    myf1(head);

    f2(head, "", &strBest, 0);
    fprintf(stdout, "f2:\n   %s\n", strBest);

    free_tree_r(head);

    return EXIT_SUCCESS;
}

结果:
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值