06_JavaScript data structure and algorithm one-way linked list

Keywords: Algorithm data structure linked list

JavaScript data structure and algorithm (VI) one way linked list

Cognitive linked list

Linked lists and arrays

Like arrays, linked lists can be used to store a series of elements, but the implementation mechanism of linked lists and arrays is completely different.

array

  • Storing multiple elements, arrays (or lists) are probably the most commonly used data structures.

  • Almost every programming language has a default implementation array structure, which provides a convenient [] syntax to access array elements.

  • Array disadvantages:

    To create an array, you need to apply for a continuous memory space (a whole block of memory) and the size is fixed. When the current array cannot meet the capacity requirements, it needs to be expanded. (generally, it is to apply for a larger array, such as double, and then copy the elements in the original array)

    Inserting data at the beginning or middle of an array is expensive and requires a large number of element displacements.

Linked list

  • To store multiple elements, another option is to use a linked list.

  • Unlike arrays, elements in linked lists do not have to be contiguous spaces in memory.

  • Each element of the linked list consists of a node that stores the element itself and a reference to the next element (called a pointer in some languages).

  • Advantages of linked list:

    The memory space does not have to be continuous. It can make full use of the computer memory to realize flexible memory dynamic management.

    The linked list does not have to be sized at the time of creation, and the size can be extended indefinitely.

    When inserting and deleting data, the time complexity of linked list can reach O(1), which is much more efficient than array.

  • Disadvantages of linked list:

    When accessing an element in any location, you need to access it from scratch. (cannot skip the first element to access any element)

    The elements cannot be accessed directly through the subscript value. You need to access them one by one from the beginning until the corresponding elements are found.

    Although you can easily reach the next node, it is difficult to go back to the previous node.

Unidirectional linked list

A one-way linked list is similar to a train. There is a locomotive. The locomotive will connect to a node. There are passengers on the node, and this node will connect to the next node, and so on.

  • Train structure of linked list

  • Data structure of linked list

    The head attribute points to the first node of the linked list.
    The last node in the linked list points to null.
    When there is no node in the linked list, the head directly points to null.

  • Structure after adding data to the train

Common operations in linked list

  • append(element) adds a new item to the end of the linked list.
  • insert(position, element) inserts a new item into a specific position in the linked list.
  • get(position) gets the element of the corresponding position.
  • indexOf(element) returns the index of the element in the linked list. If there is no such element in the linked list, - 1 is returned.
  • update(position, element) modifies an element at a certain position.
  • removeAt(position) removes an item from a specific location in the linked list.
  • remove(element) removes an item from the linked list.
  • isEmpty() returns trun if the linked list does not contain any elements, and false if the length of the linked list is greater than 0.
  • size() returns the number of elements contained in the linked list, similar to the length attribute of the array.
  • toString() because the linked list item uses the Node class, you need to override the default toString method inherited from the JavaScript object to output only the value of the element.

Encapsulation of unidirectional linked list

Create a one-way linked list class

First create a one-way linked list class LinkedList, add basic attributes, and then gradually realize the common methods of one-way linked list.

class LinkedList {
  // The initial linked list length is 0
  length = 0;

  // The initial head is null, and the head points to the first node of the linked list
  head = null;

  // Internal class (Node in the linked list)
  Node = class {
    data;
    next = null;
    constructor(data) {
      this.data = data;
    }
  };
}

Implement the append() method

code implementation
// append() appends data to the end of the linked list
append(data) {

    // 1. Create a new node
    const newNode = new this.Node(data);

    // 2. Append new node
    if (this.length === 0) {

    // When the length of the linked list is 0, that is, when there is only head
    this.head = newNode;

    } else {
    // When the length of the linked list is greater than 0, a new node is added on the last side
    let currentNode = this.head;

    // When currentNode.next is not empty,
    // Find the last node in sequence, that is, when the next of the node is null
    while (currentNode.next !== null) {
        currentNode = currentNode.next;
    }

    // The next of the last node points to the new node
    currentNode.next = newNode;
    }

    // 3. After adding a new node, the length of the linked list + 1
    this.length++;

}
Process Mapping
  • First, let currentNode point to the first node.

  • Through the while loop, make the currentNode point to the last node. Finally, make the last node point to the new node newNode through currentNode.next = newNode.

Code test
const linkedList = new LinkedList();
// Test append method
linkedList.append("A");
linkedList.append("B");
linkedList.append("C");
console.log(linkedList);

Implement the toString() method

code implementation
toString() {
    let currentNode = this.head;
    let result = '';

    // Traverse all nodes and splice them into strings until the node is null
    while (currentNode) {
    result += currentNode.data + ' ';
    currentNode = currentNode.next;
    }

    return result;
}
Code test
// Test toString method
console.log(linkedList.toString()); //--> AA BB CC

Implement the insert() method

