# Data structure - linked list

Posted by booga1138 on Fri, 26 Nov 2021 10:42:56 +0100

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.

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 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

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.

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.

## 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();   `
`}`
• 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

1. 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.
2. 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();
}
}
```
`    @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