[Java data structure and algorithm] linked list includes: single linked list, two-way linked list, ring linked list and Joseph problem

Introduction to linked list


1 linked list is stored in the form of nodes, which is chain storage
2. Each node contains data field and next field: points to the next node
As shown in the figure: it is found that each node of the linked list is not necessarily stored continuously
4 the linked list is divided into the linked list with the leading node and the linked list without the head node, which is determined according to the actual needs

1, One way linked list code implementation

public class SingleLinkedListDemo {

    class SingleLinkedList {
        //First initialize a head node. The head node does not move and does not store specific data
        private HeroNode head = new HeroNode(0, "", "");

        //Return header node
        public HeroNode getHead() {
            return head;
        }

        //Add node to one-way linked list
        //Train of thought, when the numbering sequence is not considered
        //1. Find the last node of the current linked list
        //2. Point the next of the last node to the new node
        public void add(HeroNode heroNode) {

            //Because the head node cannot be moved, we need an auxiliary node to traverse temp
            HeroNode temp = head;
            //Traverse the linked list to find the last
            while(true) {
                //Find the end of the linked list
                if(temp.next == null) {//
                    break;
                }
                //If the last is not found, move temp back
                temp = temp.next;
            }
            //When exiting the while loop, temp points to the end of the linked list
            //Point the next of the last node to the new node
            temp.next = heroNode;
        }

        //The second way is to insert the hero into the specified position according to the ranking when adding the hero
        //(if there is this ranking, the addition fails and a prompt is given)
        public void addByOrder(HeroNode heroNode) {
            //Because the head node cannot be moved, we still use an auxiliary pointer (variable) to help find the added position
            //Because of the single linked list, because the temp we are looking for is the previous node in the add location, otherwise it cannot be inserted
            HeroNode temp = head;
            boolean flag = false; // Whether the number added by flag flag exists. The default is false
            while(true) {
                if(temp.next == null) {//Note that temp is at the end of the linked list
                    break; //
                }
                if(temp.next.no > heroNode.no) { //If the location is found, insert it after temp
                    break;
                } else if (temp.next.no == heroNode.no) {//Indicates that the number of the heroNode you want to add already exists

                    flag = true; //Description number exists
                    break;
                }
                temp = temp.next; //Move back and traverse the current linked list
            }
            //Judge the value of flag
            if(flag) { //Cannot add, description number exists
                System.out.printf("The number of the hero to insert %d It already exists, Can't join\n", heroNode.no);
            } else {
                //Insert it into the linked list, after temp
                heroNode.next = temp.next;//Pass the next node pointed to by the original node to the newly added node
                temp.next = heroNode;//Then let the original node point to the newly added node, so that an insertion is completed
            }
        }

        //Modify the node information according to the no number, that is, the no number cannot be changed
        //explain
        //1. Modify according to the no of newHeroNode
        public void update(HeroNode newHeroNode) {
            //Judge whether it is empty
            if(head.next == null) {
                System.out.println("The linked list is empty~");
                return;
            }
            //Find the node to be modified and number it according to no
            //Define an auxiliary variable
            HeroNode temp = head.next;
            boolean flag = false; //Indicates whether the node is found
            while(true) {
                if (temp == null) {
                    break; //The linked list has been traversed
                }
                if(temp.no == newHeroNode.no) {
                    //find
                    flag = true;
                    break;
                }
                temp = temp.next;
            }
            //Judge whether to find the node to be modified according to the flag
            if(flag) {
                temp.name = newHeroNode.name;
                temp.nickname = newHeroNode.nickname;
            } else { //Can't find
                System.out.printf("No number found %d The node cannot be modified\n", newHeroNode.no);
            }
        }

        //Delete node
        //thinking
        //1. The head cannot be moved, so we need a temp auxiliary node to find the previous node of the node to be deleted
        //2. Explain that when we compare, it is temp next. Comparison between NO and no of the node to be deleted
        public void del(int no) {
            HeroNode temp = head;
            boolean flag = false; // Flag whether the node to be deleted is found
            while(true) {
                if(temp.next == null) { //It has reached the end of the linked list
                    break;
                }
                if(temp.next.no == no) {
                    //Found the previous node temp of the node to be deleted
                    flag = true;
                    break;
                }
                temp = temp.next; //temp backward, traversal
            }
            //Judge flag
            if(flag) { //find
                //Can delete
                temp.next = temp.next.next;
            }else {
                System.out.printf("To delete %d Node does not exist\n", no);
            }
        }

