[JAVA data structure] realized by JAVA language - the basic function of headless two-way linked list (with super detailed code comments)

Posted by Cronje on Sun, 21 Nov 2021 22:53:23 +0100


[preface] in the previous article, we have learned about the basic functions of sequential list and single linked list. Next, let's learn how to realize the basic functions of two-way linked list. First, let's take a look at the structure of the two-way linked list:

Headless bidirectional linked list

Concept and structure of bidirectional linked list

Concept: bidirectional linked list, also known as double linked list, is a kind of linked list. There are two pointers in each data node, pointing to the direct successor and direct precursor respectively. Therefore, starting from any node in the two-way linked list, you can easily access its predecessor node and successor node.
Structure:

Implementation of linked list

Before we implement the interface function of the linked list, we need to create two classes, the linked list class (including a variable head node and variable tail node and interfaces to realize various functions) and the node class (including the member attribute value, prev precursor and next successor).

class ListNode{
    public int value;//Data domain
    public ListNode prev;//precursor
    public ListNode next;//Successor
    public ListNode(int value){
        this.value= value;
    }
}

public class LinkList {
    public ListNode head;//Defines the header reference of the linked list
    public ListNode last;//Defines the tail node of the linked list
   }

Print double linked list

Its idea is similar to the idea of printing sequence table. You can traverse the linked list, but it should be noted that you need to create a local variable cur to traverse instead of the head node, because the head node is fixed before adding or deleting nodes, and you can't let it change.

public void disply(){
        ListNode cur=this.head;//Reference the variable cur instead of the header node
        while(cur!=null){//When the node is not empty, traverse the single linked list. If the node is empty, the traversal ends
            System.out.print(cur.value+" ");
            cur=cur.next;//Point to next node
        }
        System.out.println();
    }

Gets the length of the double linked list

Reference the local variable cur to traverse the linked list, and reference a local variable count to count. As long as the node is not null, the count will be + 1, and the last returned count value is the length of the linked list. (consistent with the single linked list method)

    public int size(){
        ListNode cur=this.head;
        int count=0 ;//The local variable count is introduced to count
        while(cur!=null){
            count++;//Counter + 1 when cur is not empty
            cur=cur.next;//cur goes back and points to the next node
        }
        return count;//Return double linked list length
    }

Find out whether the keyword is included and whether the key is in the double linked list

Pass in the keyword key and introduce the local variable cur to traverse the linked list. If the same element as the key is encountered during the traversal, it returns true, otherwise it returns false. (consistent with the single linked list method)

    public boolean contains(int key){
        ListNode cur=this.head;//The local variable cur is introduced to traverse the linked list instead of the head node
        while(cur!=null){
            if(cur.value==key)//When equal, it indicates that the linked list contains the keyword key and returns true
                return true;
            cur=cur.next;//Traverse the next node
        }
        //When the variable cur is empty, the traversal ends and the loop jumps out. The linked list does not contain this keyword and returns false
        return false;
    }

Head insertion

The so-called head insertion method is to insert a node in front of the head node. First, we need to judge whether the head node is empty. If it is empty, the new node created will be the head node and tail node directly; If it is not empty, the successor of the newly inserted node points to the original head node, the precursor of the original head node points to the newly inserted node, and finally set the newly inserted node as the head node.

public void addFirst(int data){
        ListNode node=new ListNode(data);//Create a new node and store the data to be inserted
        if(this.head==null){//Judge whether the header node is empty. If the header node is empty, it indicates that it is the first insertion
            this.head=node;//Directly point the head node and tail node to the insertion node
            this.last=node;
        }
        else{//If not for the first time
            node.next=this.head;//The successor of the newly inserted node points to the head node
            this.head.prev=node;//Point the precursor of the original head node to the newly inserted node
            this.head=node;//Finally, set the newly inserted node as the head node
        }
    }

Tail interpolation

The tail insertion method is similar to the head insertion method. You must first judge whether the head node is empty. If it is empty, it indicates that it is the first insertion. You can directly point the head node and tail node to the newly inserted node. If it is not empty, find the last node of the double linked list, and then insert the new node.

public void addLast(int data){
        ListNode node=new ListNode(data);//Create a new node and store the data to be inserted
        if(this.head==null){//Judge whether the header node is empty. If the header node is empty, it indicates that it is the first insertion
            this.head=node;//Directly point the head node and tail node to the insertion node
            this.last=node;
        }
        else{//If not for the first time
            this.last.next=node;//Point the successor of the original tail node to the newly inserted node
            node.prev=this.last;//The precursor of the new node is the original tail node
            this.last=node;//Finally, the newly inserted node becomes the tail node
        }
    }

Insert at any position, and the first data node is subscript 0

First, we need to judge whether the insertion position is reasonable. Then, if we want to insert a node at any position, we must first find the insertion position, and then insert the newly created node between the current node and the previous node. Because it is a double linked list, we should pay attention to the changes of the precursor and subsequent nodes.

