Bidirectional linked list implementation

1.1 basic introduction

1. Advantages and disadvantages of one-way linked list

  • For one-way linked lists, the search direction can only be one direction, while two-way linked lists can search forward or backward.
  • A one-way linked list cannot be deleted by itself. It needs to rely on auxiliary nodes, while a two-way linked list can be deleted by itself.

2. Basic introduction to bidirectional linked list

Bidirectional linked list, also known as bidirectional list, is a kind of linked list. It is composed of multiple nodes. Each node is composed of a data field and two pointer fields. The data field (data) is used to store data, one pointer field (next) is used to point to its successor node, and the other pointer field is used to point to the predecessor node (prev pointer). The data field of the head node of the linked list does not store data, the pointer field value pointing to the predecessor node is null, and the pointer field pointing to the successor node points to the first node that actually stores data.

1.2 add operation

1. Train of thought analysis

Head insertion

  • New insert node newNode.

  • The first precursor points to the newNode.

  • The rear drive of newNode points to first.

  • The first point to the newNode. At this time, the head only represents the second node, and the head needs to represent the first node, so the direction is changed.

Tail insertion

  • New insert node newNode.
  • The newNode precursor points to last.
  • The last rear drive points to the newNode.
  • Last points to newNode. At this time, last only represents the penultimate node, and last needs to represent the last node, so it points to newNode.

Middle insertion

New insert node newNode

Locate the previous node to insert the newNode. And the next node nextNode

The newNode trailing driver points to the nextnode, and the nextnode leading driver points to the newNode. At this time, the newNode and the following are associated with the linked list, but they are separated from the previous processing.

The preNode driver points to the newNode and the newNode precursor points to the preNode. At this time, the complete insertion operation is completed.

2. Code example

Linked list class: DoubleLinkedList

package cn.linkedlist.demo03;

public class DoubleLinkedList<E> {
    // Number of linked list elements
    private int size;
    // Declaration header node
    private Node first;
    // Declare tail node
    private Node last;

    // Create Node
    private class Node{
        // Storage content
        public E data;
        // Point to the previous node of the linked list
        public Node prev;
        // Point to next node
        public Node next;
        // Construction method
        
        public Node() {
            
        }
        
        public Node(Node prev, E data, Node next) {
            this.prev = prev;
            this.data = data;
            this.next = next;
        }

        @Override
        public String toString(){
            return data.toString();
        }
    }

    // Initialize header node
    public DoubleLinkedList(){
        first = null;
        last = null;
        size = 0;
    }

    /***
     * Gets the number of elements in the linked list
     * @return
     */
    public int getSize(){
        return size;
    }

    /***
     * Returns whether the linked list is empty
     * @return
     */
    public boolean isEmpty(){
        return size == 0;
    }

    /***
     * Add a new element according to the index position of the linked list e
     * @param index
     * @param data
     */
    public void add(int index, E data){
        // Call method
        rangeCheckAdd(index);

        if (index == size) { // Add the last element
            addLast(data);
        } else if(index == 0){
            addLast(data);
        } else {
            // Add new node next element
            Node nextNode = node(index);
            // The previous element of the newly added node
            Node prevNode = nextNode.prev;
            // Add new node
            Node newNode = new Node (prevNode, data, nextNode);
            // The previous prev of the next node points to the new node
            nextNode.prev = newNode;
            // The next of the prevNode node points to the new node
            prevNode.next = newNode;
        }
        size++;
    }

    /***
     * Add a new element to the linked list header e
     * @param data
     */
    public void addFirst(E data){
        // Create a new node
       Node newNode = new Node(null, data, null);
       if(isEmpty()){
           last = newNode; // last -> newNode
       }else {
           first.prev = newNode; // first.prev->newNode
       }
       newNode.next = first; // newNode.next -> first;
       first = newNode;
       size++;
    }

    /***
     * Add a new element at the end of the linked list e
     * @param data
     */
    public void addLast(E data){
        // Create a new node
        Node newNode = new Node(null, data, null);
        if(isEmpty()){
            first = newNode; // first->newNode
        }else{
            last.next = newNode; // The node pointed to by last points to the new node
            newNode.prev = last; // The precursor of the new node points to the last pointer
        }
        last = newNode; // The last pointer points to the new node
        size++;
    }
	
