考虑一个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.next等等?
当构造函数运行时,this.tail
和this.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
时,第二个对象的next
属性将被更新,因此我们得到:
this.head newNode
↓ ↓
┌───────────┐ ┌───────────┐ ┌───────────┐
│ value: 10 │ │ value: 5 │ │ value: 16 │
│ next: ———————→ │ next: ———————→ │ next:null │
└───────────┘ └───────────┘ └───────────┘
↑
this.tail
是的,这种变化也可以追溯到this.head
,因为。。。它是一个链接列表。。。因此它应该是可追踪的。由于每个next
属性都指向下一个节点,因此可以找到从head
到任何节点的路径。