Algorithm learning 1-1 linked list learning pen

Posted by alluoshi on Thu, 16 Dec 2021 15:11:33 +0100

Linked list

Definition of linked list

n nodes are linked into a linked list, which is the linked storage structure of linear list. Because each node of this linked list contains only one pointer field, it is called single linked list.

1. Characteristics of linked list

Linked list is not suitable for fast positioning data, and is suitable for dynamic insertion and deletion of data

a. Query time complexity O(n)

All nodes before this node need to be traversed, so the time complexity is o (n)

b. Insert and delete time complexity O(1)

When you need to insert or delete a node, you only need to modify the pointing relationship before and after the node, so the time complexity is o (1)

2. Application scenario of linked list

Scenario 1: dynamic memory allocation of the operating system

Assuming that the original memory area is 10GB, 2GB of memory is allocated by using malloc function (a function for dynamically allocating memory, which needs to be manually released), and the remaining 8GB of memory needs to be maintained by the operating system.

One way for the operating system to maintain memory (there is a linked list based memory management scheme in openVPN) is to thread different remaining memory fragments into a linked list for maintenance.

Scenario 2: LRU cache elimination algorithm

LRU (Least Recently Used)
give an example:
There are speed differences between devices. More efficient call can be realized by storing data with high frequency on high-speed devices.
The implementation method is to delete (or transfer) the least used when the capacity reaches the upper limit, leaving space for new data

Implementation of linked list

1. Array

#include <iostream>
using namespace std;
i
nt data[100];
int nxt[100];

//Insert node p after ind node, and the value of p is val
void add(int ind, int p, int val){
    nxt[ind] = p;
    data[p] = val;
    return;
}

int main(void)
{
    int head = 3;
    data[3] = 0;
    add(3, 5, 1); 
    add(5, 2, 2); 
    add(2, 7, 3); 
    add(7, 9, 4); 
    int p = head;
    while(p != 0){
        cout << data[p] << " ";
        p = nxt[p];
    }
    cout << endl;
    return 0;
}

2. Pointer

#include <iostream>
using namespace std;

struct node{
node(int data)
    :_data(data)
     ,_next(NULL)
    {}  

    int _data;
    node *_next;
};

int main(void)
{
    node *head = NULL;
    head = new node(1);
    head->_next = new node(2);
    head->_next->_next = new node(3);
    head->_next->_next->_next = new node(4);
        
    node *p = head;
    while(p != NULL){
        cout << p->_data;
        p = p->_next;
    }   
    cout << endl;
    return 0;
}

3 Comparison of linked list and array

In memory: the linked list is easy to expand, and the array hits the cpu cache optimization (because it is continuous)
Operation: the linked list is easy to insert and delete

Classic problems of linked lists

Sentinel node: create sentinel node only when the head node may change!!!

1. Access to linked list

It is required to flip the element order of the linked list by modifying the pointing relationship between each cell of the linked list and the next cell without modifying the stored value.

141. Circular linked list

Idea: speed pointer
A. When the next node of the fast pointer is NULL, or the node of the fast pointer itself is NULL, it indicates that the linked list has no ring and the traversal ends
B. If there is a ring, then the fast and slow pointers will meet. When the pointer points to the same node, the traversal ends


code:

class Solution {
public:
    bool hasCycle(ListNode *head) {
        if(head == NULL) return false;
        ListNode*p = head,*q = head->next;

        while(p != q && q && q->next){
            p = p->next;
            q = q->next->next;
        }
        return p == q;
        
    }
};

142. Ring linked list II

Idea: speed pointer

code:

class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        if(head == NULL) return NULL;
        ListNode *p =head, *q = head;
        while(q && q->next){
            p = p->next;
            q = q->next->next;
            while(p == q){
                q = head;
                while(p != q){
                    p = p->next;
                    q = q->next;
                }
                return p;
            }

        }
        return NULL;
    }
};

202. Happy number

Idea: find the ring with fast and slow pointer
A if the traversal of a node is 1, it means there is no ring, which is the happy number
B. If you traverse to duplicate nodes, it indicates that there are rings, which is not a happy number

code

class Solution {
public:

    int getNext(int x){
        int z = 0;
        while(x){
            z += (x%10)*(x%10);
            x/=10;
        }
        return z;
    }

    bool isHappy(int n) {
        int p = n,q = n;
        do{
            p = getNext(p);
            q = getNext(getNext(q));

        }while(p != q && p != 1);

        return q == 1;

    }
};

2. Reversal of linked list

206. Reverse linked list

Idea 1: define pre to point to null (the inverted head node), cur to the non inverted head node, and next to the next node of cur
First, let cur point to pre,
Then move pre to the position of cur and cur to the position of next
At this point, we have reversed the first node,
Point the next pointer to the next node
At this time, it returns to the initial state (the difference is that the first node is reversed)
Repeat the above steps


