Linear data structure linked list, linked list stack, linked list queue, bidirectional linked list, circular linked list and Joseph Ring problem solving

Keywords: Java data structure linked list

preface

This chapter describes in detail the use and related operations of linked list, a linear data structure.
In fact, many people can't understand the link between the nodes and the link before the nodes. It will be very difficult to learn, because I was like this at the beginning. I figured it out later. In fact, it is the following idea:
For example, "temp.next", which sometimes represents a node and sometimes a pointer, is easy to understand as long as the concept is distinguished and brought into the code for understanding.

Single linked list

As shown in the figure, the linked list is connected by one node after another, and each node has a data field and a pointer field.
The data field is the data of this node, and the pointer field points to the address space of the next node.
This one needs xdm a little bit of foundation. If you really can't understand it, just look at the code and find more things to learn. It's over and you'll get on the road slowly. I directly give the implementation method here. You can find relevant videos or books to learn, because I feel that I use words to link the list, which is certainly not as clear as others use videos, and the comments in my code are very complete, which can be referred to and should be well understood.
Code implementation:

package Linked list;

public class LinkedListTest {
    public static void main(String[] args) {
        //test
        //Create node
        Node n1 = new Node(5);
        Node n2 = new Node(4);
        Node n3 = new Node(10);
        Node n4 = new Node(6);
        //Create single linked list
        singleLinkedList list = new singleLinkedList();
        //Add node
        list.add(n1);
        list.add(n2);
        list.add(n3);
        list.add(n4);
        //Print single linked list
        list.show(); //Output 5 4 10 6
        //Delete function test: we delete 5
        list.delete(5); //Output 4 10 6
        list.show();
        //Insert function test, we insert 88 after 4,
        //The subscript here has the same meaning as the subscript value of the array
        //That is, the parameters passed in should be index=1 and newNode.data=88
        Node n5 = new Node(88);
        list.insert(1,n5);
        list.show(); //Output 4 88 10 6
        //Test the update function. We update 88 to 99
        list.update(88,99);
        list.show(); //Output 4 99 10 6
    }

}

//Define single linked list
class singleLinkedList{

    //Initialize a head node. The head node does not move because it is the head of the whole linked list and does not store data
    Node headNode = new Node(0);

    //Return header method
    public Node getHeadNode(){
        return headNode;
    }
//--------------------------------------------------------------------
    //Method to add a new node to a single linked list: add
    //Thinking, when the order is not considered
    //1. Find the last node of the current linked list
    //2. Mount the node to be added on the last node
    //That is, let the pointer field of the last node point to the new node
    public void add(Node newNode){
        //First, create a working pointer to traverse the head node instead of the head node, so as not to change the position of the head node
        //Because if the header pointer is used to traverse, the original address of the header pointer will change
        Node temp = headNode;
        while (true){
            if(temp.next == null) break; //When temp traverses a node and the next field is null, it indicates that the tail node is reached
            //If the tail node is not reached, the pointer moves back and continues to traverse the next node
            temp = temp.next;
        }
        //When exiting the loop, it is proved that we have reached the last node and hang up our new node
        temp.next = newNode;
    }
//--------------------------------------------------------------------
    //Method of inserting new nodes into single linked list: insert
    //Idea:
    //1. Use a counter cnt to calculate the number of positions traversed
    //2. Start traversing from the first node, and insert a new node newNode when cnt = = the location index we want to insert
    public void insert(int index,Node newNode){
        Node temp = headNode; //Working pointer
        int cnt = 0; //Counter

        while(true){
            if(temp.next == null) break; //After traversal, exit the loop
            if(cnt == index){ //Find the target location and insert it
                newNode.next = temp.next;
                temp.next = newNode;
                break;
            }
            temp = temp.next; //Pointer backward
            cnt++;
        }
    }
    //--------------------------------------------------------------------
    //Method for deleting a node in a single linked list: delete
    public void delete(int target){ //target: element to delete
        Node temp = headNode;
        while (true){
            //To prevent null pointer exceptions, the judgment that the value ends when it is null should be placed on the first line
            if(temp.next == null) break;
            if(temp.next.data == target){ //Find the previous node of the node to be deleted before we can delete the node
                temp.next = temp.next.next;
                break;
            }
            temp = temp.next; //Pointer backward
        }
    }
    //--------------------------------------------------------------------
    //Method for updating a node in a single linked list: update
    //srcData: the target element value in the linked list to be updated, and newData: the element value to replace
    public void update(int srcData,int newData){
        Node temp = headNode;
        while (true){
            if(temp.next == null) break; //After traversal, exit the loop
            if(temp.data == srcData){ //If the target element value is found
                temp.data = newData; //Replace data field with
                break;
            }
            temp = temp.next; //Pointer backward
        }
    }