        //Display linked list [traversal]
        public void list() {
            //Determine whether the linked list is empty
            if(head.next == null) {
                System.out.println("The linked list is empty");
                return;
            }
            //Because the head node cannot be moved, we need an auxiliary variable to traverse
            HeroNode temp = head.next;
            while(true) {
                //Judge whether to reach the end of the linked list
                if(temp == null) {
                    break;
                }
                //Output node information
                System.out.println(temp);
                //Move temp backward. Be careful
                temp = temp.next;
            }
        }
    }


    //Define a HeroNode. Each HeroNode object is a node
   static class HeroNode {
        public int no;
        public String name;
        public String nickname;
        public HeroNode next; //Point to next node

        //constructor 
        public HeroNode(int no, String name, String nickname) {
            this.no = no;
            this.name = name;
            this.nickname = nickname;
        }

        //To display the method, we re toString
        @Override
        public String toString() {
            return "HeroNode [no=" + no + ", name=" + name + ", nickname=" + nickname + "]";
        }

    }
}

2, Single chain surface test

Find the number of effective nodes in the single linked list

//Method: obtain the number of nodes in the single linked list (if it is the linked list of the leading node, the head node shall not be counted)
	/**
	 * 
	 * @param head Head node of linked list
	 * @return Returns the number of valid nodes
	 */
	public static int getLength(HeroNode head) {
		if(head.next == null) { //Empty linked list
			return 0;
		}
		int length = 0;
		//Define an auxiliary variable. Here we do not have a statistical header node
		HeroNode cur = head.next;
		while(cur != null) {
			length++;
			cur = cur.next; //ergodic
		}
		return length;
	}

Find the penultimate node in the single lin k ed list [Sina interview question]

//Find the penultimate node in the single lin k ed list [Sina interview question]
	//thinking
	//1. Write a method to receive the head node and an index at the same time 
	//2. index indicates the penultimate node
	//3. Traverse the linked list from beginning to end to get the total length of the linked list getLength
	//4. After getting the size, we can get it by traversing the first size index in the linked list
	//5. If it is found, the node will be returned; otherwise, nulll will be returned
	public static HeroNode findLastIndexNode(HeroNode head, int index) {
		//Judge if the linked list is empty, return null
		if(head.next == null) {
			return null;//Can't find
		}
		//The first traversal obtains the length of the linked list (number of nodes)
		int size = getLength(head);
		//The second time we traverse the size index position is the K-th node from the bottom
		//First do an index check
		if(index <=0 || index > size) {
			return null; 
		}
		//Defined to the auxiliary variable, the for loop locates the index of the reciprocal
		HeroNode cur = head.next; //3 // 3 - 1 = 2
		for(int i =0; i< size - index; i++) {
			cur = cur.next;
		}
		return cur;
	
	}

Reversal of single linked list [Tencent interview question]

Method 1: create a new linked list to save data

  1. First define a node reverseHead = new HeroNode();
  2. Traverse the original linked list from beginning to end. Take out each node and put it at the front of the new linked list reverseHead
  3. The head of the original linked list next = reverseHead. next
//Reverse single linked list
	public static void reversetList(HeroNode head) {
		//If the current linked list is empty or there is only one node, there is no need to reverse and return directly
		if(head.next == null || head.next.next == null) {
			return ;
		}
		
		//Define an auxiliary pointer (variable) to help us traverse the original linked list
		HeroNode cur = head.next;
		HeroNode next = null;// Points to the next node of the current node [cur]
		HeroNode reverseHead = new HeroNode(0, "", "");
		//Traverse the original linked list. Every time you traverse a node, take it out and put it at the front of the new linked list reverseHead
		//Use your head
		while(cur != null) { 
			next = cur.next;//First save the next node of the current node temporarily, because it needs to be used later
			cur.next = reverseHead.next;//Point the next node of cur to the front of the new linked list
			reverseHead.next = cur; //Connect cur to the new linked list
			cur = next;//Move cur back
		}
		//Put head Next points to reversehead Next, realize the inversion of single linked list
		head.next = reverseHead.next;
	}

Method 2: use the data structure of stack

//You can use the data structure of stack to press each node into the stack, and then use the first in and last out characteristics of stack to realize the effect of reverse order printing
	public static void reversePrint(HeroNode head) {
		if(head.next == null) {
			return;//Empty linked list, cannot print
		}
		//To create a stack, press each node into the stack
		Stack<HeroNode> stack = new Stack<HeroNode>();
		HeroNode cur = head.next;
		//Push all nodes of the linked list onto the stack
		while(cur != null) {
			stack.push(cur);
			cur = cur.next; //cur moves backward so that the next node can be pushed in
		}
		//Print the nodes in the stack and pop them out of the stack
		while (stack.size() > 0) {
			System.out.println(stack.pop()); //stack is characterized by first in and last out
		}
	}

