如何将对象视为数组并遍历它并将响应保存在具有特里数据结构的 javascript 中?

问题描述

我是数据结构的初学者。我刚刚开始学习它。我正在尝试使用特里数据结构创建一个家庭结构。我指的是 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',children :[]
}]

} ... 继续

解决方法

您可以为 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。 (或者不代表女性父亲这样的概念是否可以?这些事情的细节可能会由您工作的法律框架来定义。但世界是一个复杂的地方。)

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

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

您还可以根据需要使用将性别映射到 husbandwife 的技巧。或者,如果您想表示复数婚姻,那么您可以使用一组合作伙伴(就像对待孩子一样。)

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


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

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

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

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

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


最后,让我快速回答您关于如何在函数 key 中声明 parentchildrenTrieNode 的性质的问题。

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

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

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