Home > Mobile >  Confusion - Delete node from position K in Linked List (Javascript)
Confusion - Delete node from position K in Linked List (Javascript)

Time:09-09

I'm studying data structures in Javascript to prep for interviews and am a bit stumped by a return value in the solution. Details below.

The Problem (Reference Link): https://www.hackerrank.com/challenges/delete-a-node-from-a-linked-list/problem

Delete the node at a given position in a linked list and return a reference to the head node. The head is at position 0. The list may be empty after you delete the node. In that case, return a null value.

Boilerplate:

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);
        }
    }
}

And the functional answer:

/*
 * 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
    }

This function appears to pass all tests provided.

My question is: Why does returning 'head' give me the correct answer? It does not appear that head is being altered in the function. Returning llist also gives a correct answer as well.

I've tried returning currentNode but obviously that provides the values from currentNode on.

CodePudding user response:

Why does returning 'head' give me the correct answer?

Because head is a reference to a node. It is only because that node itself also has a reference to a next node, and that next node might also have a reference to a next node, that this head reference implies a linked list. Even though the node that head references might remain the same node, somewhere along that chain of next references, there might be a change (and will be), and so implicitly the head reference leads to a linked list that is no longer the same.

It does not appear that head is being altered in the function.

Unless it is the head node itself that needs to be removed, this is a true statement. That is because head only references the first node. So if the first node has to stay in the linked list, head does not need to change. What will change is a next reference somewhere along the way.

Returning llist also gives a correct answer as well.

Yes, the introduction of the variable head is actually overkill. We can solve this without that extra variable and just use llist where this code uses head.

It may help to visualise this.

Let's say we have a linked list with four nodes (with values 1, 2, 3 and 4) and position is 2. Then when the code execution arrives at the while statement, we have this state:

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

As position is 2, the purpose of this code is to remove the node with value 3 from this list.

The while loop will make one iteration in this case, so that currentNode will reference the predecessor of the node that needs to be removed:

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

The first statement after the while loop performs a "redirection" of the next reference that is stored in that predecessor node:

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

Note that there is nothing anymore that references the node with value 3. It has become unreachable to any JavaScript code. The garbage collector can free its memory, so the above is no different from this:

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

In all this process, head did not change. The change is reflected in one of the nodes that is in the chain of next references, and so we could say the linked list was mutated.

I hope this clarifies how it works.

  • Related