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