Linear list of data structure (sequential list, linked list, stack, queue)

1, Linear table

Linear table is the most basic, simplest and most commonly used data structure. A linear table is a finite sequence of n data elements with the same characteristics.

Precursor element:
If element A precedes element B, it is called the precursor element of B
Successor elements:
If element B follows element a, it is called the successor element of A
**Features of linear table: * * there is a "one-to-one" logical relationship between data elements.

  1. The first data element has no precursor, and this data element is called a header node;
  2. The last data element has no successor, and this data element is called the tail node;
  3. Except for the first and last data elements, other data elements have only one precursor and one successor.

If the linear table is defined in mathematical language, it can be expressed as (a1,... ai-1,ai,ai+1,... an), ai-1 is ahead of ai,ai is ahead of ai+1, ai-1 is the precursor element of ai, and ai+1 is the successor element of ai

Classification of linear tables:
The data storage mode of linear table can be sequential storage or chain storage. According to different data storage modes, linear table can be divided into sequential table and chain list.

1.1 sequence table

The sequential table is a linear table saved in the form of an array in the computer memory. The sequential storage of the linear table refers to the sequential storage of each element in the linear table with a group of storage units with continuous addresses, so that the data elements that ring logically in the linear table are stored in adjacent physical storage units, That is, the logical adjacency relationship between data elements is reflected through the adjacency relationship physically stored by data elements.

1.1.1 implementation of sequence table

Sequence table API design:

Code implementation of sequence table:

public class SequenceList<T>{
    // An array of storage elements
    private T[] eles;
    // Record the number of elements in the current sequence table
    private  int N;

    // Construction method
    public SequenceList(int capacity) {
        // Initialize array
        this.eles = (T[])new Object[capacity];
        // Initialization length
        this.N = 0;
    }

    // Set a linear table as an empty table
    public void clear(){
        this.N = 0;
    }
    // Judge whether the current linear table is empty
    public boolean isEmpty(){
        return N==0;
    }
    // Gets the length of the linear table
    public int length(){
        return N;
    }
    // Gets the element at the specified location
    public T get(int i){
        return eles[i];
    }
    // Add element t to linear table
    public void insert(T t){
        eles[N++] = t; // Ingenious writing
    }
    //Insert element t at element i
    public void insert(int i,T t){
        // First, move the element at the index of i and its subsequent elements backward one bit at a time
        for (int index=N-1;index>i;index--){
            eles[index] = eles[index-1];
        }
        // Then put other elements in the i index
        eles[i] = t;
    }
    //Deletes the element at the specified location i and returns the element
    public T remove(int i){
        // Record the value at the index
        T current = eles[i];
        // The elements behind the index can be moved backward in turn
        for (int index=i;index<N-1;index++){
            eles[index] = eles[index+1];
        }
        // Number of elements - 1
        N--;
        return current;
    }
    //Find where the t element first appears
    public int indexOf(T t){
        for (int i=0;i<N;i++){
            if (eles[i].equals(t)) {
                
                return 1;
            }
        }
        return -1;
    }
}

1.1.2 traversal of sequence table (just understand and learn)

Generally, when storing data as a container, we need to provide traversal mode to the outside, so we need to provide traversal mode to the sequence table.

In java, foreach loops are generally used to traverse collections. If you want our SequenceList to support foreach loops, you need to do the following:

  1. Let SequenceList implement Iterable interface and rewrite iterator method;
  2. Provide an internal class SIterator inside the SequenceList, implement the Iterator interface, and override hasNext method and next method;

code:

1.1.3 variable capacity of sequence table

In the previous implementation, when we use SequenceList, we first create an object with new SequenceList(5). When creating an object, we need to specify the size of the container and initialize the array of the specified size to store elements. When we insert elements, if we have already inserted 5 elements and continue to insert data, we will report an error and cannot insert them. This design does not conform to the design concept of the container, so we should consider its capacity scalability when designing the sequence table.

Considering the capacity scalability of the container is actually changing the size of the array storing data elements. When do we need to change the size of the array?

  1. When adding elements:
    When adding elements, you should check whether the size of the current array can accommodate new elements. If not, you need to create a new array with larger capacity. Here, we create a new array storage element with twice the capacity of the original array.

  2. When removing an element:
    When removing elements, you should check whether the size of the current array is too large. For example, you are using an array with 100 capacity to store 10 elements, which will waste memory space. You should create an array with smaller capacity to store elements. If we find that the number of data elements is less than 1 / 4 of the capacity of the array, we create a new array storage element that is 1 / 2 of the capacity of the original array.

Variable capacity code of sequence table:

public class SequenceList<T> implements Iterable<T>{
    //An array of storage elements
    private T[] eles;
    //Record the number of elements in the current sequence table
    private int N;

    //Construction method
    public SequenceList(int capacity){
        //Initialize array
        this.eles=(T[])new Object[capacity];
        //Initialization length
        this.N=0;
    }

    //Set a linear table as an empty table
    public void clear(){
        this.N=0;
    }

    //Judge whether the current linear table is empty
    public boolean isEmpty(){
       return N==0;
    }

    //Gets the length of the linear table
    public int length(){
        return N;
    }

    //Gets the element at the specified location
    public T get(int i){
        return eles[i];
    }