    //--------------------------------------------------------------------
    //How to traverse the output linked list: show
    public void show(){
        System.out.print("The single linked list data is:");
        Node temp = headNode;
        while (true){
            if(temp.next == null) break;
            temp = temp.next;
            System.out.print(temp.data+" ");
        }
        System.out.println();
    }

}

//Define node
class Node{
    public int data; //Data domain
    //Pointer field, pointing to the position of the next Node, because the next Node is also of Node type
    //Therefore, the data type of this next is also Node
    public Node next;

    //The data field of the node is directly declared in the construction method
    public Node(int data){
        this.data = data;
    }

    //Print node information
    @Override
    public String toString() {
        return "Node{" +
                "data=" + data +
                '}';
    }
}

Linked list implementation stack data structure

The data structure of stack has been mentioned in the chapter of array. It will not be repeated here. It will be directly applied to the source code:
Code implementation:

package Linked list;

public class LinkedStack {
    public static void main(String[] args) {
        //test
        //Create node
        Node n1 = new Node(5);
        Node n2 = new Node(4);
        Node n3 = new Node(10);
        Node n4 = new Node(6);
        //Create stack
        linkedListStack stack = new linkedListStack();
        //Test stack function
        stack.push(n1);
        stack.show(); //Output 5
        stack.push(n2);
        stack.push(n3);
        stack.push(n4);
        stack.show(); //Output 5 4 10 6
        //Test out stack function
        stack.pop();
        stack.show(); //Output 5 4 10
        stack.pop();
        stack.pop();
        stack.show(); //Output 5
        //Reentry stack
        stack.push(n2);
        stack.push(n3);
        stack.show(); //Output 5 4 10
    }
}

//Define stack
class linkedListStack {

    //Define header node
    Node headNode = new Node(0);

    //Return header node
    public Node getHeadNode() {
        return headNode;
    }

    //--------------------------------------------------------
    //Stack method push
    public void push(Node newNode) {
        //Working pointer
        Node temp = headNode;

        while (true) {
            if (temp.next == null) break;
            temp = temp.next;
        }
        temp.next = newNode;
    }

    //--------------------------------------------------------
    //Stack out method pop
    public int pop() {
        Node temp = headNode;
        while (true) {
            if (temp.next.next == null) break;
            temp = temp.next;
        }
        //When exiting the loop, temp is located at the penultimate node of the linked list
        int element = temp.next.data; //Take out the value of the last node and return it later
        //The last node pops up, that is, delete the last node
        temp.next = null;
        return element;
    }

    //--------------------------------------------------------
    //Traversal stack
    public void show(){
        Node temp = headNode;
        System.out.print("Stack data is:");
        while (true){
            if(temp.next == null) break;
            temp = temp.next;
            System.out.print(temp.data + " ");
        }
        System.out.println();
    }
}

//Define node
class Node{
    public int data; //Data domain
    //Pointer field, pointing to the position of the next Node, because the next Node is also of Node type
    //So this
    public Node next;

    //Directly declare a node in the constructor
    public Node(int data){
        this.data = data;
    }

    //Print node information

    @Override
    public String toString() {
        return "Node{" +
                "data=" + data +
                '}';
    }
}

The linked list implements the queue data structure

The queue is also introduced in the chapter of array, and the source code is given directly here.
Code implementation:

package Linked list;

public class LinkedListQueue {
    public static void main(String[] args) {
        //test
        //Create node
        Node n1 = new Node(5);
        Node n2 = new Node(4);
        Node n3 = new Node(10);
        Node n4 = new Node(6);
        //Create queue
        LinkedQueue queue = new LinkedQueue();
        //Test in queue
        queue.put(n1);
        queue.show(); //Output 5
        queue.put(n2);
        queue.put(n3);
        queue.put(n4);
        queue.show(); //Output 5 4 10 6
        //Test out queue
        queue.out();
        queue.show(); //Output 4 10 6
        queue.out();
        queue.show(); //Output 10 6
    }
}

