【问题标题】:Ternary search trie insert function issues三元搜索 trie 插入函数问题
【发布时间】:2014-03-29 16:31:42
【问题描述】:

所以我正在尝试进行三元搜索尝试。现在,我只处理插入功能。我已经在线了解了三元搜索树的基本思想。我知道一个根节点有 3 个叶子,如果字符出现在根之前,它会向左,之后 - 右,如果它与根匹配,它会进入中间叶子。所以我的主要目标是制作一个程序,可以为拼写错误的用户输入的单词建议单词。但目前我只是在做三元搜索尝试。我使用 trie 制作字典,使用它检查用户输入的单词以建议下一个最佳选择。但是现在,只是将一些字符输入到三元树中,当我显示它时,它应该按顺序显示。 我不是 100% 确定我关于中间叶子的逻辑。现在我的程序在运行时给了我一些与输入的最后一个字符有关的垃圾无限值。我不知道我哪里出错了。有人可以指出我在哪里犯了错误吗?另外,你能告诉我我写的任何逻辑是否错误吗?我的意思是,你不必给我代码或任何东西,因为一旦我知道我哪里出错了,我觉得我有能力自己做,但是如果有人可以帮助我找到我的错误,那会帮助我很多!

整个代码:

#include <stdio.h>
#include <stdlib.h>  //Because usage of malloc gives warnings without this header
typedef struct tstree
{
    struct tstree *lokid;
    struct tstree *hikid;
    struct tstree *eqkid;
    char letter;
}node;
node *head = NULL;
int count = 0;
int insert(char x, node **head)
{
    if (*head==NULL)            //If it's the first element
    {
        *head = malloc(sizeof(node));   
        (*head)->letter = x;
        count++;            
        (*head)->lokid = (*head)->hikid = (*head)->eqkid = NULL;            //Assign all 3 to null initially
    }
    else if ((*head)->letter == x)          //If equal, insert below
    insert(x , &((*head)->eqkid) );
    else if ((*head)->letter > x)   //If inserted char comes before current val, insert to left
    insert(x,&(*head)->lokid);
    else
    insert(x,&(*head)->hikid);              //Else to the right
    return 0;
}
void display(node *head)
{
    if(head)
    {
        display(head->lokid);               //print in infix order
        printf("%c ",head->letter);
        display(head->hikid);
    }
    //return 0;
}
int main()
{   
    int op;
    char num;
    printf("\nWelcome Fabiz. Hope it meets your expectations!\n");
    while(1)
    {
        printf("\n1. To insert an element\n2. Display the tree\n3. Exit\nOption :");
        scanf("%d",&op);
        switch(op)
        {
            case 1:
            {
                system("clear");
                printf("\nEnter the element : ");
                scanf(" %c",&num);
                insert(num,&head);
                break;
            }
            case 2:
            {
                system("clear");
                if(count == 0)
                printf("\nEmpty tree\n");
                else
                {
                    printf("Display in order..\n");
                    display(head);
                }
                break;
            }
            default: exit(0);
        }
    }
    return 0;
}

我正在使用 Geany 文本编辑器并且在 Linux Mint 上。我遇到了一个问题,当我点击显示功能时,编译器只会打印我无限输入的最后一个字符。 任何帮助都会非常有帮助! 谢谢!

【问题讨论】:

    标签: c trie ternary-search-tree


    【解决方案1】:

    您的显示功能错误。循环条件永远不会计算为假:

    while(&(*head)->lokid!=NULL && &(*head)->hikid!=NULL)
    

    这不是你想要的。 &amp;(*head)-&gt;lokid&amp;(*head)-&gt;hikid 都不会评估为 NULL。如果head 不是NULL,那么&amp;(*head)-&gt;lokid 就是与*head 相同的地址加上lokidstruct tstree 中的偏移量。请注意,您的循环甚至没有可能使条件为假的更新语句,并且它没有任何break - 它注定要失败。

    事实上,您甚至根本不需要循环。要按顺序打印,这就是您所需要的:

    void display(node *head) {
        if (head) {
            display(head->lokid);
            printf("%c ", head->letter);
            display(head->hikid);
        }
    }
    

    请注意,传递node **(我将其更改为node *)没有任何意义,返回值应为void

    更新

    您的insert() 函数是正确的,但是您在main 中使用了错误的变量。来自insert() 的递归基中的这个赋值导致了意外行为:

    temp = *head = malloc(sizeof(node));
    

    请注意,每次遇到基本情况时,都会为 *headtemp 分配一个新节点,从而失去对之前存储在 temp 中的任何内容的引用。然后你打电话给display(temp)。是的,您构建了 trie,但您从最后插入的节点开始打印它 - 不是您想要的。

    相反,您应该使用全局变量 head 调用 display,这是您的 trie 的正确根:

    display(head);
    

    调用 insert 时也会发生同样的情况。您不想从最后添加的节点开始插入新字母,而是希望从根节点开始添加它。 main() 应该有这个:

    insert(num, &head);
    

    在我们讨论的时候,请注意temp 是完全没有必要的。你不需要它。 insert 通过引用操作全局头部,temp 在这里没有用(实际上它引入了一个错误)。

    在 main 中更改这两行就足够了,在这里进行了测试,它的工作原理就像一个魅力。

    【讨论】:

    • 哦!天。我最初实际上是这样做的。这只是我的实验。但是,是的,我将其改回 if (head) 并使其成为单指针。这只是一个单指针类型函数的原因是因为我们真的不需要编辑 trie 中的任何内容/添加到 trie 中,因此不需要节点的实际地址,对吗?此外,在实施您的建议时,我得到了一些东西,但它只给了我输入的最后一个字符。
    • 是的,因为你永远不会改变传递的参数,所以不需要接收指向指针的指针。
    • @icyfox26 基本上,C 没有内置的传递引用调用。如果要调用f(a) 并查看f()a 所做的修改,则必须传递一个指向a 的指针,即&amp;a。这就是我们在insert() 中所做的事情。由于您可以修改insert() 中的头部,并且您希望调用者看到这些更改,因此您必须将指针传递给头部。头部是node *,所以指向头部的指针是node **。同样,我们这样做是为了通过引用调用。如果你只通过了head,那么在返回后就不会看到headinsert 的变化。 [继续]
    • 显示函数只是读取head的内容,所以不需要通过引用传递。让我们看一个例子:head 是一个指针,想象它是地址 0x21。当您拨打display(head)时,会复制此地址;在display() 内部,head 是 0x21,但是如果您将 head 更改为 0x30,则只有 display 中的本地副本会被更改 - 调用者中的 head 仍然是 0x21。这对于display() 是可以的,但对于insert 则不行,因为我们想更改head。我们实际上传递了存储地址0x21的地址(指向指针的指针)。
    • 现在,如果您将 0x21 更改为其他值,因为您正在写入存储调用者指针的地址,即使在函数之后也会看到返回。希望现在清楚了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-11
    • 1970-01-01
    • 1970-01-01
    • 2019-08-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多