Print the single linked list from end to end [Baidu, requires method 1: reverse traversal. Method 2: Stack]

  1. The requirement of the above question is to print the single linked list in reverse order
  2. Method 1: first reverse the single linked list and then traverse it. The problem with this is that it will destroy the structure of the original single linked list. It is not recommended
  3. Method 2: you can use the data structure of stack to press each node into the stack, and then use the first in and last out characteristics of stack to realize the effect of reverse order printing
    An example demonstrates the use of Stack
//You can use the data structure of stack to press each node into the stack, and then use the first in and last out characteristics of stack to realize the effect of reverse order printing
	public static void reversePrint(HeroNode head) {
		if(head.next == null) {
			return;//Empty linked list, cannot print
		}
		//To create a stack, press each node into the stack
		Stack<HeroNode> stack = new Stack<HeroNode>();
		HeroNode cur = head.next;
		//Push all nodes of the linked list onto the stack
		while(cur != null) {
			stack.push(cur);
			cur = cur.next; //cur moves backward so that the next node can be pushed in
		}
		//Print the nodes in the stack and pop them out of the stack
		while (stack.size() > 0) {
			System.out.println(stack.pop()); //stack is characterized by first in and last out
		}
	}

3, Bidirectional linked list

For one-way linked list, the search direction can only be one direction, while the two-way linked list can search forward or backward.
A one-way linked list cannot be deleted by itself and needs to rely on auxiliary nodes, while a two-way linked list can be deleted by itself. Therefore, when we delete a single linked list, we always find temp, which is the previous node of the node to be deleted


Analyze the operation ideas of traversal, addition, modification and deletion of two-way linked list = = > code implementation

  1. The traversal party is the same as the single linked list, but it can be searched forward or backward
  2. Add (added to the end of the bidirectional linked list by default)
    (1) First find the last node of the two-way linked list
    (2) temp.next = newHeroNode
    (3) newHeroNode.pre = temp;
  3. The modification idea is the same as the original one-way linked list
  4. delete
    (1) Because it is a two-way linked list, we can delete a node by ourselves
    (2) Directly find the node to be deleted, such as temp
    (3) temp.pre.next = temp.next / / point the next of the node on temp to the next node of temp, which is equivalent to skipping temp directly
    (4) temp.next.pre = temp.pre; // Similarly, point the pre of the node under temp to the previous node of temp