class LinkedQueue{
    //Define header node
    Node headNode = new Node(0);

    //Return header node
    public Node getHeadNode(){ return headNode; }

    //---------------------------------------------
    //Queue
    public void put(Node newNode){
        Node temp = headNode;
        while (true){
            if(temp.next == null) break;
            temp = temp.next;
        }
        temp.next = newNode;
    }

    //---------------------------------------------
    //Out of queue
    public int out(){
        //The queue is one in and one out, because we added it at the end of the queue
        //So we should go out of the queue at the head of the queue, so we can operate directly with the head node
        //No working pointer is required
        int element = headNode.next.data; //Take out the element of the team head and return later
        headNode = headNode.next;
        return element;
    }

    //---------------------------------------------
    //Traversal queue
    public void show(){
        Node temp = headNode;
        System.out.print("The queue data is:");
        while (true){
            if(temp.next == null) break;
            temp = temp.next;
            System.out.print(temp.data+" ");
        }
        System.out.println();
    }

}

//Define node
class Node{
    public int data; //Data domain
    //Pointer field, pointing to the position of the next Node, because the next Node is also of Node type
    //So this
    public Node next;

    //Directly declare a node in the constructor
    public Node(int data){
        this.data = data;
    }

    //Print node information

    @Override
    public String toString() {
        return "Node{" +
                "data=" + data +
                '}';
    }
}

Realize bidirectional linked list

Why is there a two-way linked list?
Because of the problems with the single linked list:

1. For one-way linked lists, the search direction can only be one direction, while two-way linked lists can search forward or backward.
2. 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. Therefore, when deleting a node in the previous single linked list, we always find temp. Temp is the previous node of the node to be deleted. We use it to delete the node we want to delete.

In fact, the two-way linked list is not complicated, that is, it has one more precursor pointer pre than the original single linked list.
The idea of adding, deleting, modifying and querying a two-way linked list is as follows:

Code implementation:

package Linked list;

public class DoubleLinked {
    public static void main(String[] args) {
        //test
        //Create node
        Node n1 = new Node(5);
        Node n2 = new Node(4);
        Node n3 = new Node(10);
        Node n4 = new Node(6);
        //Create a two-way linked list
        doubleLinkedList list = new doubleLinkedList();
        //Add function test
        list.add(n1);
        list.show(); //Output 5
        list.add(n2);
        list.add(n3);
        list.add(n4);
        list.show(); //Output 5 4 10 6, 6 4 10 5
        //Test of deletion function: we delete 5
        list.delete(5);
        list.show(); //Output 4 10 6, 6 4 10
        //Test of update method: we update 4 to 88
        list.update(4,88);
        list.show();//Output 88 10 6, 6 10 88
        //Test of insertion method: we insert 99 after 88, that is, insert 99 in the second position
        //That is, index=2, newNode.data=99
        Node n5 = new Node(99);
        list.insert(2,n5);
        list.show(); //Output 99 88 10 6, 6 10 88 99
    }
}

//Define bidirectional linked list
class doubleLinkedList{

    //Define header node
    Node headNode = new Node(0);

