The previously learned sequence table query is very fast, and the time complexity is O(1), but the efficiency of addition, deletion and modification is very low, because each addition, deletion and modification will move the elements. You can use another storage method - chain storage structure.
Linked list is a non continuous and non sequential storage structure on physical storage unit. A linked list consists of a sequence of nodes (each element in the list becomes a node).
Node API design:
Class name | Node |
Construction method | Node (T, node next) creates a node object |
Member variable | T item: store data Node next: points to the next node |
Node class:
public class Node<T>{ Node next; private T item; public Node(Node next, T item) { this.next = next; this.item = item; } }
Generate linked list:
public class TestNode { public static void main(String[] args) { // Generation node Node<Integer> one = new Node<Integer>(null,12); Node<Integer> two = new Node<Integer>(null,16); Node<Integer> three = new Node<Integer>(null,11); Node<Integer> four = new Node<Integer>(null,10); //Generate linked list one.next = two; two.next = three; three.next =four; } }
1. Single 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. The data field is used to store data and the pointer field is used to point to 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 actually stores data.
API design of one-way linked list
Class name | LinkList | ||||||||||||||||
Construction method | LinkList(): create a LinkList object | ||||||||||||||||
Member variable | private Node head; Record first node private int N; Record the length of the linked list | ||||||||||||||||
Member inner class | private class Node; Node class | ||||||||||||||||
Member method |
|
Java code implementation of one-way linked list
package com.ycy.Algorithm.LinkList; import java.util.Iterator; /** * The head of the linked list cannot be moved * @param <T> */ public class LinkList<T> implements Iterable<T>{ private Node head;//The head node of the linked list cannot be moved private int N;//Number of nodes public LinkList() { this.head = new Node(null,null); N = 0; } /** * Node inner class */ private class Node{ //Store data T item; //Next node Node next; public Node(T item,Node next) { this.item = item; this.next = next; } } /** * Empty linked list */ public void clear(){ head.item=null; head.next=null;// If the head node next is null, it is an empty linked list this.N=0; } /** * Determine whether the linked list is empty */ public boolean isEmpty(){ return this.N==0; } /** * Get the length of the linked list */ public int length(){ return this.N; } /** * Read the element value at position i of the linked list and return */ public T get(int i){ //Illegality check if(i<0 || i > this.N){ throw new RuntimeException("Illegal location"); } // n also refers to the head node Node n = head; for(int index=0; index<i; index++){ n = n.next; } return n.item; } /** * Insert data into the linked list t */ public void insert(T t){ // The head cannot be moved, otherwise the linked list cannot be found // You can define a temporary Node and a pointer to the head by moving the pointer Node n = head; // Get tail node while(true){ // When there is just one node (header node) if(n.next == null){ break; } n = n.next; } //When the table is empty, it can be inserted Node node = new Node(t, null); n.next =node; this.N ++; } /** * Insert data t at position i */ public void insert(T t,int i){ // Illegality check if(i < 0 || i > this.N){ throw new RuntimeException("Insertion position❌"); } Node pre = head; for(int index=0;index <= i-1; index++){ pre = pre.next; } Node current = pre.next; //Link the following nodes first Node newNode = new Node(t,null); pre.next = newNode; newNode.next = current; this.N++; } /** * Removes and returns the element value at position i */ public T remove(int i){ // Illegality check if(i < 0 || i >this.N){ throw new RuntimeException("Error deleting location"); } Node n =head; for(int index=0;index <= i-1;index ++){ n = n.next; } //Node to delete Node curr = n.next; n.next = curr.next; this.N--;//Number of nodes minus one return curr.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.item.equals(t)){ return i; } } return -1; } @Override public Iterator iterator() { return new Iterator() { Node n =head; @Override public boolean hasNext() { return n.next !=null; } @Override public Object next() { //Move down one pointer n = n.next; return n.item; } }; } }
Add a point: after the assignment of the linked list to the new linked list, the two linked lists will be affected. In other words, the address is assigned to it. Their operation is the same object of the same memory. Node n = head; Assign the head to N, and now the operation on N will also affect the head
It provides traversal mode and implements Iterable interface.
Test code:
public class test { public static void main(String[] args) { LinkList<Integer>linkList = new LinkList<>(); linkList.insert(1); linkList.insert(2); linkList.insert(4); linkList.insert(3); linkList.insert(1,2); for (Integer i : linkList) { System.out.print(i+" "); } } }
Operation results:
D:\Java\jdk-12.0.2\bin\java.exe "-javaagent:D:\IDEA\IntelliJ IDEA 2019.1\lib\idea_rt.jar=3542:D:\IDEA\IntelliJ IDEA 2019.1
1 2 1 4 3
After learning the linked list, you still need to practice. You can brush questions on LeetCode to deepen your understanding.
2. Bidirectional linked list
Head inserting method: new nodes are always inserted in the head
It is easy to understand and can be represented by drawing
Head interpolation: in the original drawing, node is the node to be inserted
Figure after insertion:
Critical code:
Tail inserting method: new nodes are always inserted at the tail
The original drawing is as follows:
After inserting nodes, the figure is as follows:
Critical code:
Insert anywhere in the middle
Original image before insertion:
Insert into the middle of the linked list:
Critical code:
Code demonstration:
class MyLinkedList { Node head;//Defines the head node of a two-way linked list Node last;//Defines the tail node of a two-way linked list //Print bidirectional linked list public void disPlay() { Node cur = this.head; while (cur != null) { System.out.print(cur.val + " "); cur = cur.next; } System.out.println(); } //Find the length of the two-way linked list (which will be used in addIndex code later) public int size() { int count = 0; Node cur = this.head; while (cur != null) { count++; cur = cur.next; } return count; } //Head insertion public void addFirst(int data) { Node node = new Node(data);//Define a node for insertion //1. Suppose the two-way linked list is empty if (this.head == null) { this.head = node; this.last = node; } else { //2. When the two-way linked list is not empty //I don't understand. Please look at the diagram above. It's very simple node.next = this.head; this.head.prev = node; this.head = node; } } //Tail insertion (similar to head insertion) public void addLast(int data) { //Define a node for insertion Node node = new Node(data); //1. Suppose the two-way linked list is empty if (this.head == null) { this.head = node; this.last = node; } else { //2. When the two-way linked list is not empty //The diagram above is very simple, please read it last.next = node; node.prev = last; last = node; } } //Insert anywhere public void addIndex(int index, int data) {//The formal parameter index is the position of the inserted element, and data is the inserted value //Define a node for insertion Node node = new Node(data); Node cur = this.head;//Define a cur to traverse the bidirectional linked list //1. Judge the legitimacy of the insertion position if (index < 0 || index > size()) { System.out.println("Illegal insertion position!!!"); return; } //2. Assuming that the insertion position is the head node, that is, the head insertion method if (index == 0) { addFirst(data);//Call header insertion code return; } //3. Assuming that the insertion position is the tail node, that is, the tail insertion method if (index == size()) { addLast(data);//Call tail interpolation code return; } //4. At any position in the middle while (index != 0) { cur = cur.next; index--; } //I don't understand. Please look at the diagram above. It's very simple //Core code node.next = cur; node.prev = cur.prev; cur.prev = node; node.prev.next = node; } } //Node structure of bidirectional linked list class Node { int val; Node prev; Node next; Node(int val) { this.val = val; } }
3. Linked list inversion
public void reverse(){ if(N==0){ //Currently, it is an empty linked list and does not need to be reversed return; } reverse(head.next); } /** * @param curr Node currently traversed * @return The previous node of the current node after inversion */ public Node reverse(Node curr){ //The last element has been reached if(curr.next==null){ //After inversion, the head node should point to the last element in the original linked list head.next=curr; return curr; } //Previous node of current node Node pre=reverse(curr.next); pre.next=curr; //The next node of the current node is set to null curr.next=null; //Return current node return curr; }