    //Adds an element t to the linetype table
    public void insert(T t){
    	// Capacity expansion
        if (N==eles.length){
            resize(2*eles.length);
        }

        eles[N++]=t;
    }

    //Insert element t at element i
    public void insert(int i,T t){
    	// Capacity expansion
        if (N==eles.length){
            resize(2*eles.length);
        }

        //First, move the element at the i index and its subsequent elements backward by one bit
        for(int index=N;index>i;index--){
            eles[index]=eles[index-1];
        }
        //Then put the t element at the i index
        eles[i]=t;

        //Number of elements + 1
        N++;
    }

    //Deletes the element at the specified location i and returns the element
    public T remove(int i){
        //Record the value at index i
        T current = eles[i];
        //The elements behind index i can be moved forward one bit in turn
        for(int index=i;index<N-1;index++){
            eles[index]=eles[index+1];
        }
        //Number of elements - 1
        N--;
		// Volume reduction
        if (N<eles.length/4){
            resize(eles.length/2);
        }

        return current;
    }


    //Find where the t element first appears
    public int indexOf(T t){
        for(int i=0;i<N;i++){
            if (eles[i].equals(t)){
                return i;
            }
        }
        return -1;
    }

    //According to the parameter newSize, reset the size of eles - expansion and contraction
    public void resize(int newSize){
        //Defines a temporary array that points to the original array
        T[] temp=eles;
        //Create a new array
        eles=(T[])new Object[newSize];
        //Copy the data of the original array to the new array
        for(int i=0;i<N;i++){
            eles[i]=temp[i];
        }
    }
    @Override
    public Iterator<T> iterator() {
        return new SIterator();
    }

    private class SIterator implements Iterator{
        private int cusor;
        public SIterator(){
            this.cusor=0;
        }
        @Override
        public boolean hasNext() {
            return cusor<N;
        }

        @Override
        public Object next() {
            return eles[cusor++];
        }
    }
}

1.1.4 time complexity of sequence table

get(i): it is not difficult to see that no matter how large the number of data elements N is, the corresponding elements can be obtained only once eles[i], so the time complexity is O(1);
Insert (int i, t, t): every time you insert, you need to move the elements behind I. as the number of elements N increases, more elements will be moved
The inter complex is O(n);
remove(int i): every time you delete, you need to move the elements behind the I position once. With the increase of the amount of data N, more elements are moved, and the time complexity is O(n);
Because the bottom layer of the sequence table is implemented by an array, and the length of the array is fixed, the container expansion operation is involved in the process of operation. This will lead to the time complexity of the sequence table in the use process is not linear. At some nodes that need to be expanded, the time will increase sharply, especially the more elements, the more obvious this problem is.

1.1.5 ArrayList implementation in Java (source code analysis)

The bottom layer of ArrayList set in java is also a sequence table, which is implemented by array. It also provides functions such as addition, deletion, modification, query and capacity expansion.

  1. Whether to implement with array; yes
  2. Whether there is capacity expansion operation; yes
  3. Whether traversal mode is provided; yes

1.2 linked list

We have implemented the linear table using the sequential storage structure before. We will find that although the query of the sequential table is fast and the time complexity is O(1), the efficiency of addition and deletion is relatively low, because each addition and deletion operation is accompanied by a large number of data elements. Is there a solution to this problem? Yes, we can use another storage structure to realize linear list and chain storage structure.

Linked list is a non continuous and non sequential storage structure on the physical storage unit. Its physical structure can not only represent the logical order of data elements. The logical order of data elements is realized through the pointer link order in the linked list. The linked list consists of a series of nodes (each element in the linked list is called a node), which can be generated dynamically at run time.

How do we use the linked list? According to the object-oriented idea, we can design a class to describe the node, use one attribute to describe the element stored in the node, and use another attribute to describe the next node of the node.
Node API design:

Node class implementation:

public class Node<T> { 
	//Storage element 
	public T item; 
	//Point to the next node 
	public Node next; 
	//Construction method
	public Node(T item, Node next) { 
		his.item = item; 
		this.next = next; 
	}
}

Generate linked list:

public static void main(String[] args) throws Exception { 
	//Build node 
	Node<Integer> first = new Node<Integer>(11, null);
	Node<Integer> second = new Node<Integer>(13, null);
	Node<Integer> third = new Node<Integer>(12, null); 
	Node<Integer> fourth = new Node<Integer>(8, null); 
	Node<Integer> fifth = new Node<Integer>(9, null); 
	//Generate linked list 
	first.next = second; 
	second.next = third; 
	third.next = fourth; 
	fourth.next = fifth; 
}

1.2.1 one way linked list

Unidirectional linked list is a kind of linked list. It is composed of multiple nodes. Each node is composed of a data field and a pointer field. The data field is used to store data and the pointer field is used to point to its subsequent nodes. The data field of the head node of the linked list does not store data, and the pointer field points to the first node that really stores data.

1.2.1.1 API design of one-way linked list

1.2.1.2 one way linked list code implementation
public class LinkList<T> implements Iterable<T>{
    // Record header node
    private Node head;
    // Record the length of the linked list
    private int N;


    // Internal class: node class
    private class Node{
        // Store data
        T item;
        // Next node
        Node next;

        public Node(T item, Node next) {
            this.item = item;
            this.next = next;
        }
    }

    public LinkList() {
        // Initialize header node
        this.head = new Node(null,null);
        // Initialization length
        this.N = 0;
    }

