JavaScript Data Structure and Algorithms One-way Chain List

 

Catalog

JavaScript Data Structure and Algorithms (6) One-way Chain List

Recognition Chain List

Chain lists and arrays

Singly linked list

Common operations in a chain table

Encapsulation of one-way chain table

JavaScript Data Structure and Algorithms (6) One-way Chain List

Recognition Chain List

Chain lists and arrays

Chain lists, like arrays, can be used to store a series of elements, but they are implemented entirely differently.

array

  • Storing multiple elements, arrays (or lists) may be the most common data structure.

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

  • Array drawbacks:

    The creation of an array requires a continuous memory space (a block of memory) and is fixed in size, which needs to be expanded when the current array cannot meet capacity requirements. (Typically, a larger array is requested, such as twice, and then the elements in the original array are copied over.)

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

linked list

  • Storing multiple elements, another option is to use a chain table.

  • Unlike arrays, elements in a chain table do not have to be contiguous in memory.

  • Each element of a chain table consists of a node that stores the element itself and a reference to the next element (referred to in some languages as a pointer).

  • Chain list advantages:

    Memory space does not have to be continuous to make full use of the computer's memory for flexible dynamic memory management.

    Chain lists do not have to be sized when they are created, and sizes can extend indefinitely.

    Chain lists can be O(1) time-complex and much more efficient than arrays when inserting and deleting data.

  • Chain list shortcomings:

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

    Elements cannot be accessed directly from subscript values; they need to be accessed from scratch one by one until the corresponding element is found.

    Although it is easy to get to the next node, it is difficult to get back to the previous node.

Singly linked list

A one-way chain list is similar to a train with a locomotive that connects a node with passengers, and this node connects the next node, and so on.

  • Train structure of chain list

  • Data structure of linked list

    The head property points to the first node of the chain table.
    The last node in the list points to null. When there is no node in the list, the head er points directly to null.

  • Add data to train structure

Common operations in a chain table

  • append(element) adds a new item to the end of the list.
  • insert(position, element) inserts a new item to a specific location in the list of chains.
  • get(position) Gets the element of the corresponding position.
  • indexOf(element) returns the index of an element in a chain table. If the element is not in the chain table, it returns -1.
  • update(position, element) Modifies the element of a location.
  • removeAt(position) Removes an item from a specific location in the list of chains.
  • remove(element) Removes an item from the list of chains.
  • isEmpty() Returns true if the list does not contain any elements, false if the length of the list is greater than 0.
  • size() returns the number of elements contained in the chain table, similar to the length property of an array.
  • toString() Since chain list items use the Node class, you need to override the default toString method inherited from the JavaScript object so that it only outputs the value of the element.

Encapsulation of one-way chain table

Create a one-way chain table class

Create a one-way chain table class LinkedList, add basic properties, and then gradually implement the common methods of one-way chain table.

class LinkedList {
  // Initial list length is 0
  length = 0;

  // The initial header is null, and the header points to the first node in the chain table
  head = null;

  // Internal Class (Node in Chain List)
  Node = class {
    data;
    next = null;
    constructor(data) {
      this.data = data;
    }
  };
}

Implementing the append() method

code implementation

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

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

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

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

    } else {
    // Add a new node at the end when the chain length is greater than 0
    let currentNode = this.head;

    // When currentNode.next is not empty,
    // Find the last node in sequence, when the next 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. Chain list length + 1 after appending new nodes
    this.length++;

}

Process Mapping

  • First, point the currentNode to the first node.

  • Make the currentNode point to the last node through the while loop, and finally make the last node point to the new node, newNode, through the currentNode.next = newNode.

Code Testing

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 stitch them into strings until they are null
    while (currentNode) {
    result += currentNode.data + ' ';
    currentNode = currentNode.next;
    }

    return result;
}

Code Testing

// Test the toString method
console.log(linkedList.toString()); //--> AA BB CC

Implement insert() method

code implementation

// insert() inserts a node at a specified location
insert(position, data) {
    // position New Inserted Node Location
    // position = 0 means the first node after the new insert
    // position = 1 indicates the second node after the new insertion, and so on

    // 1. position cannot be less than 0 or greater than the length of the 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, head
    newNode.next = this.head;

    // head assigns newNode
    this.head = newNode;
    } else { // 0 < position <= length case

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

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

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

    // Update Link List Length
    this.length++;
    return newNode;
}

Code Testing

// 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 at the specified location.

code implementation