//Find insertion location
    public ListNode searchIndex(int index){
        ListNode cur=this.head;//The local variable count is introduced to count
        while(index!=0){//Traverse the linked list to find the location to insert
            cur=cur.next;//cur goes back and points to the next node
            index--;//Subscript-1
        }
        return cur;
    }
    //Insert at any position, and the first data node is subscript 0
    public void addIndex(int index,int data){
        ListNode node=new ListNode(data);//Create a new node and store the data to be inserted
        //Determine whether the insertion position is legal
        if(index<0||index>size()){
            System.out.println("Illegal insertion position");
            return;
        }
        //If the insertion position is 0, it is header insertion. You can call header insertion directly
        if(index==0){
            addFirst(data);
            return ;
        }
        //If the insertion position is the position of the length of the linked list, it is tail insertion. You can directly call the tail insertion method
        if(index==size()){
            addLast(data);
            return;
        }
        ListNode cur=searchIndex(index);//Reference the variable cur to receive the insertion location returned by the searchIndex method
        node.next=cur.prev.next;//Make the successor of the newly inserted node point to the successor node of the predecessor of the insertion position
        cur.prev.next=node;//The successor node of the predecessor node at the insertion position is the newly inserted node
        node.prev=cur.prev;//The precursor node of the newly inserted node becomes the precursor node at the insertion position
        cur.prev=node;//The precursor node at the inserted position becomes the newly inserted node
    }

Delete the node whose keyword is key for the first time

First, we need to judge whether the linked list is empty (that is, whether the head node is empty). Second, we need to see whether the key node to be deleted is the head node. If it is the head node, we will directly point the reference of the head node to the next node, and the next node will become the head node; If the keyword is in the tail node, set the previous node of the tail node as the new tail node; If the keyword is an intermediate node, the successor of the previous node of the key node points to the next node of the key node, and the predecessor of the next node of the key node points to the previous node of the key node.

public void remove(int key){
        ListNode cur=this.head;//Reference the variable cur instead of the header node
        while(cur!=null){//Traverse the linked list
            if(cur.value==key){//If the value of the node is equal to the keyword key, enter the loop
                if(cur==this.head){//If the header node is the keyword key
                    this.head=this.head.next;//The head node points directly to the next node
                    if(this.head!=null) {//If there is only one element in the linked list, check it
                        this.head.prev = null;//Otherwise, null pointer exception will occur
                    }
                    else{
                        this.last=null;
                    }
                }
                else{//If the keyword is at the end or in the middle
                    cur.prev.next=cur.next;//Make the successor of the previous node of the current node point to the next node of the current node
                    if(cur==this.last){//Keyword key at the end
                        last=last.prev;//The previous node of the tail node is the last node
                    }else{
                        //Intermediate node
                        cur.next.prev=cur.prev;//The predecessor of the next node after the current node points to the previous node of the current node
                    }
                    /*
                    if(this.last.next!=null){
                    cur.next.prev=cur.prev;
                    }
                    last=last.prev;
                     */
                }
                return ;//Interrupt when keyword key is found
            }
           cur=cur.next;//If not found, cur continues to point to the next node
        }
    }

Delete all nodes with the value of key

It is similar to the above method of deleting a node with the keyword key for the first time, except that the above method does not go down after finding a node with the keyword key. This method of deleting all nodes with the value key is to continue to go down after finding the first key until all the nodes are found.

public void remove(int key){
        ListNode cur=this.head;//Reference the variable cur instead of the header node
        while(cur!=null){//Traverse the linked list
            if(cur.value==key){//If the value of the node is equal to the keyword key, enter the loop
                if(cur==this.head){//If the header node is the keyword key
                    this.head=this.head.next;//The head node points directly to the next node
                    if(this.head!=null) {//If there is only one element in the linked list, check it
                        this.head.prev = null;//Otherwise, null pointer exception will occur
                    }
                    else{
                        this.last=null;
                    }
                }
                else{//If the keyword is at the end or in the middle
                    cur.prev.next=cur.next;//Make the successor of the previous node of the current node point to the next node of the current node
                    if(cur==this.last){//Keyword key at the end
                        last=last.prev;//The previous node of the tail node is the last node
                    }else{
                        //Intermediate node
                        cur.next.prev=cur.prev;//The predecessor of the next node after the current node points to the previous node of the current node
                    }
                    /*
                    if(this.last.next!=null){
                    cur.next.prev=cur.prev;
                    }
                    last=last.prev;
                     */
                }
            }
           cur=cur.next;//If not found, cur continues to point to the next node
        }
    }

Empty linked list

It is similar to the method of empty single linked list, except that there are more precursors of empty nodes, and the tail node should also be empty.

  public void clear(){
        //When the header node is not empty
        while(this.head!=null){
            ListNode curNext=this.head.next;//Reference the variable curNext to save the next node of the header node
            this.head.next=null;//Set the next in the header node to null
            this.head.prev=null;//Set the prev in the header node to null
            this.head=curNext;//Set the next node as the head node
        }
        this.last=null;//The tail node should also be empty
    }

Topics: Java data structure Back-end