// Create a class of two-way linked list
    static class DoubleLinkedList {
        // First initialize a head node. The head node does not move and does not store specific data
        private HeroNode2 head = new HeroNode2(0, "", "");

        // Return header node
        public HeroNode2 getHead() {
            return head;
        }

        // Method of traversing bidirectional linked list
        // Display linked list [traversal]
        public void list() {
            // Because the head node cannot be moved, we need an auxiliary variable to traverse
            HeroNode2 temp = head.next;
            while (true) {
                if (temp == null) {
                    break;
                }
                // Output node information
                System.out.println(temp);
                // Move temp backward. Be careful
                temp = temp.next;
            }
        }

        // Add a node to the end of the bidirectional linked list
        public void add(HeroNode2 heroNode) {
            HeroNode2 temp = head;
            while (true) {
                if (temp.next == null) {
                    break;
                }
                temp = temp.next;
            }
            // When exiting the while loop, temp points to the end of the linked list
            // Form a two-way linked list

            temp.next = heroNode;
            heroNode.pre = temp;
        }


        //The second way is to insert the hero into the specified position according to the ranking when adding the hero
        //(if there is this ranking, the addition fails and a prompt is given)
        public void addByOrder(HeroNode2 heroNode) {
            //Because the head node cannot be moved, we still use an auxiliary pointer (variable) to help find the added position
            HeroNode2 temp = head;
            boolean flag = false; // Whether the number added by flag flag exists. The default is false
            while(true) {
                if(temp.next == null) {//Note that temp is at the end of the linked list
                    break; //
                }
                if(temp.next.no > heroNode.no) { //Location found
                    break;
                } else if (temp.next.no == heroNode.no) {//Indicates that the number of the heroNode you want to add already exists

                    flag = true; //Description number exists
                    break;
                }
                temp = temp.next; //Move back and traverse the current linked list
            }
            //Judge the value of flag
            if(flag) { //Cannot add, description number exists
                System.out.printf("The number of the hero to insert %d It already exists, Can't join\n", heroNode.no);
            } else {
                heroNode.next=temp.next;
                temp.next.pre=heroNode;
                temp.next = heroNode;
                heroNode.pre = temp;
            }
        }

        // When modifying the content of a node, you can see that the node content modification of the two-way linked list is the same as that of the one-way linked list
        public void update(HeroNode2 newHeroNode) {
            // Judge whether it is empty
            if (head.next == null) {
                System.out.println("The linked list is empty~");
                return;
            }
            // Find the node to be modified and number it according to no
            // Define an auxiliary variable
            HeroNode2 temp = head.next;
            boolean flag = false; // Indicates whether the node is found
            while (true) {
                if (temp == null) {
                    break;
                }
                if (temp.no == newHeroNode.no) {
                    // find
                    flag = true;
                    break;//Exit the loop directly
                }

                temp = temp.next;
            }
            // Judge whether to find the node to be modified according to the flag
            if (flag) {
                temp.name = newHeroNode.name;
                temp.nickname = newHeroNode.nickname;
            } else { // Can't find
                System.out.printf("No number found %d The node cannot be modified\n", newHeroNode.no);
            }
        }

        // Delete a node from the two-way linked list,
        // explain
        // For a two-way linked list, we can directly find the node to be deleted
        // 2. After finding it, delete it by yourself
        public void del(int no) {

            // Judge whether the current linked list is empty
            if (head.next == null) {// Empty linked list
                System.out.println("The linked list is empty and cannot be deleted");
                return;
            }

            HeroNode2 temp = head.next; // Auxiliary variable (pointer)
            boolean flag = false; // Flag whether the node to be deleted is found
            while (true) {
                if (temp == null) {
                    break;
                }
                if (temp.no == no) {
                    flag = true;
                    break;
                }
                temp = temp.next;
            }
            // Judge flag
            if (flag){
                temp.pre.next = temp.next;
                // What's wrong with our code here?
                // If it is the last node, you do not need to execute the following sentence, otherwise a null pointer will appear
                if (temp.next != null) {
                    temp.next.pre = temp.pre;
                }else {
                    System.out.printf("To delete %d Node does not exist\n", no);
                }
            }

        }
    }

    // Define HeroNode2. Each HeroNode object is a node
    static class HeroNode2{
        public int no;
        public String name;
        public String nickname;
        public HeroNode2 next;// Point to the next node, which is null by default
        public HeroNode2 pre;//Point to the previous node, which is null by default

        public HeroNode2(int no, String name, String nickname) {
            this.no = no;
            this.name = name;
            this.nickname = nickname;
        }

        // To display the method, we re toString
        @Override
        public String toString() {
            return "HeroNode [no=" + no + ", name=" + name + ", nickname=" + nickname + "]";
        }
    }

4, Unidirectional ring linked list and Joseph problem

Joseph problem description

Josephu's problem is: let n people with numbers 1, 2,... N sit around. It is agreed that the person with number k (1 < = k < = n) will count from 1, and the person who counts to m will be listed. Its next person will count from 1, and the person who counts to m will be listed again, and so on until everyone is listed, resulting in a sequence of out of line numbers.

Tip: use a circular linked list without leading nodes to deal with the Josephu problem: first form a single circular linked list with n nodes, and then count from 1 from node k. when m is counted, the corresponding node is deleted from the linked list, and then count from 1 from the next node of the deleted node until the last node is deleted from the linked list. The algorithm ends.

Circular linked list


The idea of constructing a one-way circular linked list

  1. First create the first node, let the first point to the node, and form a ring
  2. Later, when we create a new node, we can add the node to the existing ring linked list

Traverse circular linked list

  1. First, let an auxiliary pointer (variable) curBoy point to the first node
  2. Then go through the circular linked list through a while loop to curboy Next = = first end


According to the user's input, generate the order of a child out of the circle
n = 5, that is, there are 5 people
k = 1, counting off from the first person
m = 2, number 2

  1. You need to create an auxiliary pointer (variable) helper, which should point to the last node of the ring linked list in advance
    Add: let the first and helper move k - 1 time before the child counts off
  2. When the child counts off, let the first and helper pointers move m - 1 times at the same time
  3. At this time, the child node pointed to by first can be circled
    first = first .next
    helper.next = first
    The node pointed to by first has no reference and will be recycled

Order of out circle
2->4->1->5->3

code implementation

public class Josepfu {
    public static void main(String[] args) {
        CircleSingleLinkedList c = new CircleSingleLinkedList();
        c.addBoy(2);
    }
}