    // Empty linked list
    public void clear(){
        this.head=null;
        this.N=0;
    }

    // Get the length of the linked list
    public int length(){
        return N;
    }

    // Determine whether the linked list is empty
    public boolean isEmpty(){
        return N == 0;
    }

    // Gets the element at the specified location i
    public T get(int i){
        // Through the loop, start from the node and look back, i times in turn, you can find the corresponding element
        Node n = head.next;
        for (int index=0;index<i;index++){
            n= n.next;
        }
        return n.item;
    }

    // Add element to linked list
    public void insert(T t){
        // Find the last node
        Node n = head;
        while(n.next!=null){
            n = n.next;
        }
        //Create a new node and save the element t
        Node newNode = new Node(t,null);
        // Make the current last node point to the new node
        n.next = newNode;
        // Number of elements + 1
        N++;
    }

    // Add an element to the specified location i
    public void insert(int i,T t){
        // Find the previous node of i position first
        Node pre = head;
        for (int index=0;index<=i-1;index++){
            pre = pre.next;
        }
        // Find the node at position i
        Node curr = pre.next;
        // Create a new node, and the new node needs to point to the node at the original i location
        Node newNode = new Node(t,curr);
        // The previous node in the original i position can point to the new node
        pre.next = newNode;
        // Number of elements + 1
        N++;
    }

    // Deletes the element at the specified position i and returns the deleted element
    public T remove(int i){
        // Find the previous node at i first
        Node pre = head;
        for (int index=0;index<=i-1;index++){
            pre = pre.next;
        }
        // To find the node at i position
        Node curr = pre.next;
        // The node at position I points to the next node, that is, find the next node at position i
        Node newNode = curr.next;
        // The previous node points to the next node
        pre.next = newNode;
        // Number of elements - 1
        N--;
        return curr.item;
    }
    //Find the position where the element t first appears in the linked list
    public int indexOf(T t){
        // From the beginning, find each node at a time, take out the item, and compare it with t. If it is the same, it will be found
        Node n = head;
        for (int i=0;n.next!=null;i++){
            n = n.next;
            if (n.item.equals(t)){
                return i;
            }
        }
        return -1;
    }

    // What is the purpose of rewriting? - > > Because the traversal mode is provided for the Linklist class, it allows the external to better audit it and implement the iterator interface
    @Override
    public Iterator<T> iterator(){
        return new LIterator();
    }
    //Inner class
    private class LIterator implements Iterator{
        // Member variable
        private Node n;

        public LIterator() {
            this.n = head;
        }

        @Override
        public boolean hasNext() {
            return n.next!=null;
        }

        @Override
        public Object next() {
            n = n.next;
            return n.item;
        }
    }
}

1.2.2 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 is used to store data. One pointer field is used to point to its successor node and the other pointer field is used to point to the predecessor node.
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.

According to the object-oriented idea, we need to design a class to describe the node. Because the node belongs to the linked list, we implement the node class as an internal class of the linked list class.

1.2.2.1 node API design

1.2.2.2 bi directional linked list API design

1.2.2.3 code implementation of bidirectional linked list

public class TowWayLinkList<T> implements Iterable<T> {
    //First node
    private Node head;
    //Last node
    private Node last;
    //Length of linked list
    private int N;

    //Node class
    private class Node{
        public Node(T item, Node pre, Node next) {
            this.item = item;
            this.pre = pre;
            this.next = next;
        }

        //Store data
        public T item;
        //Point to previous node
        public Node pre;
        //Point to the next node
        public Node next;
    }

    public TowWayLinkList() {
       //Initialize head and tail nodes
        this.head = new Node(null,null,null);
        this.last=null;
        //Number of initialization elements
        this.N=0;
    }

    //Empty linked list
    public void clear(){
        this.head.next=null;
        this.head.pre=null;
        this.head.item=null;
        this.last=null;
        this.N=0;
    }

    //Get linked list length
    public int length(){
        return N;
    }

    //Determine whether the linked list is empty
    public boolean isEmpty(){
        return N==0;
    }

    //Get the first element
    public T getFirst(){
        if (isEmpty()){
            return null;
        }
        return head.next.item;
    }

    //Get the last element
    public T getLast(){
        if (isEmpty()){
            return null;
        }
        return last.item;
    }

    //Insert element t
    public void insert(T t){

        if (isEmpty()){
            //If the linked list is empty:

            //Create a new node
            Node newNode = new Node(t,head, null);
            //Let the new node be called the tail node
            last=newNode;
            //Let the head node point to the tail node
            head.next=last;
        }else {
            //If the linked list is not empty
            Node oldLast = last;

            //Create a new node
            Node newNode = new Node(t, oldLast, null);

            //Make the current tail node point to the new node
            oldLast.next=newNode;
            //Let the new node be called the tail node
            last = newNode;
        }
        //Number of elements + 1
        N++;
    }

    //Inserts the element t at the specified location i
    public void insert(int i,T t){
        //Find the previous node at position i
        Node pre = head;
        for(int index=0;index<i;index++){
            pre=pre.next;
        }
        //Find the node at position i
        Node curr = pre.next;
        //Create a new node
        Node newNode = new Node(t, pre, curr);
        //Let the next node of the previous node at i position become a new node
        pre.next=newNode;
        //Make the previous node at i position become a new node
        curr.pre=newNode;
        //Number of elements + 1
        N++;
    }

