【问题标题】:How do the pointers work in the Linked List?指针在链表中是如何工作的?
【发布时间】:2021-01-18 17:35:15
【问题描述】:

我刚刚学习了链表反向功能,但我认为还不够了解。 第一个,第二个,临时变量是指针对吗?他们会在while循环中改变,对吗?我很不明白它是如何扭转它的。我用调试器多次检查它,但仍然不明白指针如何在这里工作以及它如何改变它的下一个值。 这是链接列表:

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;
          this.tail = newNode;
          this.length++;
          return this;
        }
        printList() {
          const array = [];
          let currentNode = this.head;
          while(currentNode !== null){
              array.push(currentNode.value)
              currentNode = currentNode.next
          }
          return array;
        }
        reverse() {
          if (!this.head.next) {
            return this.head;
          }
          var first = this.head; // 1
          this.tail = this.head; 
          var second = first.next; // 2
          while(second) { // Runs 2 times
            const temp = second.next; // 3
            second.next = first; // 3 => 1
            first = second; // 1 => 2
            second = temp;  2 => 3
          }
      
          this.head.next = null;
          this.head = first;
          return this;
        }
    }
    
    let myLinkedList = new LinkedList(1);
    myLinkedList.append(2)
    myLinkedList.append(3)
    myLinkedList.reverse()

如果您能逐步向我解释,我将不胜感激。 非常感谢您, ~ 卢卡斯

【问题讨论】:

  • 仅供参考,JavaScript 有引用而不是指针。
  • 有一个不错的解释here

标签: javascript linked-list


【解决方案1】:

如果您能一步一步地向我解释,我将不胜感激

让我们从插入值 1、2 和 3 之后的状态开始:

 head                             tail
  ↓                                ↓
┌──────────┐    ┌──────────┐    ┌──────────┐
│ value: 1 │    │ value: 2 │    │ value: 3 │
│ next: ——————→ │ next: ——————→ │ next:null│
└──────────┘    └──────────┘    └──────────┘

while 循环开始时,我们引入了更多的引用。请注意second 是如何通过first.next 到达那里的,即它引用与第一个对象中的next 引用相同的对象。另外,this.tail 被设置为它的最终值,所以我们不必再考虑这个了:

 first
 head
 tail            second
  ↓               ↓
┌──────────┐    ┌──────────┐    ┌──────────┐
│ value: 1 │    │ value: 2 │    │ value: 3 │
│ next: ——————→ │ next: ——————→ │ next:null│
└──────────┘    └──────────┘    └──────────┘

现在让我们想象一下while循环中每个赋值的效果:

第一次迭代:

const temp = second.next; 初始化一个新引用,它等于second 引用的对象中的next 引用:

 first
 head
 tail            second          temp
  ↓               ↓               ↓ 
┌──────────┐    ┌──────────┐    ┌──────────┐
│ value: 1 │    │ value: 2 │    │ value: 3 │
│ next: ——————→ │ next: ——————→ │ next:null│
└──────────┘    └──────────┘    └──────────┘

second.next = first; 会将“第二个”对象的next 引用重定向到其前面的 节点:

 first
 head
 tail            second          temp
  ↓               ↓               ↓ 
┌──────────┐    ┌──────────┐    ┌──────────┐
│ value: 1 │    │ value: 2 │    │ value: 3 │
│ next: ——————→ │          │    │ next:null│
│          │ ←——— next     │    │          │
└──────────┘    └──────────┘    └──────────┘

注意这如何将第三个对象从列表中分离出来。幸运的是,我们仍然可以通过temp 引用它,否则我们将永远失去它。当然,这正是引入 temp 变量的原因。

接下来的两个语句将为下一轮循环准备:firstsecond 引用更新为引用下一个节点。这样循环将能够从头到尾遍历列表。

first = second; 导致此状态:

 head            first
 tail            second          temp
  ↓               ↓               ↓ 
