【问题标题】:How an object can be treated as an array and iterates through it and save the response in javascript with trie data structure?如何将对象视为数组并对其进行迭代并将响应保存在具有 trie 数据结构的 javascript 中?
【发布时间】:2021-04-13 12:02:30
【问题描述】:

我是数据结构的初学者。我刚刚开始学习它。我正在尝试使用 trie 数据结构创建一个家庭结构。我在这里指的是 github 上的文档: https://gist.github.com/tpae/72e1c54471e88b689f85ad2b3940a8f0#file-trie-js-L44

这是一些代码。

function TrieNode(key) {
  // the "key" value will be the character in sequence
  this.key = key;
  
  // we keep a reference to parent
  this.parent = null;
  
  // we have hash of children
  this.children = {};
  
  // check to see if the node is at the end
  this.end = false;
}

// -----------------------------------------

// we implement Trie with just a simple root with null value.
function Trie() {
  this.root = new TrieNode(null);
}

// inserts a word into the trie.
// time complexity: O(k), k = word length
Trie.prototype.insert = function(word) {
  var node = this.root; // we start at the root ????
  
  // for every character in the word
  for(var i = 0; i < word.length; i++) {
    // check to see if character node exists in children.
    if (!node.children[word[i]]) {
      // if it doesn't exist, we then create it.
      node.children[word[i]] = new TrieNode(word[i]);
      
      // we also assign the parent to the child node.
      node.children[word[i]].parent = node;
    }
    
    // proceed to the next depth in the trie.
    node = node.children[word[i]];
    
    // finally, we check to see if it's the last word.
    if (i == word.length-1) {
      // if it is, we set the end flag to true.
      node.end = true;
    }
  }
};

我的疑问是在插入节点时,我们如何遍历单词并创建一个节点:

 node.children[word[i]]

这对我来说是无法理解的。还有如何在函数 TrieNode 中声明键、父、子?它们是否被视为在创建对象时初始化的全局变量?为什么在其他函数中声明了 root 以及它是如何工作的? 附言我必须像这样插入树:

var familyHead = {
      name: 'Chit',
      gender:'Male',
      grandfather:'',
      grandmother:'',
      father:'Shan',
      mother:'Anga',
      wife:'Amba',
    children : [
    {
      name: 'Chit',
      gender:'Male',
      grandfather:'',
      grandmother:'',
      father:'Shan',
      mother:'Anga',
      wife:'Amba',
      children :[]
}]

} ...继续

【问题讨论】:

  • node.children[word[i]] 有什么不明白的地方?如果word 是类似HELLO 的字符串,那么word[i] 是该字符串中的一个字母,例如Hchildren 是一个 JavaScript 对象,所以我们讨论的是该对象的属性,其键为 H,即 TrieNode。你了解this.children = {} 的作用吗?你追那么多吗?您能具体说明您在这里做什么或不了解什么吗?
  • 感谢@Wyck 在这里的输入。雅现在我明白了。我的另一个疑问是,我们通过调用 insert 函数直接插入了“hello”、“apple”等词或任何字符串。我想了解插入方法内部发生了什么?意味着孩子对象存储和父母是什么?另外,你能看到我的家谱结构吗?如何插入这样的树,然后根据输入作为名称获取关系?
  • 当心:the difference between tree and trie 您可能只需要一个 tree 来存储 family treetrie 用于通过重用这些单词的公共前缀子串的表示来有效地存储单词集合。例如,单词grandfathergrandmother 将只存储一次常用字母g,r,a,n,d。将单词 fathermother 的表示存储在 trie 中单词 grand 的表示下方。
  • 明白了!你能告诉我函数 TrieNode(key) {} 吗?在这种情况下如何直接声明父、子对象?它们是全局变量吗?为什么首先不使用 'var' 或 'let' 关键字声明然后使用它? @Wyck
  • 它们不是全局变量。它们是对象的属性。我写了一个完整的答案。如果您的问题只是您不了解构造函数、属性和变量的工作原理,请原谅我的冗长。如果是这种情况,那么就该语言的特定方面提出一个更有针对性的问题,这会给您带来麻烦,范围大大缩小。

标签: javascript node.js trie family-tree


【解决方案1】:

