Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”.
This article has participated in the “Digitalstar Project” and won a creative gift package to challenge the creative incentive money.
Last time we used JavaScript to design a single linked list, this time we’re going to design a double linked list, why do we design a double linked list? Since I met The “Quick Hand second Interview” in an interview, I thought I’d post it on my blog as well so that I could have another tidbit of knowledge. In general, it’s one more prev pointer than a single linked list
707. Design linked lists
Design the implementation of linked lists. You can choose to use single or double linked lists. Nodes in a singly linked list should have two attributes: val and next. Val is the value of the current node and next is a pointer/reference to the next node. If you are using a bidirectional linked list, you also need an attribute prev to indicate the last node in the list. Assume that all nodes in the list are 0-index.
Implement these functions in the linked list class:
get(index)
: Gets the value of the index node in the list. If the index is invalid, -1 is returned.addAtHead(val)
: Adds a node with the value val before the first element of the list. After insertion, the new node becomes the first node in the linked list.addAtTail(val)
: appends a node with the value val to the last element of the list.addAtIndex(index,val)
: Adds a node with the value val before the index node in the list. If index is equal to the length of the list, the node is appended to the end of the list. If index is greater than the list length, no node will be inserted. If index is less than 0, insert the node in the header.deleteAtIndex(index)
: If index is valid, the index node in the linked list is deleted.
Source: LeetCode link: leetcode-cn.com/problems/de…
1. Define node constructors in a double-linked list
There is an extra pointer prev to the previous node
/** * Node constructor * in the list@param {*} val* /
function ListNode(val) {
this.val = val;
this.next = null;
this.prev = null;
};
Copy the code
2. Initialize data structure MyLinkedList
Different from the idea of designing a single linked list last time, this time we add a pseudo-header and a pseudo-tail to the list, so that we can easily operate. (The previous single-linked list design could do the same thing.)
/** * Initialize your data structure here
var MyLinkedList = function () {
// Initializes the list length
this.length = 0;
// Define a virtual header and a virtual dummy to facilitate subsequent operations
this.head = new ListNode(0);
this.tail = new ListNode(0);
// Establish the initial connection
this.head.next = this.tail;
this.tail.prev = this.head;
};
Copy the code
3. get
Since we have designed a pseudo-header and a pseudo-tail, when we get an element, we can first make a judgment to see whether the index is near the head or the tail, which can improve the efficiency of searching
/** * Get the value of the index-th node in the linked list. If the index is invalid, return -1. * Get the value of the index node in the linked list. If the index is invalid, -1 * is returned@param {number} index
* @return {number}* /
MyLinkedList.prototype.get = function (index) {
if (index < 0 || index >= this.length) {
return -1;
}
let current = this.head;
// If you are close to the head node, start traversing the head node
if(index + 1 < this.length - index){
for (let i = 0; i < index + 1; i++) { current = current.next; }}else{
// If it is close to the tail, start traversal from the tail
current = this.tail;
for(let i = 0; i < this.length - index; i++) { current = current.prev; }}return current ? current.val : -1;
};
Copy the code
With pseudo headers and pseudo tails, we can unify our three methods of inserting data
- Find the precursor node to insert the node into
prevNode
And successor nodesnextNode
. If a node is inserted in a header, its precursor is a dummy header. If a node is inserted at the tail, its successor is a pseudo-tail. - Add elements by changing the link relationship between the precursor node and its successor.
A uniform procedure for adding an element to a double-linked list
addNode.prev = prevNode;
addNode.next = nextNode;
prevNode.next = addNode;
nextNode.prev = addNode;
Copy the code
4. addAtHead
/** * Add a node of value val before the first element of the linked list. After the insertion, The new node will be the first node of the linked list. * Add a node with the value val before the first element of the linked list. After insertion, the new node will become the first node in the linked list *@param {number} val
* @return {void}* /
MyLinkedList.prototype.addAtHead = function (val) {
// Create a new node and pass in the value val
let addNode = new ListNode(val);
// Find the precursor and the successor
let prevNode = this.head;
let nextNode = this.head.next;
// Add a node
addNode.prev = prevNode;
addNode.next = nextNode;
prevNode.next = addNode;
nextNode.prev = addNode;
// The list length increases
this.length++;
};
Copy the code
5. addAtTail
/** * Append a node of value val to the last element of the linked list@param {number} val
* @return {void}* /
MyLinkedList.prototype.addAtTail = function (val) {
// Create a new tail
let addNode = new ListNode(val);
// Find the precursor and the successor
let prevNode = this.tail.prev;
let nextNode = this.tail;
// Add a node
addNode.prev = prevNode;
addNode.next = nextNode;
prevNode.next = addNode;
nextNode.prev = addNode;
this.length++;
};
Copy the code
6. addAtIndex
/** * Add a node of value val before the index-th node in the linked list. If index equals to the length of linked list, the node will be appended to the end of linked list. If index is greater than the length, The node will not be inserted. * Add a node with the value val before the index node in the list. * If index is equal to the length of the list, the node is appended to the end of the list. * Nodes will not be inserted if index is greater than the list length. * If index is less than 0, insert the node in the header. *@param {number} index
* @param {number} val
* @return {void}* /
MyLinkedList.prototype.addAtIndex = function (index, val) {
if (index <= 0) return this.addAtHead(val);
if (index === this.length) return this.addAtTail(val);
if (index > this.length) return;
let addNode = new ListNode(val);
// Find the precursor and the successor
let prevNode = null;
let nextNode = null;
if(index < this.length - index){
prevNode = this.head;
for(let i = 0; i < index; i++){
prevNode = prevNode.next;
}
nextNode = prevNode.next;
}else{
nextNode = this.tail;
for(let i = 0; i < this.length - index; i++){
nextNode = nextNode.prev
}
prevNode = nextNode.prev
}
// Add a node
addNode.prev = prevNode;
addNode.next = nextNode;
prevNode.next = addNode;
nextNode.prev = addNode;
this.length++;
};
Copy the code
7. deleteAtIndex
- Find the precursor of the node to delete
prevNode
And successor nodesnextNode
. - Remove elements by changing the link relationship between the precursor node and its successor.
Key steps
prevNode.next = nextNode;
nextNode.prev = prevNode;
Copy the code
/**
* Delete the index-th node in the linked list, if the index is valid.
* @param {number} index
* @return {void}* /
MyLinkedList.prototype.deleteAtIndex = function (index) {
if (index < 0 || index >= this.length) return;
// Find the precursor and the successor
let prevNode = null;
let nextNode = null;
if(index < this.length - index) {
prevNode = this.head;
for(let i = 0; i < index; i++){
prevNode = prevNode.next;
}
nextNode = prevNode.next.next;
}else{
nextNode = this.tail;
for(let i = 0; i < this.length - index - 1; i++){
nextNode = nextNode.prev;
}
prevNode = nextNode.prev.prev;
}
// Delete operation
prevNode.next = nextNode;
nextNode.prev = prevNode;
this.length--;
};
Copy the code
The results
Class version
class ListNode {
constructor(val) {
this.val = val;
this.next = null;
this.prev = null; }}class MyLinkedList {
constructor() {
this.length = 0;
this.head = new ListNode(0);
this.tail = new ListNode(0);
this.head.next = this.tail;
this.tail.prev = this.head;
}
get(index) {
if (index < 0 || index >= this.length) {
return -1;
}
let current = this.head;
// If you are close to the head node, start traversing the head node
if (index + 1 < this.length - index) {
for (let i = 0; i < index + 1; i++) { current = current.next; }}else {
// If it is close to the tail, start traversal from the tail
current = this.tail;
for (let i = 0; i < this.length - index; i++) { current = current.prev; }}return current ? current.val : -1;
}
addAtHead(val) {
// Create a new node and pass in the value val
let addNode = new ListNode(val);
// Find the precursor and the successor
let prevNode = this.head;
let nextNode = this.head.next;
// Add a node
addNode.prev = prevNode;
addNode.next = nextNode;
prevNode.next = addNode;
nextNode.prev = addNode;
// The list length increases
this.length++;
}
addAtTail(val) {
// Create a new tail
let addNode = new ListNode(val);
// Find the precursor and the successor
let prevNode = this.tail.prev;
let nextNode = this.tail;
// Add a node
addNode.prev = prevNode;
addNode.next = nextNode;
prevNode.next = addNode;
nextNode.prev = addNode;
this.length++;
}
addAtIndex(index, val) {
if (index <= 0) return this.addAtHead(val);
if (index === this.length) return this.addAtTail(val);
if (index > this.length) return;
let addNode = new ListNode(val);
// Find the precursor and the successor
let prevNode = null;
let nextNode = null;
if (index < this.length - index) {
prevNode = this.head;
for (let i = 0; i < index; i++) {
prevNode = prevNode.next;
}
nextNode = prevNode.next;
} else {
nextNode = this.tail;
for (let i = 0; i < this.length - index; i++) {
nextNode = nextNode.prev;
}
prevNode = nextNode.prev;
}
// Add a node
addNode.prev = prevNode;
addNode.next = nextNode;
prevNode.next = addNode;
nextNode.prev = addNode;
this.length++;
}
deleteAtIndex(index) {
if (index < 0 || index >= this.length) return;
// Find the precursor and the successor
let prevNode = null;
let nextNode = null;
if (index < this.length - index) {
prevNode = this.head;
for (let i = 0; i < index; i++) {
prevNode = prevNode.next;
}
nextNode = prevNode.next.next;
} else {
nextNode = this.tail;
for (let i = 0; i < this.length - index - 1; i++) {
nextNode = nextNode.prev;
}
prevNode = nextNode.prev.prev;
}
// Delete operation
prevNode.next = nextNode;
nextNode.prev = prevNode;
this.length--; }}Copy the code