getData(position) {
    // 1. position Judgment Over Bounds
    if (position < 0 || position >= this.length) return null;

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

    while (index++ < position) {
    currentNode = currentNode.next;
    }
    // 3. Return to data
    return currentNode.data;
}

Code Testing

// Test getData method
console.log(linkedList.getData(0)); //--> 123
console.log(linkedList.getData(1)); //--> AA

Implementing the indexOf() method

indexOf(data) returns the index of the specified data, or -1 if not.

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 Testing

// Testing 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 specified location node.

code implementation

update(position, data) {
    // Cross-border judgment is required when position ing is involved
    // 1. position Judgment Over Bounds
    if (position < 0 || position >= this.length) return false;

    // 2. Traverse through the loop to find the node that specified the position
    let currentNode = this.head;
    let index = 0;
    while (index++ < position) {
    currentNode = currentNode.next;
    }

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

    return currentNode;
}

Code Testing

// Testing the 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 a node at a specified location.

code implementation

removeAt(position) {
    // 1. position Judgment Over Bounds
    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
    // Loop through to find the node of the specified position and assign it to currentNode

    let previousNode = null;
    let index = 0;

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

    // Clever enough, having the next of the previous node point to the next of the current node is equivalent to deleting the current node.
    previousNode.next = currentNode.next;
    }

    // 3. Update Chain List Length-1
    this.length--;

    return currentNode;
}

Code Testing

// Test the removeAt s 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 Testing

// Test the remove method
linkedList.remove("CC");
console.log(linkedList.toString()); //--> 12345 54321 456

Implement the isEmpty() method

isEmpty() determines if the chain list is empty.

code implementation

isEmpty() {
    return this.length === 0;
}

Code Testing

// Testing isEmpty method
console.log(linkedList.isEmpty()); //--> false

Implement size() method

size() Gets the length of the chain table.

code implementation

size() {
    return this.length;
}

Code Testing

// Test the size method
console.log(linkedList.size()); //--> 3

Full implementation

class LinkedList {
  // Initial list length is 0
  length = 0;

  // The initial header is null, and the header points to the first node in the chain table
  head = null;

  // Internal Class (Node in Chain List)
  Node = class {
    data;
    next = null;

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

  // ----------------------------------//

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

    // 2. Append new nodes
    if (this.length === 0) {
      // When the list length is 0, that is, when there is only the head
      this.head = newNode;
    } else {
      // Add a new node at the end when the chain length is greater than 0
      let currentNode = this.head;

      // When currentNode.next is not empty,
      // Find the last node in sequence, when the next 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. Chain list length + 1 after appending new nodes
    this.length++;
  }

  // insert() inserts a node at a specified location
  insert(position, data) {
    // position New Inserted Node Location
    // position = 0 means the first node after the new insert
    // position = 1 indicates the second node after the new insertion, and so on

    // 1. position cannot be less than 0 or greater than the length of the 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, head
      newNode.next = this.head;

      // head assigns newNode
      this.head = newNode;
    } else {
      // 0 < position <= length case

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

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

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

    // Update Link List Length
    this.length++;
    return newNode;
  }

  // getData() Gets the data at the specified location
  getData(position) {
    // 1. position Judgment Over Bounds
    if (position < 0 || position >= this.length) return null;

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

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

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

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

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

    return -1;
  }

  // update() Modify the data of the specified location node
  update(position, data) {
    // Cross-border judgment is required when position ing is involved
    // 1. position Judgment Over Bounds
    if (position < 0 || position >= this.length) return false;

    // 2. Traverse through the loop to find the node that specified the 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 a node at a specified location
  removeAt(position) {
    // 1. position Judgment Over Bounds
    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
      // Loop through to find the node of the specified position and assign it to currentNode

      let previousNode = null;
      let index = 0;

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

      // Clever enough, having the next of the previous node point to the next of the current node is equivalent to deleting the current node.
      previousNode.next = currentNode.next;
    }

    // 3. Update Chain List Length-1
    this.length--;

    return currentNode;
  }

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

  // isEmpty() determines if the list of chains is empty
  isEmpty() {
    return this.length === 0;
  }

  // size() Gets the length of the chain table
  size() {
    return this.length;
  }

  // toString() chain table data returned as a string
  toString() {
    let currentNode = this.head;
    let result = "";

    // Traverse all nodes and stitch them into strings until they are null
    while (currentNode) {
      result += currentNode.data + " ";
      currentNode = currentNode.next;
    }

    return result;
  }
}

Tags: Algorithm data structure linked list

Posted on Sun, 12 Sep 2021 13:41:11 -0400 by Nicholas Reed