Two way linked list: I’m no longer driving in one direction

Time:2021-8-12

Unlike a single linked list, each node of a two-way linked list contains not only data, but also pointers to its front node and rear node respectively

Two way linked list: I'm no longer driving in one direction

In fact, compared with a single linked list, a double linked list adds a pointer to the previous node

Let me write a double linked list to implement some methods:

<img style=”zoom:80%;” />

We first implement a node constructor:

class Node {
    constructor(element) {
        this.element = element;  //  Node data value
        this.next = null;  //  Front pointer
        this.prev = null;  //  Rear pointer
    }
}

Overall structure of bidirectional linked list

class DoubleLinkedList {
    constructor() {
        this.count = 0; //  Number of record nodes
        this.head = null; //  Bidirectional linked list header
        this.tail = null; //  Bidirectional linked list tail
    }

    //Add node at the end of linked list
    add(element) {}

    //Add a node at the specified location in the linked list
    addAt(index, element) {}

    //Remove the node at the specified position in the linked list
    removeAt(index) {}

    //Remove the specified node of the linked list
    remove(element) {}

    //Flip linked list
    reverse() {}

    //Swap the location of two nodes
    swap() {}

    //Traversal linked list
    traverse() {}

    //Find the index of a node
    find() {}

    //Query whether the linked list is empty
    isEmpty() {}

    //Length of query linked list
    length() {}
}

The next step is to implement this one by one

Find the node according to the location (index). The next method will always use this method. Simply encapsulate it into a method for calling

getElement(index) {
    if (index >= 0 && index < this.count) {
        let node = this.head;
        //Loop through to the node
        for (let i = 0; i < index; i++) {
            node = node.next;
        }
        return node;
    }
    return undefined;
}

1. Add node

<img style=”zoom:80%;” />

1.1 add nodes at the tail

//Pass in a node
add(element) {
    //Create a node first
    const node = new Node(element);
    //If the linked list is empty, both the head node and the tail node are nodes
    if (this.head === null && this.tail === null) {
        this.head = node;
        this.tail = node
    }
    //If the linked list has a header, add a node at its tail
    else {
        node.prev = this.tail;
        this.tail.next = node;
        this.tail = node;
    }
    //Total number of nodes + 1
    this.count++;
}

1.2 add a node at the specified position in the linked list

addAt(index, element) {
    if (index >= 0 && index <= this.count) {
        const node = new Node(element);
        //When inserting in the head
        if (index === 0) {
            this.head.prev = node;
            node.next = this.head;
            this.head = node;
        } 
        //Insert node at tail
        else if (index === this.count) {
            this.add(element)
            this.count--;
        }
        //Insert node in the middle
        else {
            let current = this.getElement(index);
            //First, set the relationship between the new node and the previous node
            node.prev = current.prev;
            current.prev.next = node;
            //Then set the relationship between the new node and the next node
            node.next = current;
            current.prev = node
        }
        this.count++;
        return true
    }
    return false;
}

2. Remove node

<img style=”zoom:80%;” />

2.1. Remove the node at the specified position of the linked list

removeAt(index) {
    if (index >= 0 && index < this.count) {
        //Remove header node
        if (index === 0) {
            this.head = this.head.next;
            this.head.prev = null;
        }
        //Remove tail node
        else if (index === this.count - 1) {
            this.tail = this.tail.prev;
            this.tail.next = null;
        }
        //Remove intermediate node
        else {
            let current = this.getElement(index);
            current.next.prev = current.prev
            current.prev.next = current.next
        }
        this.count--;
        return true
    }
    return undefined;
}

2.2. Remove the specified node of the linked list

To remove a specified node, you must first find it

remove(element) {
    let current = this.head;
    while (current) {
        if (current === element) {
            //The linked list has only one node
            if (current === this.head && current === this.prev) {
                this.head = null;
                this.tail = null;
            }
            //Remove header node
            else if (current === this.head) {
                this.head = this.head.next;
                this.head.prev = null;
            }
            //Remove tail node
            else if (current === this.tail) {
                this.tail = this.tail.prev;
                this.tail.next = null;
            }
            //Remove intermediate node
            else {
                current.next.prev = current.prev;
                current.prev.next = current.next;
            }
            this.count--;
        }
        current = current.next;
    }
}

5. Flip linked list

reverse() {
    let current = this.head;
    let prev = null;
    //Put the middle first
    while (current) {
        let next = current.next;

        //Back and forth inversion
        current.next = prev;
        current.prev = next;

        prev = current;
        current = next
    }
    this.tail = this.head;
    this.head = prev
}

6. Swap the location of two nodes

swap(index1, index2) {
    if (index1 >= 0 && index1 < this.count && index2 >= 0 && index2 < this.count) {
        let node1 = this.getElement(index1)
        let node2 = this.getElement(index2)
        //Let index1 always be less than index2 to facilitate subsequent search and exchange
        if (index1 > index2) {
            return this.swap(index2, index1)
        }
        //Swap the values of two nodes
        [node1.element, node2.element] = [node2.element, node1.element]
        return true
    }
    return undefined;
}

7. Traversal linked list

display() {
    if (!this.isEmpty()) {
        let current = this.head;
        let result = '';
        while (current) {
            result += current.element
            current = current.next
            if (current) {
                result += '->';
            }
        }
        return result;
    }
    return undefined;
}

8. Find the index of a node

find(element) {
    let current = this.head;
    let index = 0
    while (current) {
        if (current === element) {
            return index;
        }
        current = current.next;
        index++;
    }
    return undefined
}