    /**
     * Get the node object corresponding to the index location
     * @param index
     * @return
     */
    private Node node(int index) {
        rangeCheck(index);
        Node node;
        if (index < (size >> 1)) {
            node = first;
            for (int i = 0; i < index; i++) {
                node = node.next;
            }
        } else {
            node = last;
            for (int i = size - 1; i > index; i--) {
                node = node.prev;
            }
        }
        return node;
    }

    @Override
    public String toString(){
        StringBuilder res = new StringBuilder();
        res.append("size=").append(size).append(", [");
        // Define a pointer variable
        Node cur = first;
        while(cur != null){
            res.append(cur + "->");
            cur = cur.next;
        }
        res.append("NULL");
        res.append("]");
        return res.toString();
    }

    // Index value check range method
    private void rangeCheck(int index){
        if(index < 0 || index >=size){
            // Call out of bounds processing method
            outOfBounds(index);
        }
    }

    // Add method index check scope
    private void rangeCheckAdd(int index){
        if(index < 0 || index >size){
            // Call out of bounds processing method
            outOfBounds(index);
        }
    }
    // Array index out of bounds processing
    private void outOfBounds(int index){
        throw new IndexOutOfBoundsException("index:" + index + ", Size:" + size);
    }
}

Test class: DoubleLikedListTest

package cn.linkedlist.demo03;

public class DoubleLinkedListDemo01 {
    public static void main(String[] args) {
        DoubleLinkedList<Integer> list = new DoubleLinkedList<>();
        System.out.println("===Linked list header insertion===");
        for(int i=0; i<5; i++){
            list.addFirst(i);
            System.out.println(list);
        }

        System.out.println("===Tail insertion of linked list===");
        list.addLast(12);
        list.addLast(111);
        list.addLast(123);
        list.addLast(15);
        System.out.println(list);

        System.out.println("===Insert in the middle of linked list===");
        list.add(2, 23);
        list.add(7, 66);
        list.add(8, 39);

        System.out.println(list);
    }
}

3. Code example

1.3 modifying and querying

1. Query operation

Linked list class: DoubleLinkedList

/***
 * Get the element at the index position of the linked list
 * @param index
 * @return
*/
public E get(int index){
    return node(index).data;
}

/***
  * Get the first element of the linked list
  * @return
 */
public E getFirst(){
    return get(0);
}

/***
  * Get the last element of the linked list
  * @return
 */
public E getLast(){
    return get(size - 1);
}

/***
 * Find out if there is an element e in the linked list
 * @param data
 * @return
*/
public boolean contains(E data){
    Node cur = first.next;
    while(cur != null){
        if(cur.data.equals(data))
            return true;
        cur = cur.next;
    }
    return false;
}

Test class: DoubleLikedListTest

package cn.linkedlist.demo03;

public class DoubleLinkedListDemo01 {
    public static void main(String[] args) {
        DoubleLinkedList<Integer> list = new DoubleLinkedList<>();
        for(int i=0; i<5; i++){
            list.addFirst(i);
        }
        list.addLast(12);
        list.addLast(111);
        list.addLast(123);
        list.addLast(15);
       
        list.add(2, 23);
        list.add(7, 66);
        list.add(8, 39);
        
        System.out.println("===Find element===");
        Integer integer = list.get(2);
        System.out.println("Get elements by index:" + integer);
        Integer first = list.getFirst();
        System.out.println("First linked list element:" + first);
        Integer last = list.getLast();
        System.out.println("Last linked list element:" + last);
        boolean b = list.contains(23);
        System.out.println("Does the element exist:" + b);
    }
}

2. Execution results

3. Modify operation

Linked list class: DoubleLinkedList

/***
     * Modify the element at the index(0-based) position of the linked list to e
     * @param index
     * @param data
     */
public void update(int index, E data){
    // Call index detection method
    rangeCheck(index);
    // Create cur pointer to virtual header node
    Node cur = first;
    for(int i = 0 ; i < index ; i ++) {
        cur = cur.next;
    }
    cur.data = data;
}

Test class: DoubleLikedListTest

package cn.linkedlist.demo03;