// Create a circular one-way linked list
class CircleSingleLinkedList{
    // Create a first node, which currently has no number
    private Boy first = null;

    // Add child nodes to build a circular linked list
    public void addBoy(int nums){
    if (nums < 1){
        System.out.println("nums The value of is incorrect");
        return;
        }
        Boy curBoy = null; // Auxiliary pointer to help build a circular linked list
        // Use for to create our circular linked list
        for (int i = 1; i <= nums; i++) {
        // Create a child node according to the number
            Boy boy = new Boy(i);
            if (i == 1){
                first = boy;
                boy.setNext(first);// Constituent ring
                curBoy = first; // Let curBoy point to the first child
                System.out.println(curBoy == first);
            }else {
                //The idea of double pointers is used here. The head pointer frist and the tail pointer curBoy (auxiliary pointer), but curBoy will move back with the new node
                //curBoy = boy is this line of code, which keeps curBoy pointing to the end. For curBoy Setnext (boy) is a wonderful line of code
                //Question: what might the pointer to the next node point to? This is because curBoy = boy
                //When this line of code assigns boy to curBoy, it actually assigns the address and the assignment between objects. Therefore, the pointer at this time represents the object of boy
                //When the auxiliary pointer points to the next node, the boy object will also point to the next boy node. System.out.println(curBoy == first)
                //The result of this line of code is true, which means that curBoy and first point to the same address
                curBoy.setNext(boy);
                boy.setNext(first);
                curBoy = boy;
            }
        }
    }
    // Traverse the current circular linked list
    public void showBoy() {
        // Determine whether the linked list is empty
        if (first == null) {
            System.out.println("There are no children~~");
            return;
        }
        // Because first cannot move, we still use an auxiliary pointer to complete the traversal
        Boy curBoy = first;
        while (true) {
            System.out.printf("Child's number %d \n", curBoy.getNo());
            if (curBoy.getNext() == first) {// Description: the traversal has been completed
                break;
            }
            curBoy = curBoy.getNext(); // curBoy move back
        }
    }
    // According to the user's input, calculate the order of the child's circle
    /**
     *
     * @param startNo
     *            It means counting from the first child
     * @param countNum
     *            It means counting
     * @param nums
     *            Indicates how many children were in the circle at first
     */
    public void countBoy(int startNo, int countNum, int nums){
        // Check the data first
        if (first == null || startNo < 1 || startNo > nums) {
            System.out.println("Error in parameter input, please re-enter");
            return;
        }
        // Create an auxiliary pointer to help the child out of the circle, that is, point to the tail. When the child out of the circle, this auxiliary pointer is equal to temp in the single linked list
        //Pointer, and then use it in the auxiliary pointer to delete the node, which is the work of children out of the circle
        Boy helper = first;
        // You need to create an auxiliary pointer (variable) helper, which should point to the last node of the ring linked list in advance
        while (true) {
            if (helper.getNext() == first) { // Note the helper points to the last child node
                break;
            }
            helper = helper.getNext();
        }

        //Before the child counts off, let the first and helper move startNo - 1 time, because it is not guaranteed to count off from the first child
        //First move to startNo, because the node actually starts from 0, so you need to subtract one. In addition, the helper is the one before the first
        for(int j = 0; j < startNo - 1; j++) {
            first = first.getNext();
            helper = helper.getNext();
        }

        //When the child counts off, let the first and helper pointers move m - 1 times at the same time, and then make a circle
        //Here is a loop operation, knowing that there is only one node in the circle
        while (true){
            if (first == helper){//There is only one node left
                break;
            }
            //Let the first and helper pointers move countNum - 1 at the same time
            for(int j = 0; j < countNum - 1; j++){
                first = first.getNext();
                helper = helper.getNext();
            }
            //At this time, the node first points to is the child node to be circled
            System.out.printf("child%d Out of circle\n", first.getNo());
            first = first.getNext();//Point the first pointer to the next node
            helper.setNext(first);//Point the node represented by the helper to first, so that the node out of the circle can be deleted
        }
        System.out.printf("Number of the last child left in the circle%d \n", first.getNo());

    }
}

// Create a Boy class to represent a node
class Boy {
    private int no;// number
    private Boy next; // Point to the next node, null by default

    public Boy(int no) {
        this.no = no;
    }

    public int getNo() {
        return no;
    }

    public void setNo(int no) {
        this.no = no;
    }

    public Boy getNext() {
        return next;
    }

    public void setNext(Boy next) {
        this.next = next;
    }

}

Keywords: Java data structure linked list

Added by murtoz on Wed, 02 Feb 2022 14:59:38 +0200