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