public class DoubleLinkedListDemo01 {
    public static void main(String[] args) {
        DoubleLinkedList<Integer> list = new DoubleLinkedList<>();
        for(int i=0; i<5; i++){
            list.addFirst(i);
        }
        list.addLast(12);
        list.addLast(111);
        list.addLast(123);
        list.addLast(15);
       
        list.add(2, 23);
        list.add(7, 66);
        list.add(8, 39);

        System.out.println("===Modify node elements===");
        System.out.println("linkedList(Before modification)" + list);
        list.update(4, 38);
        System.out.println("linkedList(After modification)" + list);
    }
}

4. Execution results

1.4 delete

1. Train of thought analysis

Because it is a two-way linked list, you can delete a node by yourself and directly find the node to be deleted.

Head node deletion

The front pointer prev of the driver node of the first node is changed to null.

The first node points to first Next, so that first points to the first node required.

Tail node deletion

Tail deletion is to delete the last node in the two-way linked list, that is, the node pointed to by the tail pointer. The idea is the same as that of head deletion.

last.prev.next=null the trailing node of the previous node (prev) of the trailing node is equal to null.

last = last. The prev tail node points to its predecessor node. At this time, the tail node is deleted because step 1next is null.

Intermediate deletion

Find the precursor node of the node to be deleted, preNode, preNode Next is the node to delete

preNode.next.next.pre=preNode, point the prev pointer of the rear drive node nextNode of the node to be deleted to the prenode, which is equivalent to nextNode pre=preNode.

preNode.next=preNode.next.next;, At this time, the deleteNode is skipped and successfully deleted.

2. Code example

Linked list class: DoubleLinkedList

/***
 * Delete the element at the index position from the linked list and return the deleted element
 * @param index
*/
public void remove(int index){
    // Call index detection method
    rangeCheck(index);
    // Conditional judgment
    if(index == 0){
        removeFirst();
    }else if(index == size -1 ){
        removeLast();
    }else {

        //Deletes the previous element of the location
        Node preNode = first;
        for(int i=0; i < index-1; i++) {
            preNode = preNode.next;
        }
        //The element of the location to delete
        Node deleteNode = preNode.next;
        //The next element to delete
        Node nextNode = deleteNode.next;
        
        preNode.next = nextNode;
        nextNode.prev = preNode;

        size--;
    }
}

/***
 * Delete the first element from the linked list and return the deleted element
*/
public void removeFirst(){
    if (size == 1)
    {
        first = null;
        last = null;
    } else {
        first =first.next;
    }
    size--;
}

/***
 * Delete the last element from the linked list and return the deleted element
*/
public void removeLast(){
    if (size == 1)
    {
        first = null;
        last = null;
    } else {
        last.prev.next = null;
        last = last.prev;
    }
    size--;
}

/***
 * Delete element e from linked list
 * @param data
 */
public void removeElement(E data){
    // Create header node
    Node prev = first;
    while(prev.next != null){
        if(prev.next.data.equals(data))
            break;
        prev = prev.next;
    }

    if(prev.next != null){
        Node delNode = prev.next;
        prev.next = delNode.next;
        delNode.next = null;
        size --;
    }
}

Test class: DoubleLikedListTest

package cn.linkedlist.demo03;

public class DoubleLinkedListDemo01 {
    public static void main(String[] args) {
        DoubleLinkedList<Integer> list = new DoubleLinkedList<>();
        for(int i=0; i<5; i++){
            list.addFirst(i);
        }
        
        list.addLast(12);
        list.addLast(111);
        list.addLast(123);
        list.addLast(15);
        
        list.add(2, 23);
        list.add(7, 66);
        list.add(8, 39);

        System.out.println("===Before deleting a linked list node====");
        System.out.println(list);
        System.out.println("===After deleting a linked list node====");

        // Delete the first node of the linked list
        list.removeFirst();
        System.out.println(list);
        // Returns the deleted element according to the element in the index position of the linked list
        list.remove(2);
        System.out.println(list);

        // Delete the last node in the linked list
        list.removeLast();
        System.out.println(list);

        // Delete elements in linked list
        list.removeElement(39);
        System.out.println(list);
    }
}

3. Execution results

Keywords: Algorithm data structure linked list

Added by kpulatsu on Wed, 15 Dec 2021 09:43:21 +0200