    //Gets the element at the specified location i
    public T get(int i){
        Node n = head.next;
        for(int index=0;index<i;index++){
            n=n.next;
        }
        return n.item;
    }

    //Find the position where the element t first appears in the linked list
    public int indexOf(T t){
        Node n = head;
        for(int i=0;n.next!=null;i++){
            n=n.next;
            if (n.next.equals(t)){
                return i;
            }
        }
        return -1;
    }

    //Delete the element at position i and return it
    public T remove(int i){
        //Find the previous node at position i
        Node pre = head;
        for(int index=0;index<i;index++){
            pre=pre.next;
        }
        //Find the node at position i
        Node curr = pre.next;
        //Find the next node at position i
        Node nextNode= curr.next;
        //Let the next node of the previous node of i position become the next node of i position
        pre.next=nextNode;
        //Let the previous node of the next node in position I become the previous node in position i
        nextNode.pre=pre;
        //Number of elements - 1
        N--;
        return curr.item;
    }

    @Override
    public Iterator<T> iterator() {
        return new TIterator();
    }

    private class TIterator implements Iterator{
        private Node n;
        public TIterator(){
            this.n=head;
        }
        @Override
        public boolean hasNext() {
            return n.next!=null;
        }
        @Override
        public Object next() {
            n=n.next;
            return n.item;
        }
    }
}

1.2.2.4 implementation of LinkedList in Java
The LinkedList set in java is also implemented using a two-way linked list, and provides related methods such as addition, deletion, modification and query

  1. Whether the bottom layer is realized by two-way linked list; yes
  2. Whether the node class has three domains; yes

1.2.3 complexity analysis of linked list

get(int i): for each query, you need to start from the head of the linked list and search backward in turn. As the number of data elements N increases, the more elements are compared, and the time complexity is O(n)

Insert (int i, t, t): for each insertion, you need to find the previous element at position I first, and then complete the insertion operation. As the number of data elements N increases, the number of search elements increases
The more elements, the time complexity is O(n);

remove(int i): for each removal, you need to find the previous element at position I first, and then complete the insertion operation. As the number of data elements N increases, the more elements to find, and the time complexity is O(n)

Compared with the sequential list, although the time complexity of insertion and deletion of the linked list is the same, it still has great advantages, because the physical address of the linked list is discontinuous. It does not need to specify the storage space size in advance, or it involves operations such as capacity expansion in the storage process. At the same time, it does not involve the exchange of elements.

Compared with the sequential list, the query performance of the linked list will be lower. Therefore, if there are many query operations in our program, it is recommended to use sequential list, and there are many addition and deletion operations, it is recommended to use linked list.

The following are common application scenarios of linked lists in development

1.2.4 reversal of linked list (single linked list)

The reversal of single linked list is a high-frequency topic in the interview.
Requirements:

The data in the original linked list is: 1 - > 2 - > 3 > 4
After inversion, the data in the linked list is: 4 - > 3 - > 2 - > 1

Reverse API method:

public void reverse(): Reverse the entire linked list 
public Node reverse(Node curr): Invert a node in the linked list curr,And put the reversed curr Node return

Using recursion can complete the inversion. In fact, recursive inversion starts from the first node of the original linked list to store data, and recursively calls to reverse each node in turn until the last node is reversed, and the whole linked list is reversed.

code:

	//Used to reverse the entire linked list
    public void reverse(){

        //Judge whether the current linked list is an empty linked list. If it is an empty linked list, end the operation. If not, call the overloaded reverse method to complete the inversion
        if (isEmpty()){
            return;
        }
        reverse(head.next);
    }

    //Reverses the specified node curr and returns the reversed node
    public Node reverse(Node curr){
        // Exit of recursive call
        if (curr.next==null){
            head.next=curr;
            return curr;
        }
        //Recursively invert the next node of the current node curr; The return value is the previous node of the current node after the linked list is reversed
        Node pre = reverse(curr.next);
        //Let the next node of the returned node become the current node curr;
        pre.next=curr;
        //Make the next node of the current node null
        curr.next=null;
        return curr;
    }

1.2.5 speed pointer

Speed pointer refers to defining two pointers. The moving speed of the two pointers is slow one by one, so as to create the desired difference. This difference allows us to find the corresponding node on the linked list. In general, the moving step of the fast pointer is twice that of the slow pointer

1.2.5.1 intermediate value problem

Let's look at the following code first, and then complete the requirements.

Requirements:
Please improve the getMid method in the Test class to find the intermediate element value of the linked list and return it.
Using the speed pointer, we regard a linked list as a runway. Assuming that the speed of a is twice that of b, then when a runs the full range, b just runs half, so as to find the intermediate node.
As shown in the following figure, at first, the slow and fast pointers point to the first node of the linked list, then slow moves one pointer at a time, and fast moves two pointers at a time.

code:

public class FastSlowTest {

    public static void main(String[] args) throws Exception {
        //Create node
        Node<String> first = new Node<String>("aa", null);
        Node<String> second = new Node<String>("bb", null);
        Node<String> third = new Node<String>("cc", null);
        Node<String> fourth = new Node<String>("dd", null);
        Node<String> fifth = new Node<String>("ee", null);
        Node<String> six = new Node<String>("ff", null);
        Node<String> seven = new Node<String>("gg", null);

        //Complete the pointing between nodes
        first.next = second;
        second.next = third;
        third.next = fourth;
        fourth.next = fifth;
        fifth.next = six;
        six.next = seven;

        //Find intermediate value
        String mid = getMid(first);
        System.out.println("The median value is:"+mid);
    }