    //Return header node
    public Node getHeadNode(){ return headNode; }
    //-----------------------------------------------
    //Add node
    //Nothing special, just add it at the end by default
    public void add(Node newNode){
        Node temp = headNode;
        while (true){
            if(temp.next == null) break;
            temp = temp.next;
        }
        temp.next = newNode;
        newNode.pre = temp;
    }
    //-------------------------------------------------
    //Delete delete
    public void delete(int target){ //Target delete target value
        Node temp = headNode;
        boolean flag = false; //Flag whether the last node is deleted
        //If the last node is deleted, we should change the operation
        while (true){
            if(temp.next == null) {
                flag = true; //The last node is deleted
                break;
            }
            if(temp.data == target){
                //The precursor pointer of the next node of temp points to the previous node of temp node
                temp.next.pre = temp.pre;
                //The subsequent pointer of the previous node of temp points to the next node of temp node
                temp.pre.next = temp.next;
                break; //Exit loop
            }
            temp = temp.next; //Pointer backward
        }
        if(flag == true){
            //Because the last node is deleted, and temp represents the last node
            //Then we first set the subsequent pointer of the previous node of temp to null
            temp.pre.next = null;
            //Then set the precursor pointer of temp to null
            temp.pre= null;
        }
    }
    //---------------------------------------------
    //insert elements: insert
    //index: where to insert. newNode: insert a new node
    public void insert(int index,Node newNode){
        Node temp = headNode;
        int cnt = 0; //Counter, which node position does the count traverse to
        while (true){
            if(temp == null) break;
            if(cnt == index){ //Find insertion location
                //The subsequent pointer of the previous node of temp points to the new node
                temp.pre.next = newNode;
                //The precursor pointer of the new node points to the previous node of temp
                newNode.pre = temp.pre;
                //The precursor pointer of temp points to the new node
                temp.pre = newNode;
                //The subsequent pointer of the new node points to temp
                newNode.next = temp;
                break;
            }
            temp = temp.next;
            cnt++;
        }
    }
    //---------------------------------------------
    //Update method update
    //srcData, target element to update, targetData: data element to replace
    public void update(int srcData,int targetData){
        Node temp = headNode;
        while (true){
            if(temp == null) break;
            if(temp.data == srcData){
                temp.data = targetData;
                break;
            }
            temp = temp.next;
        }
    }

    //---------------------------------------------
    //Traversal queue
    public void show(){
        Node temp = headNode;
        System.out.print("The backward traversal data is:");
        while (true){
            if(temp.next == null) break;
            //The pointer moves successively and then outputs the value to avoid the data value in the header node
            //Because our default header node does not put values
            temp = temp.next;
            System.out.print(temp.data+" ");
        }
        System.out.println();
        System.out.print("The forward traversal data is:");
        while (true){
            if(temp.pre == null) break;
            //This is also to avoid the data value in the header node
            //It's written differently because it's traversed from back to front
            System.out.print(temp.data+" ");
            temp = temp.pre;
        }
        System.out.println();
    }



}


//Define node
class Node{
    public int data; //Data domain
    public Node next; //next is a pointer to the following node
    public Node pre; //pre is a pointer to the previous node

    //Directly declare a node in the constructor
    public Node(int data){
        this.data = data;
    }

    //Print node information

    @Override
    public String toString() {
        return "Node{" +
                "data=" + data +
                '}';
    }
}

Ring linked list to solve Joseph Ring problem

Why is there a one-way circular linked list?
Look at the problem of losing handkerchiefs:

Logic diagram of ring linked list:

The circular linked list also involves the very classic Joseph Ring:

for instance:
Now there are five children (n = 5), and then start counting from the first person (i.e. k = 1). When the number is 2 (m = 2), the child will be out of the queue.
The schematic diagram is as follows:
At the beginning, there were five children. They were in the state of a circular linked list:

It can be seen from the meaning of the question that the first person starts counting, and the person whose check-in number is 2, that is, No. 2, leaves the queue. After deleting the node, the situation is as follows:

Then start counting from 3, because it is the first person behind No. 2 who has just left the queue, so node 2 is the fourth person, so he leaves the queue. The node is deleted, that is, the current order of leaving the queue is 2 - > 4. The following analysis logic is the same. It is worth noting that when there is only one node left at the end of deletion, it can also form a closed loop:

Implementation of one-way ring linked list:
Idea of creating circular linked list:


Idea of out of queue sequence method:

There are three pointer variables. One is first, which points to the first node (i.e. the first person) of the current ring linked list. It will not change and always points to the first person. Then there are two pointers, boy and curBoy. Where curBoy is an auxiliary pointer, pointing to our current boy node (currentBoy), which is used to help build our ring linked list. The boy pointer represents each of our boy nodes. There are detailed comments in the code. Some abstractions are described here.

package Linked list;

public class Josepfu {
    public static void main(String[] args) {
        //test
        //Create circular linked list
        CircleSingleLinkedList list = new CircleSingleLinkedList();
        //We created a circular linked list with five children
        list.addBoy(5);
        //Test traversal function
        list.showBoy(); //Output 1,2,3,4,5
        //Now let's test the circle order
        list.countBoy(1,2,5);//Output 2,4,1,5,3
    }
}