9. Query whether the linked list is empty

isEmpty() {
    return this.count === 0
}

10. Length of query linked list

length() {
    return this.count;
}

Consolidate:

//Implement a node constructor
class Node {
    constructor(element) {
        this.element = element;
        this.next = null;
        this.prev = null;
    }
}

//Bidirectional linked list
class DoubleLinkedList {
    constructor() {
        this.count = 0; //  Number of record nodes
        this.head = null; //Bidirectional linked list头部
        this.tail = null; //Bidirectional linked list尾部
    }

    //Traverse a method and loop to the target location
    getElement(index) {
        if (index >= 0 && index < this.count) {
            let node = this.head;
            for (let i = 0; i < index; i++) {
                node = node.next;
            }
            return node;
        }
        return undefined;
    }

    //Add node at the end of linked list
    add(element) {
        //Create a node first
        const node = new Node(element);
        //If the linked list is empty
        if (this.head === null && this.tail === null) {
            this.head = node;
            this.tail = node
        }
        //If the linked list has a header, add a node at its tail
        else {
            node.prev = this.tail;
            this.tail.next = node;
            this.tail = node;
        }
        this.count++;
    }

    //Add a node at the specified location in the linked list
    addAt(index, element) {
        if (index >= 0 && index <= this.count) {
            const node = new Node(element);
            //When inserting in the head
            if (index === 0) {
                this.head.prev = node;
                node.next = this.head;
                this.head = node;
            } else if (index === this.count) {
                this.add(element)
                this.count--;
            }
            //Non head insertion
            else {
                let current = this.getElement(index);
                //First, set the relationship between the new node and the previous node
                node.prev = current.prev;
                current.prev.next = node;
                //Then set the relationship between the new node and the next node
                node.next = current;
                current.prev = node
            }
            this.count++;
            return true
        }
        return false;
    }

    //Remove the node at the specified position in the linked list
    removeAt(index) {
        if (index >= 0 && index < this.count) {
            //Remove header node
            if (index === 0) {
                this.head = this.head.next;
                this.head.prev = null;
            }
            //Remove tail node
            else if (index === this.count - 1) {
                this.tail = this.tail.prev;
                this.tail.next = null;
            }
            //Remove intermediate node
            else {
                let current = this.getElement(index);
                current.next.prev = current.prev
                current.prev.next = current.next
            }
            this.count--;
            return true
        }
        return undefined;
    }

    //Remove the specified node of the linked list
    remove(element) {
        let current = this.head;
        while (current) {
            if (current === element) {
                //The linked list has only one node
                if (current === this.head && current === this.prev) {
                    this.head = null;
                    this.tail = null;
                }
                //Remove header node
                else if (current === this.head) {
                    this.head = this.head.next;
                    this.head.prev = null;
                }
                //Remove tail node
                else if (current === this.tail) {
                    this.tail = this.tail.prev;
                    this.tail.next = null;
                }
                //Remove intermediate node
                else {
                    current.next.prev = current.prev;
                    current.prev.next = current.next;
                }
                this.count--;
            }
            current = current.next;
        }
    }

    //Flip linked list
    reverse() {
        let current = this.head;
        let prev = null;

        while (current) {
            let next = current.next;

            //Back and forth inversion
            current.next = prev;
            current.prev = next;

            prev = current;
            current = next
        }
        this.tail = this.head;
        this.head = prev
    }

    //Swap the location of two nodes
    swap(index1, index2) {
        if (index1 >= 0 && index1 < this.count && index2 >= 0 && index2 < this.count) {
            let node1 = this.getElement(index1)
            let node2 = this.getElement(index2)
                //Let index1 always be less than index2 to facilitate subsequent search and exchange
            if (index1 > index2) {
                return this.swap(index2, index1)
            }
            //Swap the values of two nodes
            [node1.element, node2.element] = [node2.element, node1.element]
            return true
        }
        return undefined;
    }

    //Traversal linked list
    display() {
        if (!this.isEmpty()) {
            let current = this.head;
            let result = '';
            while (current) {
                result += current.element
                current = current.next
                if (current) {
                    result += '->';
                }
            }
            return result;
        }
        return undefined;
    }

    //Find the index of a node
    find(element) {
        let current = this.head;
        let index = 0
        while (current) {
            if (current === element) {
                return index;
            }
            current = current.next;
            index++;
        }
        return undefined
    }

    //Query whether the linked list is empty
    isEmpty() {
        return this.count === 0
    }

    //Length of query linked list
    length() {
        return this.count;
    }
}

Let’s call:

//First instantiate a two-way linked list object
let DLL = new DoubleLinkedList();

//Tail addition
DLL.add(1);
DLL.add(2);
//Add anywhere
DLL.addAt(0, 3);
DLL.addAt(3, 6);
//Traversal
console.log(DLL.display()); // 3->1->2->6
//Delete by location
DLL.removeAt(3);

//Swap two nodes
DLL.swap(0, 2)
console.log(DLL.display()); // 2->1->3
//Flip
DLL.reverse();
//Is the linked list empty
console.log(DLL.isEmpty()); // false
//Linked list length
console.log(DLL.length()); // 3

Summary:

  • So you will find that implementing a linked list is nothing more than adding (adding nodes), deleting (deleting nodes), changing (exchanging) and searching (traversing)
  • Adding and deleting nodes is nothing more than modifying the pointer of nodes