    /**
     * @param first The first node of the linked list
     * @return The value of the intermediate node of the linked list
     */
    public static String getMid(Node<String> first) {
        //Define two pointers
        Node<String> fast = first;
        Node<String> slow = first;
        //Use two pointers to traverse the linked list. When the node pointed by the fast pointer has no next node, it can end. After that, the node pointed by the slow pointer is the intermediate value
        while(fast!=null &&fast.next!=null){
            //Change the value of fast and slow
            fast = fast.next.next;
            slow=slow.next;
        }

        return slow.item;
    }

    //Node class
    private static class Node<T> {
        //Store data
        T item;
        //Next node
        Node next;

        public Node(T item, Node next) {
            this.item = item;
            this.next = next;
        }
    }
}
1.2.5.2 is there a ring problem in the one-way linked list

See the following code to complete the requirements:

//Test class 
public class Test { 
	public static void main(String[] args) throws Exception { 
		Node<String> first = new Node<String>("aa", null); 
		Node<String> second = new Node<String>("bb", null); 
		Node<String> third = new Node<String>("cc", null); 
		Node<String> fourth = new Node<String>("dd", null); 
		Node<String> fifth = new Node<String>("ee", null); 
		Node<String> six = new Node<String>("ff", null); 
		Node<String> seven = new Node<String>("gg", null);
		 
		//Complete the pointing between nodes 
		first.next = second; 
		second.next = third; 
		third.next = fourth; 
		fourth.next = fifth; 
		fifth.next = six; 
		six.next = seven; 
		//Generating ring 
		seven.next = third; 
		
		//Determine whether the linked list has a ring 
		boolean circle = isCircle(first);
		System.out.println("first Whether there are links in the linked list:"+circle); 
	}
	
	/**
	* Determine whether there are links in the linked list 
	* @param first First node of linked list 
	* @return ture Is ring, false is acyclic 
	*/ 
	public static boolean isCircle(Node<String> first) { 
		return false; 
	}
	
	//Node class 
	private static class Node<T> { 
		//Store data T item; 
		//Next node 
		Node next; 
		public Node(T item, Node next) { 
			this.item = item; 
			this.next = next; 
		} 
	} 
}

Requirements:
Please improve the isCircle method in the Test class to return whether there are rings in the linked list.
The idea of using the speed pointer is to compare the linked list to a runway. If there are rings in the linked list, then this runway is a circular runway. In a circular runway, two people have a speed difference, so sooner or later, two people will meet. As long as they meet, it means there are rings.



code:

/**
* Determine whether there are links in the linked list
* @param first First node of linked list
* @return ture Is ring, false is acyclic
*/
public static boolean isCircle(Node<String> first) {
        //Define speed pointer
        Node<String> fast = first;
        Node<String> slow = first;

        //Traverse the linked list. If the speed pointer points to the same node, it proves that there is a ring
        while(fast!=null && fast.next!=null){
            //Transform fast and slow
            fast = fast.next.next;
            slow = slow.next;

            if (fast.equals(slow)){
                return true;
            }
        }
        return false;
    }
1.2.5.3 problem of linked list entry

Also look at the following code to complete the requirements:

//Test class 
public class CircleListInTest {
    public static void main(String[] args) throws Exception {
        Node<String> first = new Node<String>("aa", null);
        Node<String> second = new Node<String>("bb", null);
        Node<String> third = new Node<String>("cc", null);
        Node<String> fourth = new Node<String>("dd", null);
        Node<String> fifth = new Node<String>("ee", null);
        Node<String> six = new Node<String>("ff", null);
        Node<String> seven = new Node<String>("gg", null);

        //Complete the pointing between nodes
        first.next = second;
        second.next = third;
        third.next = fourth;
        fourth.next = fifth;
        fifth.next = six;
        six.next = seven;
        //Generating ring
        seven.next = third;

        //Find the entry node of the ring
        Node<String> entrance = getEntrance(first);
        System.out.println("first The entry node elements of the link in the linked list are:"+entrance.item);
    }

Requirements:
Please improve the getentry method in the Test class to find the entry node of the ring in the linked list.
When the fast and slow pointers meet, we can judge that there is a ring in the linked list. At this time, reset a new pointer to the starting point of the linked list, and the step size is 1 as the slow pointer. Then the place where the slow pointer meets the "new" pointer is the entrance of the ring. Proving this conclusion involves the knowledge of number theory, which is omitted here and only about realization.

code:

/**
     * Find the entry node of a ring in a linked list
     * @param first First node of linked list
     * @return Entrance node of ring
     */
    public static Node getEntrance(Node<String> first) {
        //Define speed pointer
        Node<String> fast = first;
        Node<String> slow = first;
        Node<String> temp = null;

        //To traverse the linked list, first find the ring (the fast and slow pointers meet), prepare a temporary pointer, point to the first node of the linked list, and continue to traverse until the slow pointer meets the temporary pointer. Then the node pointed to when meeting is the entrance of the ring
        while(fast!=null && fast.next!=null){
            //Transform speed pointer
            fast = fast.next.next;
            slow = slow.next;

            //Judge whether the fast and slow pointers meet
            if (fast.equals(slow)){
                temp = first;
                continue;
            }

            //Let temporary nodes transform
            if (temp!=null){
                temp = temp.next;
                //Determine whether the temporary pointer meets the slow pointer
                if (temp.equals(slow)){
                    break;
                }
            }
        }
        return temp;
    }

1.2.6 circular linked list

Circular linked list, as the name suggests, the whole linked list should form a circular ring. In a one-way linked list, the pointer of the last node is null and does not point to any node
Point, because there is no next element. To realize the circular linked list, we only need to make the pointer of the last node of the one-way linked list point to the head node.

Construction of circular linked list: seven.next = first;

1.2.7 problems

Problem Description:
It is said that after the Romans occupied jotapat, 39 Jews hid in a cave with Joseph and his friends. 39 Jews decided to die rather than be caught by the enemy, so they decided to commit suicide. 41 people lined up in a circle. The first person counted off from 1, and then, if someone counted off to 3, Then this person must commit suicide, and then his next person starts counting again from 1 until everyone commits suicide. However, Joseph and his friends did not want to comply. So Joseph asked his friend to pretend to obey first. He arranged his friend and himself in the 16th and 31st positions, so as to escape the death game.
Problem conversion:
41 people sit in a circle. The first person is numbered 1, the second person is numbered 2, and the nth person is numbered n.

