this.tail.next是如何将节点添加到this.head的



我目前正在看一个关于单链表的教程,导师对单链表的实现让我对推送方法非常困惑,我不知道发生了什么。

在这里:

class Node {
constructor(val) {
this.val = val;
this.next = null;
}
}
class SinglyLinkedList {
constructor() {
this.length = 0;
this.head = null;
this.tail = null;
}
push(val) {
let node = new Node(val);
if (!this.head) {
this.head = node;
this.tail = this.head;
} else {
this.tail.next = node;
this.tail = node;
}
this.length++;
return this;
}
let list = new SinglyLinkedList();
list.push('hello');
list.push('goodbye');
}

在这段代码中,我很难理解this.tail.next是如何向this.head插入新节点的。对于第一次插入,很明显,头和尾都指向同一个对象,即第一个节点。但在第一次推送后,this.tail被分配了一个新对象,与this.head不同,但this.tail.next仍然将新节点添加到this.head的最后一个下一个属性中这是怎么发生的?

它可能有助于可视化push方法是如何完成的。

当用let list = new SinglyLinkedList()构造(空(链表时,我们会遇到这种情况(我省略了length属性,因为它与问题无关(:

list (this)
↓
┌────────────┐
│ head: null │
│ tail: null │
└────────────┘ 

然后主代码调用list.push('hello')。这从创建一个节点实例开始,本地变量node引用它:

list              node
↓                 ↓
┌────────────┐    ┌──────────────┐
│ head: null │    │ val: "hello" │
│ tail: null │    │ next: null   │
└────────────┘    └──────────────┘ 

由于this.headnull,所以执行if块。此状态下的第一个this.head = node结果:

list              node
↓                 ↓
┌────────────┐    ┌──────────────┐
│ head: ────────> │ val: "hello" │
│ tail: null │    │ next: null   │
└────────────┘    └──────────────┘ 

然后将该引用复制到this.tail:中

list              node
↓                 ↓
┌────────────┐    ┌──────────────┐
│ head: ────────> │ val: "hello" │
│ tail: ────────> │ next: null   │
└────────────┘    └──────────────┘ 

在更新长度之后,push('hello')调用完成了它的工作,并且node变量被释放。然后执行CCD_ 12。我们再次从创建一个节点开始:

list                                  node
↓                                     ↓
┌────────────┐    ┌──────────────┐    ┌────────────────┐
│ head: ────────> │ val: "hello" │    │ val: "goodbye" │
│ tail: ────────> │ next: null   │    │ next: null     │
└────────────┘    └──────────────┘    └────────────────┘ 

现在this.head不是null,所以我们进入else块。执行this.tail.next = node,因此我们得到(只需按照从thistailnext的箭头,其中null被替换为对node的引用(:

list                                  node
↓                                     ↓
┌────────────┐    ┌──────────────┐    ┌────────────────┐
│ head: ────────> │ val: "hello" │    │ val: "goodbye" │
│ tail: ────────> │ next: ──────────> │ next: null     │
└────────────┘    └──────────────┘    └────────────────┘ 

然后执行CCD_ 22。这将用另一个引用替换现有引用,恢复list.tail总是引用最后一个节点的不变量:

list                                  node
↓                                     ↓
┌────────────┐    ┌──────────────┐    ┌────────────────┐
│ head: ────────> │ val: "hello" │    │ val: "goodbye" │
│ tail: ───────┐  │ next: ──────────> │ next: null     │
└────────────┘ │  └──────────────┘┌─> └────────────────┘ 
└──────────────────┘

同样,node变量的生命周期在执行结束时结束,因此我们只剩下list和上面描述的依赖结构。

但在第一次推送后,this.tail被分配了一个新对象,与this.head不同

这是不对的。第一次插入后,this.headthis.tail指向同一对象:

if (!this.head) {
this.head = node;
this.tail = this.head;
}

对于任何后续插入,this.tail都指向先前插入的节点。在将新节点分配给this.tail:之前,更新this.tail.next

this.tail.next = node;
this.tail = node;

这意味着当第二次插入发生时,this.tailthis.head仍然指向同一对象。执行CCD_ 35,从而也更新CCD_。只有在之后的,才会更新this.tail以指向新节点。

最新更新