C中的Trie机制
Trie mechanism in C
我完全是初学者,正在尝试创建用于拼写检查的 trie 结构。我已经阅读了很多文档,但我的理解仍然存在差距,如果有人解释我将不胜感激。对不起,我的问题看起来很菜鸟,但我基本上是菜鸟。
#include <stdio.h>
#include <stdlib.h>
#define LENGTH 45
#define N 27
char word[LENGTH + 1];
typedef struct trie
{
char data; //letter(character)
struct trie* child[N]; //array of pointers to the next trie
int leaf; //is word ending here
}trie;
我将所有新尝试的 int leaf
设置为 0
。当我完成插入单词后,我将 int leaf
更改为 1
这样我就知道我正在检查的单词是否存在。
如果我把 leaf = 1
留给另一个词呢?程序如何知道叶子对于其他单词是否正确?我应该制作一个指针数组还是应该用不同的方法重新开始? TIA
my trie node sketch
我快速查看了您的结构并尝试实施快速而肮脏的插入和查找。我将名称 'leaf' 更改为 'flag',因为它不是一片叶子,而是一个标志,表明我们有一个词,而不是一些前缀。
#define N 26
typedef struct trie {
char data;
struct trie* children[N];
int flag;
} trie;
// all zero data...
trie TRIE_TEMPLATE;
#define edge_idx(c) (c - 'a')
trie *next(trie *node, char c)
{
trie *n = node->children[edge_idx(c)];
if (!n) {
// no such edge yet...
n = malloc(sizeof *n);
if (!n) abort(); // error handling
*n = TRIE_TEMPLATE;
n->data = c;
node->children[edge_idx(c)] = n;
}
return n;
}
void insert(trie *root, char const *word)
{
trie *n = root;
for (char const *c = word; *c; c++) {
n = next(n, *c);
}
n->flag = 1; // tag final node as a word
}
int contains(trie *root, char const *word)
{
trie *n = root;
for (char const *c = word; *c; c++) {
n = n->children[edge_idx(*c)];
if (!n) return 0;
}
return n->flag;
}
我还没有很好地测试它,所以不要相信它,但正如你所看到的,我使用一个全为零的模板节点(一个全局变量)来初始化新节点。这会将数据、子项和标志设置为零。 (它不符合标准,因为 NULL 和零不一定是同一件事,但它可能是,并且对于快速原型来说很好)。
因此节点最初将标志设置为零。在插入中,我在字符串的末尾将标志设置为 1,因此只有最后一个节点获得标志。没有任何通往那里的节点。如果我们插入现有节点的前缀,我们不会创建新节点,而是在适当的节点中设置标志。如果我们在trie已经有前缀的地方添加一个单词,它不会修改现有的节点。
至少,它应该是这样工作的,通过这个快速测试,这是我所看到的:
int main(void)
{
trie root = TRIE_TEMPLATE;
insert(&root, "foo");
insert(&root, "bar");
printf("fo %s in trie\n",
contains(&root, "fo") ? "is" : "is not");
printf("foo %s in trie\n",
contains(&root, "foo") ? "is" : "is not");
printf("ba %s in trie\n",
contains(&root, "ba") ? "is" : "is not");
printf("bar %s in trie\n",
contains(&root, "bar") ? "is" : "is not");
// bar and baz share a prefix, but that is fine...
printf("baz %s in trie\n",
contains(&root, "baz") ? "is" : "is not");
insert(&root, "baz");
printf("baz %s in trie\n",
contains(&root, "baz") ? "is" : "is not");
// after inserting ba, it is there, and bar and baz are
// also there. It doesn't matter that ba is a prefix
insert(&root, "ba");
printf("ba %s in trie\n",
contains(&root, "ba") ? "is" : "is not");
printf("bar %s in trie\n",
contains(&root, "bar") ? "is" : "is not");
printf("baz %s in trie\n",
contains(&root, "baz") ? "is" : "is not");
// foobar already has a prefix in the trie, foo,
// but when we insert it, that is not a problem.
printf("foobar %s in trie\n",
contains(&root, "foobar") ? "is" : "is not");
insert(&root, "foobar");
printf("foobar %s in trie\n",
contains(&root, "foobar") ? "is" : "is not");
return 0;
}
我完全是初学者,正在尝试创建用于拼写检查的 trie 结构。我已经阅读了很多文档,但我的理解仍然存在差距,如果有人解释我将不胜感激。对不起,我的问题看起来很菜鸟,但我基本上是菜鸟。
#include <stdio.h>
#include <stdlib.h>
#define LENGTH 45
#define N 27
char word[LENGTH + 1];
typedef struct trie
{
char data; //letter(character)
struct trie* child[N]; //array of pointers to the next trie
int leaf; //is word ending here
}trie;
我将所有新尝试的 int leaf
设置为 0
。当我完成插入单词后,我将 int leaf
更改为 1
这样我就知道我正在检查的单词是否存在。
如果我把 leaf = 1
留给另一个词呢?程序如何知道叶子对于其他单词是否正确?我应该制作一个指针数组还是应该用不同的方法重新开始? TIA
my trie node sketch
我快速查看了您的结构并尝试实施快速而肮脏的插入和查找。我将名称 'leaf' 更改为 'flag',因为它不是一片叶子,而是一个标志,表明我们有一个词,而不是一些前缀。
#define N 26
typedef struct trie {
char data;
struct trie* children[N];
int flag;
} trie;
// all zero data...
trie TRIE_TEMPLATE;
#define edge_idx(c) (c - 'a')
trie *next(trie *node, char c)
{
trie *n = node->children[edge_idx(c)];
if (!n) {
// no such edge yet...
n = malloc(sizeof *n);
if (!n) abort(); // error handling
*n = TRIE_TEMPLATE;
n->data = c;
node->children[edge_idx(c)] = n;
}
return n;
}
void insert(trie *root, char const *word)
{
trie *n = root;
for (char const *c = word; *c; c++) {
n = next(n, *c);
}
n->flag = 1; // tag final node as a word
}
int contains(trie *root, char const *word)
{
trie *n = root;
for (char const *c = word; *c; c++) {
n = n->children[edge_idx(*c)];
if (!n) return 0;
}
return n->flag;
}
我还没有很好地测试它,所以不要相信它,但正如你所看到的,我使用一个全为零的模板节点(一个全局变量)来初始化新节点。这会将数据、子项和标志设置为零。 (它不符合标准,因为 NULL 和零不一定是同一件事,但它可能是,并且对于快速原型来说很好)。
因此节点最初将标志设置为零。在插入中,我在字符串的末尾将标志设置为 1,因此只有最后一个节点获得标志。没有任何通往那里的节点。如果我们插入现有节点的前缀,我们不会创建新节点,而是在适当的节点中设置标志。如果我们在trie已经有前缀的地方添加一个单词,它不会修改现有的节点。
至少,它应该是这样工作的,通过这个快速测试,这是我所看到的:
int main(void)
{
trie root = TRIE_TEMPLATE;
insert(&root, "foo");
insert(&root, "bar");
printf("fo %s in trie\n",
contains(&root, "fo") ? "is" : "is not");
printf("foo %s in trie\n",
contains(&root, "foo") ? "is" : "is not");
printf("ba %s in trie\n",
contains(&root, "ba") ? "is" : "is not");
printf("bar %s in trie\n",
contains(&root, "bar") ? "is" : "is not");
// bar and baz share a prefix, but that is fine...
printf("baz %s in trie\n",
contains(&root, "baz") ? "is" : "is not");
insert(&root, "baz");
printf("baz %s in trie\n",
contains(&root, "baz") ? "is" : "is not");
// after inserting ba, it is there, and bar and baz are
// also there. It doesn't matter that ba is a prefix
insert(&root, "ba");
printf("ba %s in trie\n",
contains(&root, "ba") ? "is" : "is not");
printf("bar %s in trie\n",
contains(&root, "bar") ? "is" : "is not");
printf("baz %s in trie\n",
contains(&root, "baz") ? "is" : "is not");
// foobar already has a prefix in the trie, foo,
// but when we insert it, that is not a problem.
printf("foobar %s in trie\n",
contains(&root, "foobar") ? "is" : "is not");
insert(&root, "foobar");
printf("foobar %s in trie\n",
contains(&root, "foobar") ? "is" : "is not");
return 0;
}