您可以为 JavaScript 对象提供引用相关对象的属性来完成您想要形成的图表。 (不要使用 trie,那是为了别的东西。

例如,为每个人创建一个纯 JavaScript 对象。

// Create an to represent each unique person
const chit = { name: 'Chit', gender: 'male' };
const shan = { name: 'Shan', gender: 'male' };
const anga = { name: 'Anga', gender: 'female' };
const anba = { name: 'Anba', gender: 'female' };

// Give them properties that establish the relationships as required
chit.father = shan;
chit.mother = anga;
chit.wife = amba;
/// ...

到目前为止,这些都是单向链接。如果您还希望父亲拥有一组他们的孩子,那么您必须在构建时维护这个数据结构。您将需要一些方便的功能来执行此操作。

要在两个人之间建立亲子关系,至少必须发生一件事:

  1. 子对象应将其parent 属性设置为父对象。

如果出于方便或效率的考虑,您还想维护每个人拥有的孩子列表的缓存,那么:

  1. 父对象应将子对象添加到其children 数组中。

这样做过度指定父子关系,因此数据结构必须保持完整性。这意味着您需要提供函数以在检查不一致时对树进行修改。例如,同时尝试为同一个孩子建立两个父子关系,或者有一个没有父母的孩子可能没有意义(有人需要没有父母,因为你可以' t 存储无限深的世代历史),或者将一个人列为孩子,但不会同时将同一个孩子列为具有相同的父母。

在这样的功能中,现在可能会检查整个树的完整性,但仅检查正在修改的结构部分可能就足够了。

在建立新的父子关系时,检查孩子是否已经有了父母。如果他们这样做了,有两种策略来处理这个问题,这取决于编辑谁是父母的生物学上不可变的属性是否有意义(例如,考虑像收养这样的过程)

  1. 抛出异常。
  2. 在更新以设置新的父级之前,通过从父级的子级数组中删除子级来编辑关系。

在这里,我将提供一个允许每个性别的父母的实现。我将父母的性别映射到motherfather 属性名称只是为了说明这样做的便利性。例如,如果您发现需要代表有两个母亲的家庭,那么您将需要使用一组父母。 (但您的示例使用motherfather,我也应如此。)

const parent_property_name = {
  male: 'father',
  female: 'mother'
};

function establishParentChildRelationship(parent, child) {
   if (!child.parent) child.parent = {};
   if (child[parent_property_name[parent.gender]]) throw new Error('child already has a parent of that gender');
   child[parent_property_name[parent.gender]] = parent;
   if (!parent.children) parent.children = [];
   parent.children.push(child);
}

希望您可以看到,由于需要维护,通常不会在树中进一步存储明确的祖父祖母关系。父亲的父亲隐含地是祖父,可以这样确定。类似地,父母的兄弟姐妹是阿姨和叔叔等。表亲和第二表亲都可以仅根据亲子关系来确定。无需再次显式存储它。此外,必须考虑是否需要明确定义 motherfather,因为它们可能来自 parent + gender。 (或者不代表女性父亲这样的概念是否可以?这些事情的细节可能会由您工作的法律框架定义。但世界是一个复杂的地方。)

然而,婚姻关系(法律或宗教或其他)确实需要额外的链接。还必须考虑数据结构是否允许复数婚姻(可能需要一个数组,很像孩子)和同性婚姻('married' + 'gender' => 'husband' or 'wife')。但是对于一夫一妻制的婚姻,人们可以想象,至少,只需用maritalPartner 来表示这一点。

function establishMonogamousMaritalPartnerRelationship(person1, person2) {
   if (person1.maritalPartner || person2.maritalPartner) throw new Error('already married!');
   person1.maritalPartner = person2;
   person2.maritalPartner = person1;

您还可以根据需要使用将性别映射到husbandwife 的技巧。或者,如果你想代表一夫多妻制,那么你可以使用一组伴侣(就像对孩子所做的那样。)

数据结构可能需要再次修改,以便能够表示这些关系随时间变化的复杂性。 (例如离婚或二婚、收养、解放、性别转换或重新分配等)


让我也花点时间澄清一下 trie 的用途。 (它适用于家谱。)

trie 将单词表示为一棵树,其中每个节点都是一个字母,每个节点的父节点是它之前的字母。一个完整的单词在树中的节点上用一个标志标记,即单词的最后一个字母。

考虑一个存储单词bebeebellball的trie:

.
└── b
    ├── a
    │   └── l
    │       └── l*
    └── e*
        ├── e*
        └── l
            └── l*

标有* 的节点代表完整的单词(在您的示例中带有end 标志)。请注意,即使允许更多字母跟随它以形成不同的作品,一个单词也可能会结束。 (例如:be 是一个单词,即使它后面可能跟有 el)。每个带有end 标志的节点都代表一个由其在树中的祖先决定的词。


最后,让我快速回答一下您的问题,即keyparentchildren 在函数 TrieNode 中的声明方式。

您正在查看的是 JavaScript 构造函数。我建议你阅读the basicsthis answer

但在这种特定情况下,调用new TrieNode('X') 将创建一个基本上如下所示的 JavaScript 对象:

{ key: 'X', parent: null, children: {}, end: false }

【讨论】:

  • 我不明白这两个语句:node.children[word[i]] = new TrieNode(word[i]); // 我们还将父节点分配给子节点。 node.children[word[i]].parent = 节点;在这里,孩子的索引如何不是整数? node.children[c] 如何证明?如果插入的单词假设是 = 'chit' ?
  • 听起来你需要复习 JavaScript 的基础知识。在 JavaScript 中,对象是键值对的集合,其中键是字符串(或称为符号的一些特殊事物)。在这种情况下,“索引”是一个字符串。例如:let obj = { "H" : "Hello", "W": "World" } 给出 obj["H"] === "Hello"obj["W"] === "World",还有 obj.H === "Hello"obj.W === "World"HW 是属性键(字符串),HelloWorld 是属性值(在这种情况下也是字符串)。对于字符串,word[3] 是 word 中字符串的第 4 个字母。例如:"chit"[3] === "t"
  • node.children[word[i]] 访问wordith 字母(假设它是chit 中的t)并访问存储在@987654371 中的对象中名为t 的属性@,就像一个将字母映射到对象的字典——就像树中的节点一样。每个节点都有一个这样的字典(每个字母下面可能有另一个这样的节点,从而形成一棵树)。
猜你喜欢
  • 2018-06-21
  • 1970-01-01
  • 2020-05-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多