  1. The person with number 1 starts to count back from 1, and the person with number 3 exits the circle;
  2. The next person starting from the person who quit will count off from 1 again, and so on;
  3. Find the number of the last person to quit.

Illustration:

Problem solving ideas:

  1. A one-way circular linked list with 41 nodes is constructed, and the values of 1 ~ 41 are stored respectively to represent these 41 people;
  2. Use the counter count to record the value of the current message;
  3. Traverse the linked list, once per cycle, count + +;
  4. Judge the value of count. If it is 3, delete the node from the linked list, print the value of the node, and reset the count to 0;

code:

public class JosephTest {
    public static void main(String[] args) {
        //Solve the Joseph problem

        //1. Build a circular linked list, including 41 nodes, and store values between 1 and 41 respectively
        //For the first node
        Node<Integer> first = null;
        //Used to record the previous node
        Node<Integer> pre = null;

        for(int i = 1;i<=41;i++){

            //If it is the first node
            if (i==1){
                first = new Node<>(i,null);
                pre = first;
                continue;
            }

            //If not the first node
            Node<Integer> newNode = new Node<>(i, null);
            pre.next=newNode;
            pre=newNode; // Reset pre
            //If it is the last node, you need to make the next node of the last node become first and a circular linked list
            if (i==41){
                pre.next=first;
            }

        }

        //2. count counter is required to simulate the alarm
        int count=0;
        //3. Traverse the circular linked list
        //Record the nodes obtained in each traversal, starting from the first node by default
        Node<Integer> n = first;
        //Record the previous node of the current node
        Node<Integer> before = null;
        while(n!=n.next){ // When it's not a self ring
            //Analog alarm
            count++;
            //Judge whether the current number of reports is 3
            if (count==3){
                //If it is 3, delete and call the current node, print the current node, reset count=0, and move the current node n backward
                before.next=n.next;
                System.out.print(n.item+",");
                count=0;
                n=n.next;
            }else{
                //If it is not 3, let before become the current node and move the current node backward;
                before=n;
                n=n.next;
            }
        }

        //Print last element
        System.out.println(n.item);
    }

    //Node class
    private static class Node<T> {
        //Store data
        T item;
        //Next node
        Node next;

        public Node(T item, Node next) {
            this.item = item;
            this.next = next;
        }
    }
}

1.3 stack

1.3.1 stack overview

1.3.1.1 stack in life

The place where goods are stored or passengers stay can be extended to warehouse and transfer station. For example, the hotel in our life now was called an inn in ancient times. It is a place for passengers to rest. Passengers can enter the inn to rest and leave the inn after rest.

1.3.1.2 stack in computer

We introduce the concept of stack in life into the computer, which is a place for data to rest. It is a data structure. Data can enter and exit the stack.

Stack is a data structure based on first in last out. It is a special linear table that can only be inserted and deleted at one end. It stores data according to the principle of first in and last out. The first entered data is pressed into the bottom of the stack, and the last data is at the top of the stack. When data needs to be read, data will pop up from the top of the stack (the last data will be read out first).
We call the action of data entering the stack as pressing the stack, and the action of data leaving the stack as bouncing the stack.

1.3.2 implementation of stack (this article is based on linked list)

(Note: stack is a logical data structure, because the data is stored in physical storage, i.e. sequential storage structure (array) and linked storage structure (linked list). Therefore, there are two implementations of stack, i.e. array implementation and linked list implementation)

1.3.2.1 stack API design

1.3.2.2 stack code implementation
public class Stack<T> implements Iterable<T>{
    //Record first node
    private Node head;
    //Number of elements in the stack
    private int N;

    private class Node{
        public T item;
        public Node next;

        public Node(T item, Node next) {
            this.item = item;
            this.next = next;
        }
    }

    public Stack() {
        this.head = new Node(null,null);
        this.N=0;
    }

