为什么 this.tail 在我的链表 class 中更改了 this.head 的 属性?

Why does this.tail change the property of this.head in my Linkedlist class?

考虑一个 LinkedList class,它模仿如下的 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.next 等等?

当构造函数有 运行、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 到任何节点的路径。