┌──────────┐    ┌──────────┐    ┌──────────┐
│ value: 1 │    │ value: 2 │    │ value: 3 │
│ next: ——————→ │          │    │ next:null│
│          │ ←——— next     │    │          │
└──────────┘    └──────────┘    └──────────┘

second = temp; 之后我们有:

 head                            second
 tail            first           temp
  ↓               ↓               ↓ 
┌──────────┐    ┌──────────┐    ┌──────────┐
│ value: 1 │    │ value: 2 │    │ value: 3 │
│ next: ——————→ │          │    │ next:null│
│          │ ←——— next     │    │          │
└──────────┘    └──────────┘    └──────────┘

第二次迭代:

const temp = second.next; 初始化一个新的引用(旧的temp 只存在于上一次迭代中)。但是现在second 指的是列表中的最后一个节点,它的next 属性是null,所以tempnull

 head
 tail            first           second          temp: null
  ↓               ↓               ↓ 
┌──────────┐    ┌──────────┐    ┌──────────┐
│ value: 1 │    │ value: 2 │    │ value: 3 │
│ next: ——————→ │          │    │ next:null│
│          │ ←——— next     │    │          │
└──────────┘    └──────────┘    └──────────┘

second.next = first; 会将“第二个”对象(现在是第三个对象)的 next 引用重定向到它的 preceding 节点,因为这就是 first 所引用的:

 head
 tail            first           second          temp: null
  ↓               ↓               ↓ 
┌──────────┐    ┌──────────┐    ┌──────────┐
│ value: 1 │    │ value: 2 │    │ value: 3 │
│ next: ——————→ │          │    │          │
│          │ ←——— next     │ ←——— next     │
└──────────┘    └──────────┘    └──────────┘

接下来的两个语句将为可能的下一轮循环准备循环:firstsecond 引用再次更新以引用下一个节点:

first = second; 导致此状态:

 head                            first
 tail                            second          temp: null
  ↓                               ↓ 
┌──────────┐    ┌──────────┐    ┌──────────┐
│ value: 1 │    │ value: 2 │    │ value: 3 │
│ next: ——————→ │          │    │          │
│          │ ←——— next     │ ←——— next     │
└──────────┘    └──────────┘    └──────────┘

second = temp; 之后我们有:

 head                                            second: null
 tail                            first           temp: null
  ↓                               ↓ 
┌──────────┐    ┌──────────┐    ┌──────────┐
│ value: 1 │    │ value: 2 │    │ value: 3 │
│ next: ——————→ │          │    │          │
│          │ ←——— next     │ ←——— next     │
└──────────┘    └──────────┘    └──────────┘

现在循环条件为假,所以我们退出循环。此时我们确定所有的next 引用都被反转了,除了第一个节点中的那个应该变成null。而且我们还需要调整head,使其指向最后一个节点。这正是循环之后剩下的两个赋值执行的,所以我们最终得到:

                                 first           second: null
 tail                            head           (temp is out of scope)
  ↓                               ↓ 
┌──────────┐    ┌──────────┐    ┌──────────┐
│ value: 1 │    │ value: 2 │    │ value: 3 │
│ next:null│ ←——— next     │ ←——— next     │
└──────────┘    └──────────┘    └──────────┘

现在列表反转了,headtail 都引用了它们应该引用的节点。

算法

最后,该算法维护了三个沿列表移动的引用:firstsecondtemp。只要它们不变成null,它们总是引用列表中的3 个连续节点,并且在每次迭代中它们引用列表下方的一个节点。这三人紧密合作,从“左到右”行走。

second 引用的每个节点都将更新其next 引用,因此它引用其前一个节点。 temp 确保它仍然可以沿着列表的其余部分移动,即使它已被 next 的此更新分离。

【讨论】:

  • 哇!非常感谢!
猜你喜欢
  • 1970-01-01
  • 2023-01-03
  • 1970-01-01
  • 1970-01-01
  • 2011-05-30
  • 2015-06-17
  • 2016-10-19
  • 1970-01-01
  • 2015-01-20
相关资源
最近更新 更多