Linked list structure
There are many kinds of linked list structures. Today, I will focus on three most common linked list structures: single linked list, two-way linked list and circular linked list. Let's first look at the simplest and most commonly used single linked list.
Single linked list
We habitually call the first node the head node and the last node the tail node. The header node is used to record the base address of the linked list. With it, we can traverse the whole linked list. The tail node is special: the pointer does not point to the next node, but to an empty address
NULL indicates that this is the last node on the linked list.
For the insertion and deletion of linked list, we only need to consider the pointer change of adjacent nodes, so the corresponding time complexity is O(1). The performance of random access to linked lists is not as good as that of arrays, which requires O(n) time complexity.
Circular linked list
Circular linked list is a special single linked list. In fact, the circular linked list is also very simple. The only difference between it and a single linked list is the tail node. We know that the tail node pointer of the single linked list points to the null address, indicating that this is the last node. The tail node pointer of the circular linked list points to the head node of the linked list. From the circular list diagram I drew, you should see that it is connected end to end like a ring, so it is called "Circular" list.
It can be seen from the diagram I drew that the two-way linked list needs two additional spaces to store the addresses of subsequent nodes and precursor nodes. Therefore, if the same amount of data is stored, the two-way linked list occupies more memory space than the single linked list. Although two pointers waste storage space, they can support two-way traversal, which also brings the flexibility of two-way linked list operation. Compared with single linked list, what kind of problem is bidirectional linked list suitable for? Structurally, two-way linked lists can support
Finding the precursor node in the case of O(1) time complexity is such a feature, which also makes the insertion and deletion of the two-way linked list simpler and more efficient than the single linked list in some cases.
The head node is the first node, undefined, and the tail node points to an empty address
The node with Sentry is conducive to simplifying the code, which is recommended
Bidirectional linked list
Circular linked list is a special single linked list. In fact, the circular linked list is also very simple. The only difference between it and a single linked list is the tail node. We know that the tail node pointer of the single linked list points to the null address, indicating that this is the last node. The tail node pointer of the circular linked list points to the head node of the linked list. From the circular list diagram I drew, you should see that it is connected end to end like a ring, so it is called "Circular" list.
Bidirectional circular linked list
Understand the circular linked list and two-way linked list. If the two linked lists are integrated together, it is a new version: two-way circular linked list.
Example of bidirectional linked list [to be added]
Must practice operation
Interface definition
package com.s1.array;
public interface IList {
void print();
// Add to tail
void add(Integer data);
void addFirst(Integer data);
void addLast(Integer data);
// Add an element at the specified location (within the valid range)
void add(int position, Integer data);
// Delete to tail
Object remove();
// Delete first element
Object removeFirst();
// Delete tail element
Object removeLast();
// Deletes the element with the specified subscript
Object remove(int index);
// Deletes the element of the specified data
Object remove(Object obj);
// Change 1: find the subscript and change it
boolean updateByPosition(int position, Integer value);
// Change 2: find the original data and change it
boolean updateByData(Integer oldData, Integer newData);
void clear();
}
- Realize single linked list [optional circular linked list and two-way linked list], and support addition and deletion operations
- Single linked list reverse chain
- Detection of ring in table
- Merge two ordered linked lists
- Delete the penultimate node of the linked list
- Find the middle node of the linked list
Thinking problem: LRU algorithm based on linked list
LRU idea I
- If this data has been cached in the linked list before, we traverse the node corresponding to this data, delete it from its original position, and then insert it into the head of the linked list.
- If this data is not in the cache linked list, it can be divided into two cases: undefined. If the cache is not full at this time, insert this node directly into the head of the linked list; Undefined if the cache is full at this time, the tail node of the linked list will be deleted and the new data node will be inserted into the head of the linked list.
Or idea two
/**
* If it doesn't exist * Insert if queue is not full tail * Queue full remove head And insert from tail * If present, remove from and insert from tail */
package com.s2.link;
public class LRULinkedList extends LinkedList {
private static final int DEFAULT_LENGTH = 10;
private final int length;
private int used = 0;
public LRULinkedList() {
this(DEFAULT_LENGTH);
}
public LRULinkedList(int length) {
this.length = length;
}
protected boolean isFull () {
return this.used == this.length;
}
@Override
public void add(Integer data) {
/**
* If it doesn't exist * Insert if queue is not full tail * Queue full remove head And insert from tail * If present, remove from and insert from tail */ Object removeNode = this.remove(data); if (removeNode == null && this.isFull()) { this.removeFirst(); } this.addLast(data); }
@Override
public Object remove(Object obj) {
Object removeObject = super.remove(obj);
if (removeObject != null) {
this.used--;
}
return removeObject;
}
@Override
public Object removeFirst() {
Object removeObject = super.removeFirst();
if (removeObject != null) {
this.used--;
}
return removeObject;
}
@Override
public void addLast(Integer data) {
super.addLast(data);
used++;
}
}
Thinking question: judge whether it is palindrome string
Find the intermediate node. According to the parity number.
If it is an odd number, separate them.
If it is an even number, it is considered that there are two midpoint and continue to separate.
Then get the head pointers at both ends and cycle. If the data of the node is inconsistent, it is determined that it is not a palindrome string. If the loop ends, the string is considered a palindrome string.
code snippet
//Determine whether it is palindrome
public boolean palindrome() {
// Find the intermediate node according to the speed pointer, but do not know whether the number of summary points is odd or even
if (this.headNode == null) {
return false;
}
if (this.headNode.next == null) {
return true;
}
// Greater than two nodes
Node slow = this.headNode;
Node fast = this.headNode;
while (fast.next != null && fast.next.next != null) {
slow = slow.next;
fast = fast.next.next;
}
// slow fast
// 1 2
// 1 2 3
System.out.println("slow " + slow);
System.out.println("fast " + fast);
Node leftNode;
Node rightNode;
// Total odd number, one key point
if (fast.next == null) {
rightNode = slow.next;
this.inverseLinkList(slow);
leftNode = slow.next;
}
// Total even number, two midpoint
else {
rightNode = slow.next;
this.inverseLinkList(slow);
leftNode = slow;
}
return this.TFResult(leftNode, rightNode);
}
My code
https://gitee.com/kaiLee/struct/tree/master/src/main/java/com/s2