【问题标题】:Converting a Binary Tree into an Array (and later save) in C在 C 中将二叉树转换为数组(并稍后保存)
【发布时间】:2013-01-05 18:57:43
【问题描述】:

所以,我正在做这个客户应用程序,您可以在其中创建/修改/搜索/列出客户。后来这扩展到通过订单将客户与产品联系起来等等,但我现在的重点只是客户。我已经创建了一个二叉树并且所有这些功能都可以工作,但是我需要一种方法来存储创建的客户以供下次使用。

我想以某种方式我必须将所有客户(在每个节点中找到)转移到一个数组中,然后将该数组写入文件“customer.dat”。花了很多时间。这里有一些代码 sn-ps 可以帮助更好地理解我有什么函数和结构:

typedef struct customer
{
    char Name[MAXNAME];
    char Surname[MAXNAME];
    char ID[MAXID];
    char Address[MAXADDRESS];
} Cstmr;

typedef struct node
{
    Cstmr item;
    struct node * left;
    struct node * right;
} Node;

typedef struct tree
{
    Node * root;
    int size;
} Tree;

以上是结构体,Node 包含 Cstmr 类型的项和链接的左右节点。树包含一个根节点和大小。

void Traverse (const Tree * ptree, void (* pfun)(Cstmr item))
{
    if (ptree != NULL)
        InOrder(ptree->root,pfun);
}

static void InOrder(const Node * root, void(* pfun)(Cstmr item))
{
    if (root != NULL)
    {
    InOrder(root->left, pfun);
    (*pfun)(root->item);
    InOrder(root->right, pfun);
    }
} 

这些功能用于列出客户并添加功能

void printItem(Cstmr C)
{ 
    printf("%-10s %-10s %-8s\n", C.Name, C.Surname, C.ID);
}

最后通过写来执行

Traverse(tree,printItem); 

我试图将 printItem 更改为另一个函数以添加到数组(输出到文件而不是屏幕),但现在事情变得太复杂了!有什么建议吗?

【问题讨论】:

  • 我认为您不需要先将其转换为数组。您可以直接在此处写入文件,而不是执行 return (*pfun)(root->item) 。

标签: c serialization data-structures binary-tree


【解决方案1】:

如果不必将数据存储在数组中,您可以调整 printItem 函数以使用 fprintf 而不是 printf 直接写入文件。您还需要将文件句柄传递给您的函数。

您的InOrder 遍历也有点问题。 InOrder(root->right, pfun); 行永远不会执行,因为您直接返回它上面。仅删除 return 关键字。一旦你确认你的 InOrder 遍历工作正常,尝试进行适当的修改以将其打印到文件中(最好使用函数指针,就像你对 printItem 所做的那样)。

如果您确实想写入数组,则需要知道树中的项目总数或使用动态容器(在 C 中不可用,除非您自己编写),但您可以完成没有数组你需要什么。

【讨论】:

  • 是的,我确实想到了,但是对于这个任务,它应该使用 fread/fwrite 代替。那个返回是一个错误,我以前在玩我的代码,但是谢谢!
  • 使用 fwrite 代替 fprintf 应该没有问题。要点是您可以编写一个包含 fwrite 的函数并将其传递给您的遍历函数。祝你好运!
【解决方案2】:

首先,您的逻辑中有一个错误,用于进行按顺序遍历。我在这里转载了代码:

static void InOrder(const Node * root, void(* pfun)(Cstmr item))
{
    if (root != NULL)
    {
        InOrder(root->left, pfun);
        return (*pfun)(root->item);
        InOrder(root->right, pfun);
    }
} 

请注意,您在逻辑中间有一个return 语句。这意味着在您下降到左子树之后,您将评估当前节点处的函数并返回,然后继续处理右子树。删除 return 语句应该可以解决此问题。

至于您的问题的一个很好的解决方案 - 有一个相当简单的递归算法可以将已排序的元素数组转换为完美平衡的二叉搜索树。如果您能够按顺序将二叉搜索树的每个元素写入文件,则可以通过将其读入一个巨大的数组来重建该树,然后从该数组重建 BST。

从数组中重建二叉搜索树的逻辑在伪代码中给出:

Node* arrayToTree(Item array[], int start, int end) {
    if (start >= end) return NULL;

    int mid = (start + end) / 2;
    Node* result  = /* Convert array[mid] to a Node. */
    result->left  = arrayToTree(array, start, mid);
    result->right = arrayToTree(array, mid + 1, end);

    return result;
}

希望这会有所帮助!

【讨论】:

  • 谢谢,我会试一试,起初没有返回,我在玩代码时添加了它。非常感谢!
【解决方案3】:

正如其他人所说,“返回”会禁用以下代码的执行。您的编译器也应该弹出一个错误,因为您在 void 函数中返回一个值。

只需使用类似这样的东西来写出你的数据(没有数组):

/* sure it may be "static"? */
static void InOrder(const Node * root)
{
    if (root != NULL)
    {
    InOrder(root->left);

    Cstmr cust = root->item;
    BUFFERED_WRITE(cust.Name, strlen(cust.Name) + 1);
    BUFFERED_WRITE(cust.Surname, strlen(cust.Surname) + 1);
    BUFFERED_WRITE(cust.ID, strlen(cust.Surname) + 1);
    BUFFERED_WRITE(cust.Address, strlen(cust.Address) + 1);

    InOrder(root->right, pfun);
    }
}

BUFFERED_WRITE 函数只是一个对文件描述符进行缓冲写入的函数的占位符。也可以是“write(export_fd, cust.Name, strlen(cust.Name) + 1)”之类的东西。

导入反之亦然,需要额外的边界检查。

【讨论】:

  • 我试试看,谢谢!那个回报不应该在那里,我的错。我在发布之前正在玩代码
【解决方案4】:

你不能在你的中序函数中间返回——它不会执行你遍历的“右边”。由于您的函数实际上并没有返回任何内容,因此只需删除多余的“返回”应该没有问题。

像这样:

static void InOrder(const Node * root, void(* pfun)(Cstmr item))
{
    if (root != NULL)
    {
    InOrder(root->left, pfun);
    (*pfun)(root->item);
    InOrder(root->right, pfun);
    }
} 

我还建议您在编译器中启用警告并实际修复您收到的任何警告,因为编译器应该能够注意到您在此处返回是不正确的 [即使它只是“您在应该返回值时返回值” t"]。

【讨论】:

  • 我的错,直到我开始使用代码时,回报才出现
【解决方案5】:

感谢大家的帮助,我已经找到了tree to array问题的解决方案,其实很简单。

首先我声明了 Cstmr 类型的数组:

Cstmr save[MAXITEMS];

然后我所要做的就是复制并稍微编辑 printItem(Cstmr C) 函数。而不是 printf,它只是添加到 save[MAXITEMS]。

void saveItem(Cstmr C)
{
    int size = TreeItemCount(&id);
    save[size] = C;
    printf("%s...saved", save[size].Name); //just to check that it works
}

后来我创建了另一个函数来检查树是否为空,然后使用 Traverse 函数:

void SaveCustomers(Tree*pt)
{
    if (TreeIsEmpty(pt))
        puts("Nothing to save!");
    else
        Traverse(pt,saveItem);
}

感谢所有快速回复!

【讨论】:

    猜你喜欢
    • 2015-06-17
    • 2023-03-18
    • 2015-05-07
    • 1970-01-01
    • 2013-04-17
    • 2020-08-24
    • 1970-01-01
    • 2012-02-28
    • 2011-07-24
    相关资源
    最近更新 更多