【问题标题】:Why does this.tail change the property of this.head in my Linkedlist class?为什么 this.tail 在我的 Linkedlist 类中更改 this.head 的属性?
【发布时间】:2021-04-06 09:16:39
【问题描述】:

考虑一个模仿 Linkedlist 数据结构的 LinkedList 类,如下所示:

class LinkedList {
  constructor(value) {
    this.head = {
      value: value,
      next: null
    };
    this.tail = this.head;
    this.length = 1;
  }
  append(value) {
    const newNode = {
      value: value,
      next: null
    }
    this.tail.next = newNode; // why does this change head.next ?
    this.tail = newNode;
    this.length++;
    return this;
  }
}

let myLinkedList = new LinkedList(10);
myLinkedList.append(5);

日志输出

LinkedList {
  head: { value: 10, next: { value: 5, next: null } },
  tail: { value: 5, next: null },
  length: 2
}

我看到this.tail.next 也会改变tail 的下一个属性(然后this.tail = newNode 会将tail 重新分配给newNode)。我在这里不明白的是为什么this.tail.next 也会改变this.head 的下一个属性?

另外,当将另一个数字附加到列表myLinkedList.append(16) 时,它会不断更新head 的下一个属性,如下所示:

LinkedList {
  head: { value: 10, next: { value: 5, next: [Object] } },
  tail: { value: 16, next: null },
  length: 3
}

也许一个可能的原因与我定义this.tail = this.head 的构造函数有关?但我不太确定,因为这个只分配头到尾的值。

总结一下,我的问题是为什么this.tail.next = newNode会改变head的next属性?另外,在追加其他值时,为什么会改变 head.next.next 等等?

【问题讨论】:

  • "也许一个可能的原因与我定义 this.tail = this.head 的构造函数有关?" - 是的,当然这就是原因:两个属性现在都拥有相同的对象引用,然后您将更改从这两个地方引用的该对象的 .next 属性。
  • 第一个追加是预期更新头部(因为开头头部尾部)。第二个追加不会更新head,它只是console.log 以这种方式表示它。也就是说,你的代码没问题。
  • (你的代码唯一奇怪的是链表总是至少包含一个元素并且不能为空)
  • 这不是我的代码(是的,它应该以这种方式工作,但我不明白它是如何工作的,所以我问:D(经过一番思考后))所以实际上它只是对象引用。 head 的引用对象被分配给 tail。当我执行tail.next 时,它还会更新它引用的对象,即head.next。对吗?

标签: javascript class oop linked-list this


【解决方案1】:

当构造函数运行时,this.tailthis.head 引用同一个对象,因此您对 this.tail.next 所做的任何分配都在 this.head 中可见,因为这实际上是对正在被调用的同一个对象的引用变异了。

这可能有助于形象化。一旦构造函数运行,我们就会遇到这种情况:

     this.head
      ↓          
    ┌───────────┐
    │ value: 10 │
    │ next: null│
    └───────────┘
      ↑
     this.tail

那么append(5)会先创建一个新节点:

     this.head        newNode
      ↓                ↓
    ┌───────────┐    ┌───────────┐
    │ value: 10 │    │ value: 5  │
    │ next:null │    │ next:null │
    └───────────┘    └───────────┘
      ↑
     this.tail

然后this.tail.next = newNode;被执行,这是对第一个对象中next属性的修改:

     this.head        newNode
      ↓                ↓
    ┌───────────┐    ┌───────────┐
    │ value: 10 │    │ value: 5  │
    │ next: ———————→ │ next:null │
    └───────────┘    └───────────┘
      ↑
     this.tail

确实,这也改变了this.head.next...,因为它只是相同的属性。

然后this.tail = newNode;被执行:

     this.head        newNode
      ↓                ↓
    ┌───────────┐    ┌───────────┐
    │ value: 10 │    │ value: 5  │
    │ next: ———————→ │ next:null │
    └───────────┘    └───────────┘
                       ↑
                      this.tail

下次调用append时,second对象的next属性将被更新,因此我们得到:

     this.head                         newNode
      ↓                                 ↓
    ┌───────────┐    ┌───────────┐    ┌───────────┐
    │ value: 10 │    │ value: 5  │    │ value: 16 │
    │ next: ———————→ │ next: ———————→ │ next:null │
    └───────────┘    └───────────┘    └───────────┘
                                        ↑
                                       this.tail

是的,这个更改也可以从this.head 追踪,因为...它是一个链表...所以它应该是可追踪的。由于每个next 属性都指向下一个节点,因此您可以找到从head 到任何节点的路径。

【讨论】:

  • 感谢您提供通俗易懂的插图!我真的很感激!
猜你喜欢
  • 1970-01-01
  • 2012-02-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-29
  • 1970-01-01
  • 2015-08-12
  • 1970-01-01
相关资源
最近更新 更多