提问者:小点点

混淆-从链表中的位置K删除节点(Javascript)


我正在研究Javascript中的数据结构以准备面试,并且对解决方案中的返回值有点束手无策。详情如下。

问题(参考链接):https://www.hackerrank.com/challenges/delete-a-node-from-a-linked-list/problem

删除链表中给定位置的节点并返回对头部节点的引用。头部位于位置0。删除节点后列表可能为空。在这种情况下,返回空值。

样板:

const SinglyLinkedListNode = class {
    constructor(nodeData) {
        this.data = nodeData;
        this.next = null;
    }
};

const SinglyLinkedList = class {
    constructor() {
        this.head = null;
        this.tail = null;
    }

    insertNode(nodeData) {
        const node = new SinglyLinkedListNode(nodeData);

        if (this.head == null) {
            this.head = node;
        } else {
            this.tail.next = node;
        }

        this.tail = node;
    }
};

function printSinglyLinkedList(node, sep, ws) {
    while (node != null) {
        ws.write(String(node.data));

        node = node.next;

        if (node != null) {
            ws.write(sep);
        }
    }
}

功能答案是:

/*
 * Complete the 'deleteNode' function below.
 *
 * The function is expected to return an INTEGER_SINGLY_LINKED_LIST.
 * The function accepts following parameters:
 *  1. INTEGER_SINGLY_LINKED_LIST llist
 *  2. INTEGER position
 */


/*
 * For your reference:
 *
 * SinglyLinkedListNode {
 *     int data;
 *     SinglyLinkedListNode next;
 * }
 *
 */

function deleteNode(llist, position) {
    
    let head = llist
    let currentNode = head
    let count = 1
    
    if (head === null) return;
    if (position === 0){ 
        head = head.next
        return head
  
    if (currentNode === null || currentNode.next === null) return;
    while (count < position){
        count++
        currentNode = currentNode.next   
    }
    currentNode.next = currentNode.next.next
    return head
    }

此功能似乎通过了提供的所有测试。

我的问题是:为什么返回“head”会给我正确的答案?在函数中似乎没有改变head。返回列表也会给出正确的答案。

我已经尝试返回当前节点,但显然这提供了来自当前节点的值。


共1个答案

匿名用户

为什么返回“头”会给我正确的答案?

因为head是对一个节点的引用。只是因为该节点本身也有对下一个节点的引用,并且下一个节点也可能有对下一个节点的引用,所以这个head引用暗示了一个链表。即使head引用的节点可能仍然是同一个节点,沿着next引用链的某个地方,可能会发生变化(并且将会发生变化),因此head引用隐含地导致一个不再相同的链表。

头部的功能似乎没有改变。

除非需要移除的是head节点本身,否则这是一个真实的陈述。那是因为head只引用了第一个节点。所以如果第一个节点必须留在链表中,head不需要改变。会改变的是沿途某个地方的next引用。

返回llist也会给出正确的答案。

是的,变量head的引入实际上是矫枉过正。我们可以在没有那个额外变量的情况下解决这个问题,只需使用llist,其中这段代码使用head

这可能有助于想象这一点。

假设我们有一个包含四个节点(值为1、2、3和4)的链表,并且位置为2。然后当代码执行到达while语句时,我们有这样的状态:

llist
head  currentNode
  ↓      ↓
┌───────────┐    ┌───────────┐    ┌───────────┐    ┌───────────┐
│ value: 1  │    │ value: 2  │    │ value: 3  │    │ value: 4  │
│ next: ───────> │ next: ───────> │ next: ───────> │ next:None │
└───────────┘    └───────────┘    └───────────┘    └───────────┘ 

由于位置为2,因此此代码的目的是从该列表中删除值为3的节点。

在这种情况下,while循环将进行一次迭代,以便当前节点将引用需要删除的节点的前身:

llist
head             currentNode
  ↓                ↓
┌───────────┐    ┌───────────┐    ┌───────────┐    ┌───────────┐
│ value: 1  │    │ value: 2  │    │ value: 3  │    │ value: 4  │
│ next: ───────> │ next: ───────> │ next: ───────> │ next:None │
└───────────┘    └───────────┘    └───────────┘    └───────────┘ 

while循环之后的第一条语句对存储在该前置节点中的next引用执行“重定向”:

llist
head             currentNode
  ↓                ↓           ┌────────────────┐
┌───────────┐    ┌───────────┐ │  ┌───────────┐ │  ┌───────────┐
│ value: 1  │    │ value: 2  │ │  │ value: 3  │ └> │ value: 4  │
│ next: ───────> │ next: ──────┘  │ next: ───────> │ next:None │
└───────────┘    └───────────┘    └───────────┘    └───────────┘ 

请注意,不再有任何引用值为3的节点的内容。任何JavaScript代码都无法访问它。垃圾收集器可以释放它的内存,所以上面的情况与此没有什么不同:

llist
head             currentNode
  ↓                ↓           
┌───────────┐    ┌───────────┐    ┌───────────┐
│ value: 1  │    │ value: 2  │    │ value: 4  │
│ next: ───────> │ next: ───────> │ next:None │
└───────────┘    └───────────┘    └───────────┘ 

在所有这些过程中,head没有改变。变化反映在下一个引用链中的一个节点上,所以我们可以说链表发生了突变。

我希望这澄清了它是如何工作的。