code implementation
// insert() inserts the node at the specified position
insert(position, data) {
    // position the location of the newly inserted node
    // position = 0 indicates that the new insert is the first node
    // position = 1 means the second node after the new insertion, and so on

    // 1. The position is judged to be out of bounds. It cannot be less than 0 or greater than the length of the linked list
    if (position < 0 || position > this.length) return false;

    // 2. Create a new node
    const newNode = new this.Node(data);

    // 3. Insert node
    if (position === 0) { // position = 0
    // Let the next of the new node point to the original first node, that is, head
    newNode.next = this.head;

    // head is assigned as newNode
    this.head = newNode;
    } else { // 0 < position < = length

    // Initialize some variables
    let currentNode = this.head; // The current node is initialized to head
    let previousNode = null; // The previous node of head is null
    let index = 0; // The index of head is 0

    // Traverse between 0 and position, and constantly update currentNode and previousNode
    // Until you find the location to insert
    while (index++ < position) {
        previousNode = currentNode;
        currentNode = currentNode.next;
    }

    // Insert new nodes between the current node and the previous node of the current node, that is, their change direction
    newNode.next = currentNode;
    previousNode.next = newNode;
    }

    // Update linked list length
    this.length++;
    return newNode;
}
Code test
// Test insert method
linkedList.insert(0, "123");
linkedList.insert(2, "456");
console.log(linkedList.toString()); //--> 123 AA 456 BB CC

Implement the getData() method

Gets the data of the specified position.

code implementation
getData(position) {
    // 1. position out of bounds judgment
    if (position < 0 || position >= this.length) return null;

    // 2. Gets the data of the specified position node
    let currentNode = this.head;
    let index = 0;

    while (index++ < position) {
    currentNode = currentNode.next;
    }
    // 3. Return data
    return currentNode.data;
}
Code test
// Test getData method
console.log(linkedList.getData(0)); //--> 123
console.log(linkedList.getData(1)); //--> AA

Implement indexOf() method

indexOf(data) returns the index of the specified data. If not, it returns - 1.

code implementation
indexOf(data) {

    let currentNode = this.head;
    let index = 0;

    while (currentNode) {
    if (currentNode.data === data) {
        return index;
    }
    currentNode = currentNode.next;
    index++;
    }

    return -1;
}
Code test
// Test indexOf method
console.log(linkedList.indexOf("AA")); //--> 1
console.log(linkedList.indexOf("ABC")); //--> -1

Implement the update() method

update(position, data) modifies the data of the node at the specified location.

code implementation
update(position, data) {
    // When it comes to position, cross-border judgment should be made
    // 1. position out of bounds judgment
    if (position < 0 || position >= this.length) return false;

    // 2. After loop traversal, find the node of the specified position
    let currentNode = this.head;
    let index = 0;
    while (index++ < position) {
    currentNode = currentNode.next;
    }

    // 3. Modify node data
    currentNode.data = data;

    return currentNode;
}
Code test
// Test update method
linkedList.update(0, "12345");
console.log(linkedList.toString()); //--> 12345 AA 456 BB CC
linkedList.update(1, "54321");
console.log(linkedList.toString()); //--> 12345 54321 456 BB CC

Implement the removeAt() method

removeAt(position) deletes the node at the specified location.

code implementation
removeAt(position) {
    // 1. position out of bounds judgment
    if (position < 0 || position >= this.length) return null;

    // 2. Delete the specified position node
    let currentNode = this.head;
    if (position === 0) {
    // position = 0
    this.head = this.head.next;

    } else {
    // Position > 0
    // Through circular traversal, find the node with the specified position and assign it to currentNode

    let previousNode = null;
    let index = 0;

    while (index++ < position) {
        previousNode = currentNode;
        currentNode = currentNode.next;
    }

    // The trick is to make the next of the previous node point to the next of the current node, which is equivalent to deleting the current node.
    previousNode.next = currentNode.next;
    }

    // 3. Update linked list length - 1
    this.length--;

    return currentNode;
}
Code test
// Test removeAt method
linkedList.removeAt(3);
console.log(linkedList.toString()); //--> 12345 54321 456 CC

Implement the remove() method

remove(data) deletes the node where the specified data resides.

code implementation
remove(data) {
    this.removeAt(this.indexOf(data));
}
Code test
// Test remove method
linkedList.remove("CC");
console.log(linkedList.toString()); //--> 12345 54321 456

Implement isEmpty() method

isEmpty() determines whether the linked list is empty.

code implementation
isEmpty() {
    return this.length === 0;
}
Code test
// Test isEmpty method
console.log(linkedList.isEmpty()); //--> false

Implement the size() method

size() gets the length of the linked list.

code implementation
size() {
    return this.length;
}
Code test
// Test method
console.log(linkedList.size()); //--> 3

Complete implementation