code

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if(!head||!head->next)  return head;
        ListNode *pre = nullptr, *cur = head, *p = head->next;
        while(cur){
            cur->next = pre;
            pre = cur;
            (cur = p) &&(p = p->next);
        }
        return pre;
    }
};

Idea 2: recursion (the idea of recursion is global, only considering the current two nodes, not the following nodes)
Define tail as the next node of head, and then put the next node of head into the function
Then reverse the head and tail nodes

code

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if(!head||!head->next)  return head;
        ListNode * tail = head->next, *p = reverseList(head->next);
        head->next = tail->next;
        tail->next = head;
        return p;
    }
};

92. Reverse linked list II

Add sentinel node: the returned head node may change, so create sentinel node!!!
Idea: sentinel node + recursion
A. First find the node to be flipped (for example, to reverse 1234, first traverse – left and find 1 times)
B. Reverse node (reverse according to the recursion of the previous topic)

code

class Solution {
    ListNode *reverseN(ListNode *head, int n){
        if(n == 1) return head;
        if(!head || !head->next) return head;
        ListNode *tail = head->next, *p = reverseN(head->next, n -1);
        head->next = tail->next;
        tail->next = head;
        return p;
    }
public:
    ListNode* reverseBetween(ListNode* head, int left, int right) {
        ListNode ret(0, head), *p = &ret;
        int cnt = right-left + 1;
        while(--left) p = p->next;
        p->next = reverseN(p->next, cnt);
        return ret.next;
    }
};

25. Turn over the linked list in groups of K

Idea: sentry + recursion
The thought is similar to the above two questions, but the transmission parameters are different

class Solution {
public:
    ListNode * _reverseK(ListNode *head, int cnt)
    {
        if(cnt == 1) return head;
        ListNode * tail = head->next, *p = _reverseK(head->next, cnt-1);
        head->next = tail->next;
        tail->next =head;
        return p;
    }

    ListNode *reverseK(ListNode *head, int cnt)
    {
        ListNode *p = head;
        int k = cnt;
        while(--k && p) p = p->next;
        if(!p) return head;
        return _reverseK(head,cnt); 
    }

    ListNode* reverseKGroup(ListNode* head, int k) {
        ListNode ret(0, head), *p =&ret, *q = p->next;
        while((p->next = reverseK(q, k))!= q)
        {
            p = q;
            q = q->next;
        }
        return ret.next;
    }
};

61. Rotating linked list

There is a pit!!! Move k positions, k may be greater than the linked list, remember to take the remainder!!

Idea: traverse nodes
Find the tail node first, then tail - > next = head;
Then find the penultimate node by k%=n
Then use k = n-k to find the number of nodes in time sequence
Then record the found node - > next as the header node and set it to null

code

class Solution {
public:
    ListNode* rotateRight(ListNode* head, int k) {
        if(head == nullptr) return head;
        ListNode *p = head;
        int n = 1;
        while(p->next){
            p = p->next;
            n++;
        }
        p->next = head;
        k %= n, k = n - k;
        while(k--) p = p->next;
        head = p->next;
        p->next = nullptr;
        return head;
    }
};

3. Node deletion of linked list

19. Delete the penultimate node of the linked list

Thought: speed pointer
Let the fast pointer take n+1 steps first
Then the fast and slow pointers go at the same time (the slow pointer goes from the beginning)
When the fast pointer points to a null node, the position of the slow pointer is the previous node of the deleted node
Then set the slow pointer - > next = - > next - > next

code

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode ret(0, head), *p = &ret ,*q = head;
        while(n--) q = q->next;
        while(q){
            q = q->next;
            p = p->next;
        }
        p->next = p->next->next;
        return ret.next;
    }
};

83. Delete duplicate elements in the sorting linked list

Idea: the definition points p to head,
When p is the same as the next node array, the next node of P points to another node
When p is different from the next node array, P is equal to the next node
code

class Solution {
public:
    ListNode* deleteDuplicates(ListNode* head) {
        if(!head) return head;
        ListNode *p = head;
        while(p->next){
            if(p->val == p->next->val){
                p->next = p->next->next;
            }else{
                p = p->next;
            }
        }
        return head;
    }
};

82. Delete duplicate Element II in the sorting linked list

Thought: sentry node p
Record the p - > next node and compare it with P - > next – > next

  • When A is the same, record as q
    Then look for the same node as p - > next after the q point, and know that q points to a node different from the value of P - > next
    Then p - > next = q

  • If B is different, P = P - > next

code

class Solution {
public:
    ListNode* deleteDuplicates(ListNode* head) {
        ListNode ret(0, head), *p = &ret ,*q;
        while(p->next){
            if(p->next->next && p->next->val ==p->next->next->val){
                q = p->next->next;
                while(q && q->val == p->next->val) q = q->next;
                p->next = q;
            }else{
                p = p->next;
            }
        }
        return ret.next;
    }
};

Topics: Algorithm data structure leetcode linked list