//Create circular linked list
class CircleSingleLinkedList{
    //Create a new first node to represent the first Boy node. You can have no value first, because we are not sure about the number
    public Boy first = null;

    //-------------------------------------------------------------
    //We provide an addBoy method to receive the number of nodes in the ring
    //Then create the corresponding ring linked list
    public void addBoy(int n){
        Boy curBoy = null; //Auxiliary pointer, used to help build a ring linked list
        //Use the for loop to create a circular linked list
        for (int i = 1; i <= n; i++) {
            //According to the number, a boy node is created every cycle
            Boy boy = new Boy(i);
            if(i == 1){ //It means meeting the first child
                //Then we point the first pointer to it, and the first will not move after that
                first = boy;
                first.next = first; //Point to yourself and form a ring
                curBoy = first; //Let curBoy point to the first child
            }else{ //This indicates that it is not the first boy node
                //curBoy points to the first node. The following sentence points the next field of the first node to the new boy node
                curBoy.next = boy;
                //The next field of the new boy node should point to the head node first to form a loop
                boy.next = first;
                //Our auxiliary pointer should also point to the second node, not to the first node
                curBoy = boy;
                //The following nodes 2, 3, 4... Are added with this logic
            }
        }
    }

    //--------------------------------------------------------
    //Traverse the current circular linked list
    public void showBoy(){
        //Determine whether the linked list is empty
        if(first == null) {
            System.out.println("No children, Ow");
            return;
        }
        //Because the first header pointer cannot move, we still use an auxiliary pointer for traversal
        Boy curBoy = first;
        while (true){
            System.out.printf("The child's number is %d \n",curBoy.no);
            if(curBoy.next == first) { //It indicates that the traversal has reached the head again. The traversal is over
                break;
            }
            curBoy = curBoy.next; //curBoy pointer moves back
        }
    }

    //-------------------------------------------------------
    //According to the user's input, calculate the order of the child's circle
    //startNo: indicates the number of children to start counting
    //countNum: indicates how many times to count
    //Num: indicates how many children are in the circle
    public void countBoy(int startNo,int countNum,int nums){
        //First, create an auxiliary traversal helper
        Boy helper = first;
        //Make it point to the last node in the linked list
        while (true){
            if(helper.next == first) break; //Traverse to the last node and exit the loop
            helper = helper.next;
        }
        //Before the child counts off, let the first and helper move startNo-1 times, minus once because they only need to move once between two points
        //It means to make these two pointers ready before moving, because the first point must be the first child to count first
        //Pay attention to drawing and understanding, otherwise it will be a little abstract
        for (int i = 0; i < startNo-1; i++) {
            first = first.next;
            helper = helper.next;
        }
        //Loop out of the loop until there is only one node left in the last loop
        while (true){
            if(helper == first) break; //If there is only the last node in the circle, exit the loop
            //When the child counts off, let the helper and first pointer move countNum-1 times at the same time
            //That is, how many times to report and move
            for (int i = 0; i < countNum-1; i++) {
                first = first.next;
                helper = helper.next;
            }
            //After the move, it indicates that the counting is completed, and the boy node pointed to by first is circled
            System.out.printf("No %d Children out of the circle \n",first.no);
            //At this time, point first to its next node and make the node to which first currently points out of the circle
            first = first.next;
            helper.next = first;
            //If the node pointed to by the original first does not have any reference, it will be recycled by GC
        }
        //Now, you can complete all the operations by turning the last child out of the circle
        System.out.printf("The number of the last child out of the circle is:%d \n",first.no);
    }
}

//Create a Boy class to represent a node
class Boy{
    public int no; //Number, which can be understood as data field
    public Boy next; //Pointer field, pointing to the next node

    //When constructing a new Boy instance, assign the value of the data field to the node
    public Boy(int no){
        this.no = no;
    }

    //Print node information
    @Override
    public String toString() {
        return "Boy{" +
                "no=" + no +
                '}';
    }
}

Posted by dunnsearch on Sat, 06 Nov 2021 01:34:53 -0700