class LinkedList {
  // The initial linked list length is 0
  length = 0;

  // The initial head is null, and the head points to the first node of the linked list
  head = null;

  // Internal class (Node in the linked list)
  Node = class {
    data;
    next = null;

    constructor(data) {
      this.data = data;
    }
  };

  // ------------Common operations of linked list ------------//

  // append() appends data to the end of the linked list
  append(data) {
    // 1. Create a new node
    const newNode = new this.Node(data);

    // 2. Append new node
    if (this.length === 0) {
      // When the length of the linked list is 0, that is, when there is only head
      this.head = newNode;
    } else {
      // When the length of the linked list is greater than 0, a new node is added on the last side
      let currentNode = this.head;

      // When currentNode.next is not empty,
      // Find the last node in sequence, that is, when the next of the node is null
      while (currentNode.next !== null) {
        currentNode = currentNode.next;
      }

      // The next of the last node points to the new node
      currentNode.next = newNode;
    }

    // 3. After adding a new node, the length of the linked list + 1
    this.length++;
  }

  // insert() inserts the node at the specified position
  insert(position, data) {
    // position the location of the newly inserted node
    // position = 0 indicates that the new insert is the first node
    // position = 1 means the second node after the new insertion, and so on

    // 1. The position is judged to be out of bounds. It cannot be less than 0 or greater than the length of the linked list
    if (position < 0 || position > this.length) return false;

    // 2. Create a new node
    const newNode = new this.Node(data);

    // 3. Insert node
    if (position === 0) {
      // position = 0
      // Let the next of the new node point to the original first node, that is, head
      newNode.next = this.head;

      // head is assigned as newNode
      this.head = newNode;
    } else {
      // 0 < position < = length

      // Initialize some variables
      let currentNode = this.head; // The current node is initialized to head
      let previousNode = null; // The previous node of head is null
      let index = 0; // The index of head is 0

      // Traverse between 0 and position, and constantly update currentNode and previousNode
      // Until you find the location to insert
      while (index++ < position) {
        previousNode = currentNode;
        currentNode = currentNode.next;
      }

      // Insert new nodes between the current node and the previous node of the current node, that is, their change direction
      newNode.next = currentNode;
      previousNode.next = newNode;
    }

    // Update linked list length
    this.length++;
    return newNode;
  }

  // getData() gets the data at the specified location
  getData(position) {
    // 1. position out of bounds judgment
    if (position < 0 || position >= this.length) return null;

    // 2. Gets the data of the specified position node
    let currentNode = this.head;
    let index = 0;

    while (index++ < position) {
      currentNode = currentNode.next;
    }

    // 3. Return data
    return currentNode.data;
  }

  // indexOf() returns the index of the specified data. If not, it returns - 1.
  indexOf(data) {
    let currentNode = this.head;
    let index = 0;

    while (currentNode) {
      if (currentNode.data === data) {
        return index;
      }
      currentNode = currentNode.next;
      index++;
    }

    return -1;
  }

  // update() modifies the data of the node at the specified location
  update(position, data) {
    // When it comes to position, cross-border judgment should be made
    // 1. position out of bounds judgment
    if (position < 0 || position >= this.length) return false;

    // 2. After loop traversal, find the node of the specified position
    let currentNode = this.head;
    let index = 0;
    while (index++ < position) {
      currentNode = currentNode.next;
    }

    // 3. Modify node data
    currentNode.data = data;

    return currentNode;
  }

  // removeAt() deletes the node at the specified location
  removeAt(position) {
    // 1. position out of bounds judgment
    if (position < 0 || position >= this.length) return null;

    // 2. Delete the specified position node
    let currentNode = this.head;
    if (position === 0) {
      // position = 0
      this.head = this.head.next;
    } else {
      // Position > 0
      // Through circular traversal, find the node with the specified position and assign it to currentNode

      let previousNode = null;
      let index = 0;

      while (index++ < position) {
        previousNode = currentNode;
        currentNode = currentNode.next;
      }

      // The trick is to make the next of the previous node point to the next of the current node, which is equivalent to deleting the current node.
      previousNode.next = currentNode.next;
    }

    // 3. Update linked list length - 1
    this.length--;

    return currentNode;
  }

  // remove() deletes the node of the specified data
  remove(data) {
    this.removeAt(this.indexOf(data));
  }

  // isEmpty() determines whether the linked list is empty
  isEmpty() {
    return this.length === 0;
  }

  // size() gets the length of the linked list
  size() {
    return this.length;
  }

  // toString() the linked list data is returned as a string
  toString() {
    let currentNode = this.head;
    let result = "";

    // Traverse all nodes and splice them into strings until the node is null
    while (currentNode) {
      result += currentNode.data + " ";
      currentNode = currentNode.next;
    }

    return result;
  }
}

Posted by _will on Fri, 01 Oct 2021 14:48:26 -0700