    //Judge whether the number of elements in the current stack is 0
    public boolean isEmpty(){
        return N==0;
    }

    //Gets the number of elements in the stack
    public int size(){
        return N;
    }

    //Push the t element onto the stack
    public void push(T t){
        //Find the first node pointed to by the first node
        Node oldFirst = head.next;
        //Create a new node
        Node newNode = new Node(t, null);
        //Let the first node point to the new node
        head.next = newNode;
        //Let the new node point to the original first node
        newNode.next=oldFirst;
        //Number of elements + 1;
        N++;
    }

    //Pop up stack top element
    public T pop(){
        //Find the first node pointed to by the first node
        Node oldFirst = head.next;
        if (oldFirst==null){
            return null;
        }
        //Let the first node point to the next node of the original first node
        head.next=oldFirst.next;
        //Number of elements - 1;
        N--;
        return oldFirst.item;
    }

    @Override
    public Iterator<T> iterator() {
        return new SIterator();
    }

    private class SIterator implements Iterator{
        private Node n;

        public SIterator(){
            this.n=head;
        }

        @Override
        public boolean hasNext() {
            return n.next!=null;
        }

        @Override
        public Object next() {
            n = n.next;
            return n.item;
        }
    }
}
// Test class
public class StackTest {
    public static void main(String[] args) {
        //Create stack object
        Stack<String> stack = new Stack<>();

        //Test stack
        stack.push("a");
        stack.push("b");
        stack.push("c");
        stack.push("d");

        for (String item : stack) {
            System.out.println(item);
        }
        System.out.println("------------------------------");
        //Test cartridge stack
        String result = stack.pop();
        System.out.println("The pop-up elements are:"+result);
        System.out.println("Number of remaining elements:"+stack.size());

    }
}

1.3.3 cases

1.3.3.1 bracket matching problem

Problem Description:

Given a string, it may contain"()"Parentheses and other characters, write a program to check whether the parentheses in the string appear in pairs. 
For example:
	"(Shanghai)(Chang'an)": Correct match 
	"Shanghai((Chang'an))": Correct match 
	"Shanghai(Chang'an(Beijing)(Shenzhen)Nanjing)":Correct match
	"Shanghai(Chang'an))": Wrong match 
	"((Shanghai)Chang'an": Wrong match

Example code:
Core code: isMath method.
analysis:

  1. Create a stack to store the left parenthesis
  2. Traverse the string from left to right to get each character
  3. Judge whether the character is an open bracket. If so, put it into the stack for storage
  4. Judge whether the character is a closing bracket. If not, continue the next cycle
  5. If the character is a closing parenthesis, an element t will pop up from the stack;
  6. Judge whether the element t is null. If not, it proves that there is a corresponding left parenthesis. If not, it proves that there is no corresponding left parenthesis
  7. After the loop ends, judge whether there are left left parentheses in the stack. If there are, they do not match. If not, they match


    Code implementation:
public class BracketsMatchTest {
    public static void main(String[] args) {
        String str = "Shanghai(Chang'an)())";
        boolean match = isMatch(str);
        System.out.println(str+"Whether the brackets in match:"+match);
    }

    /**
     * Determine whether the parentheses in str match
     * @param str A string of parentheses
     * @return Returns true if there is a match, and false if there is no match
     */
    public static boolean isMatch(String str){
        //1. Create a stack object to store the left parenthesis
        Stack<String> chars = new Stack<>();
        //2. Traverse the string from left to right
        for (int i = 0; i < str.length(); i++) {
            String currChar = str.charAt(i)+ "";
            //3. Judge whether the current character is an open bracket. If so, put the character on the stack
            if (currChar.equals("(")){
                chars.push(currChar);
            }else if(currChar.equals(")")){
                //4. Continue to judge whether the current character is bracketed. If so, an open bracket will pop up from the stack and judge whether the pop-up result is null. If NULL, it proves that there is no matching left bracket. If not, it proves that there is a matching left bracket
                String pop = chars.pop();
                if (pop==null){
                    return false;
                }
            }
        }
        //5. Judge whether there are left parentheses in the stack. If so, it proves that the parentheses do not match
        if (chars.size()==0){
            return true;
        }else{
            return false;
        }
    }
}
1.3.3.2 evaluation of inverse Polish expression

The evaluation problem of inverse Polish expression is a kind of problem often encountered in our computer. To study and understand this problem, we must first find out what is inverse Polish expression? To understand the inverse Polish expression, we have to start with infix expression.
Infix expression:
Infix expressions are expressions used in our daily life, such as 1 + 3 * 2,2 - (1 + 3), etc. the characteristic of infix expressions is that binary operators are always placed between two operands.

Infix expression is people's favorite expression because it is simple and easy to understand. But this is not the case for computers, because the operation order of infix expressions is not regular. Different operators have different priorities. If the computer executes infix expressions, it needs to parse the expression semantics and do a lot of priority related operations.

Inverse Polish expression (suffix expression):
Inverse Polish expression is an expression representation first proposed by Polish logician J. lukasewicz in 1929. The characteristic of suffix expression is that the operator is always placed after its related operands.

Requirements:
Given an array representation of an inverse Polish expression containing only four operations of addition, subtraction, multiplication and division, the result of the inverse Polish expression is obtained.

public class ReversePolishNotation { 
	public static void main(String[] args) { 
		//The inverse Polish expression of infix expression 3 * (17-15) + 18 / 6 is as follows 
		String[] notation = {"3", "17", "15", "-", "*","18", "6","/","+"}; 
		int result = caculate(notation); 
		System.out.println("The result of the inverse Polish expression is:"+result); 
		}
		/**
		* @param notaion Array representation of inverse Polish expressions 
		*  @return Calculation results of inverse Polish expression 
		*/ 
	public static int caculate(String[] notaion){ 
		return -1;
	} 
}

Improve the caculate method and calculate the results of the inverse Polish expression.
analysis:

  1. Create a stack object oprands to store operands
  2. Traverse the inverse Polish expression from left to right to get each string
  3. Judge whether the string is an operator. If not, press the operand into the oprands stack
  4. If it is an operator, two operands o 1 and O 2 pop up from the oprands stack
  5. Use this operator to calculate o 1 and o 2 to get the result
  6. Push the result into the oprands stack
  7. After traversal, take out the final result from the stack and return it

    Code implementation:
public class ReversePolishNotationTest {
    public static void main(String[] args) {
        //The inverse Polish expression of infix expression 3 * (17-15) + 18 / 6 is as follows: 6 + 3 = 9
        String[] notation = {"3", "17", "15", "-", "*", "18", "6", "/", "+"};
        int result = caculate(notation);
        System.out.println("The result of the inverse Polish expression is:" + result);
    }

    /**
     * @param notaion Array representation of inverse Polish expressions
     * @return Calculation results of inverse Polish expression
     */
    public static int caculate(String[] notaion) {
        //1. Define a stack to store operands
        Stack<Integer> oprands = new Stack<>();
        //2. Traverse the inverse Polish expression from left to right to get each element
        for (int i = 0; i < notaion.length; i++) {
            String curr = notaion[i];
            //3. Judge whether the current element is an operator or an operand
            Integer o1;
            Integer o2;
            Integer result;
            switch (curr) {
                case "+":
                    //4. Operator, pop up two operands from the stack, complete the operation, and press the calculated result into the stack
                    o1 = oprands.pop();
                    o2 = oprands.pop();
                    result = o2 + o1;
                    oprands.push(result);
                    break;
                case "-":
                    //4. Operator, pop up two operands from the stack, complete the operation, and press the calculated result into the stack
                    o1 = oprands.pop();
                    o2 = oprands.pop();
                    result = o2 - o1;
                    oprands.push(result);
                    break;
                case "*":
                    //4. Operator, pop up two operands from the stack, complete the operation, and press the calculated result into the stack
                    o1 = oprands.pop();
                    o2 = oprands.pop();
                    result = o2 * o1;
                    oprands.push(result);
                    break;
                case "/":
                    //4. Operator, pop up two operands from the stack, complete the operation, and press the calculated result into the stack
                    o1 = oprands.pop();
                    o2 = oprands.pop();
                    result = o2 / o1;
                    oprands.push(result);

                    break;
                default:
                    //5. Operand, put the operand into the stack;
                    oprands.push(Integer.parseInt(curr));
                    break;
            }
        }

        //6. Get the last element in the stack, which is the result of the inverse Polish expression
        int result = oprands.pop();

        return result;
    }
}

1.4 queue

(like the implementation of stack, this article is implemented in the form of linked list)
Queue is a data structure based on first in first out (FIFO). It is a special linear table that can only be inserted at one end and deleted at the other end. It stores data according to the principle of first in first out. The data that enters first is read out first when reading data.

1.4.1 API design of queue

1.4.2 implementation of queue

public class Queue<T> implements Iterable<T>{
    //Record first node
    private Node head;
    //Record the last node
    private Node last;
    //Record the number of elements in the queue
    private int N;


    private class Node{
        public T item;
        public Node next;

        public Node(T item, Node next) {
            this.item = item;
            this.next = next;
        }
    }
    public Queue() {
        this.head = new Node(null,null);
        this.last=null;
        this.N=0;
    }

    //Determine whether the queue is empty
    public boolean isEmpty(){
        return N==0;
    }

    //Returns the number of elements in the queue
    public int size(){
        return N;
    }

    //Insert element t into queue
    public void enqueue(T t){

        if (last==null){
            //The current tail node last is null
            last= new Node(t,null);
            head.next=last;
        }else {
            //The current tail node last is not null
            Node oldLast = last;
            last = new Node(t, null);
            oldLast.next=last;
        }

        //Number of elements + 1
        N++;
    }

    //Take an element from the queue
    public T dequeue(){
        if (isEmpty()){
            return null;
        }

        Node oldFirst= head.next;
        head.next=oldFirst.next;
        N--;

        //Because leaving the queue is actually deleting elements, if the elements in the queue are deleted, you need to reset last=null;

        if (isEmpty()){
            last=null;
        }
        return oldFirst.item;
    }

    @Override
    public Iterator<T> iterator() {
        return new QIterator();
    }

    private class QIterator implements Iterator{
        private Node n;

        public QIterator(){
            this.n=head;
        }
        @Override
        public boolean hasNext() {
            return n.next!=null;
        }

        @Override
        public Object next() {
            n = n.next;
            return n.item;
        }
    }
}

Tags: Java Algorithm data structure linked list

Posted on Sat, 27 Nov 2021 18:42:02 -0500 by ugriffin