0

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

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

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 更改为另一个函数以添加到数组(输出到文件而不是屏幕),但现在事情变得太复杂了!有什么建议么?

4

5 回答 5

0

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

像这样:

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

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

于 2013-01-05T19:01:41.800 回答
0

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

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;
}

希望这可以帮助!

于 2013-01-05T19:03:16.697 回答
0

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

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

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

于 2013-01-05T19:09:33.273 回答
0

正如其他人所说,“返回”会禁用以下代码的执行。你的编译器也应该弹出一个错误,因为你在一个 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)”之类的东西。

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

于 2013-01-05T19:31:20.027 回答
0

感谢大家的帮助,我已经找到了树到数组问题的解决方案,实际上很简单。

首先,我声明了 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);
}

感谢所有快速回复!

于 2013-01-06T10:41:29.487 回答