2022 winter vacation LeetCode question brushing - task 01: linked list
Introduction: the author is basically Xiaobai, so this topic brushing meeting is a difficult process. At the same time, it is also through this team learning to pick up the previous knowledge, and more importantly, it is to expand and consolidate. Therefore, this blog may be long and have a review of basic knowledge, which is also a record I completed step by step in the process of doing the question. (some didn't work out the answer)
1, Array Basics
(Title: 0189) rotate array
Title: give you an array, rotate the elements in the array to the right K positions, where k is a non negative number.
The first thing to be clear about:
The difference between vector and array
The standard template library (STL) of C + + is a collection of 13 containers, and vector is one of them. Dynamic array vector is an array whose capacity changes dynamically. When defining a vector object, you don't need to specify the size of the array first. If there is data, you can insert it into the array. Vector manages its own storage space.
Header file and namespace of vector:
#Include < vector > / / Note: No h using namespace std; //In std standard namespace
Definition of vector
vector can adapt to any type. It is a class template.
vector<int> arr_int; //Defines the dynamic array of int when an internal element is defined; vector<char> arr_char; //A dynamic array whose internal element is char is defined; vector<CStudent> arr_student; //Defines a dynamic array whose internal element is CStudent; vector<char*> arr_pchar; //Defines a dynamic array whose internal element is char *
Supplement: differences between public, private and protected
- public means common: the data members and functions of a class can be accessed by the class objects and derived classes
- Private private type: its own class can be accessed, but derived classes cannot.
- protected: self classes and derived classes can access private members equivalent to themselves. The difference between it and private is the difference between derived classes.
2, Linked list
Linked List: a linear list data structure. It uses a set of arbitrary storage units (which can be continuous or discontinuous) to store a set of data with the same type.
Header pointer: pointer to the first node in the linked list
First element node: refers to the node where the first data element is stored in the linked list
Head node: a node attached before the first node of the linked list
The difference between linked list and array: array is continuous in logic and physical storage; The linked list is logically continuous, but not necessarily continuous in physical storage. Therefore, a node of the linked list has a data field and a pointer field to store the address of the next node.
Traversal mode: array is random access and linked list is sequential access.
First, supplement the basic operations of the single linked list:
- Definition and representation
- Destroy
- Find the length of single linked list
- Value
- Find (find by value)
- Insert: insert a new node before the ith node
- Delete: deletes the i th node
- Establishment of single linked list (head interpolation and tail interpolation)
Algorithm - definition and representation of single linked list:
typedef struct Lnode{ //Declare the type of the node and the pointer type to the node ElemType data; //Data field of node struct Lnode *next; //Pointer field of node }Lnode,*LinkList; //LinkList is the pointer type to the structure Lnode //Initialization of single linked list /* (1)Generate a new node as the head node, and point to the head node with the head pointer L (2)Set the pointer field of the header node to null */ Status InitList_L(LinkList &L){ L = new LNode; //Or L=(LinkList) malloc(sizeof(LNode)); L->next=NULL; return OK; }
Algorithm - destruction of single linked list: the linked list does not exist after destruction
Starting from the pointer, release all nodes in turn
Status DestroyList_L(LinkList &L){ //Destroy single linked list L Lnode *p; //Or LinkList p; while(L){ p = L; L = L->next; delete p; } return OK; }
Algorithm - single linked list emptying:
The linked list still exists, but there are no elements in the linked list. It becomes an empty linked list (the head pointer and head node still exist). (release all nodes in turn and set the pointer field of the header node to null)
Status ClearList(LinkList &L){ //Reset L to empty table Lnode *p,*q; //Or LinkList p,q; p=L->next; while(p){ //It's not at the end of the watch q=p->next; delete p; p=q; } L->next=NULL; //The header node pointer field is null return OK; }
Algorithm - find the table length of single linked list:
int ListLenght_L(LinkList L){ //Returns the number of data elements in L LinkList p; p=L->next; //p points to the first node i=0; while(p){ //Traverse the single linked list and count the summary points i++; p=p->next; } return i; }
Algorithm - value of single linked list:
Starting from the head pointer of the linked list, search down the chain domain next node by node until the ith node is searched.
Status GetElem_L(LinkList,int i,ElemType &e){ //Get the content of a data element in the linear table and return it through the variable e p=L->next;j=1; //initialization while(p&&j<i){ //Scan backward until p points to the ith element or p is empty p=p->next;++j; } if(!p||j>i) return ERROR; //The ith element does not exist e=p->data; //Take the i th element return OK; }//GetElem_L
Algorithm - single linked list lookup:
//Find by value - get the location (address) of the data according to the specified data Lnode *LocateELem_L(LinkList L, Elemtype e){ //Find the data element with the value e in the linear table L //If it is found, the data element with the value e in L is returned. If the search fails, NULL is returned p=L->next; while(p&&p->data!=e) p=p->next; return p; } //Find by value - obtain the serial number of the data location according to the specified data int LocateElem_L(LinkList L, Elemtype e){ //Returns the position sequence number of the data element with e in L. if the search fails, it returns 0 p=L->next;j=1; while(p&&p->data!=e) {p=p->next; j++;} if(p) return j; else return 0; }
Algorithm - single linked list insertion: insert a new node with the value of e before the ith node
//Insert the data element e before the ith element in L Status ListInsert_L(LinkList &L,int i,ElemType e){ p=L;j=0; while(p&&j<i-1) {p=p->next; ++j;} if(!p||j>i-1) return ERROR; s=new LNode; s->data=e; //Generate a new node s and set the data field of node s to e s->next=p->next; //Insert node s into L p->next=s; return OK; }
Algorithm -- deletion of single linked list: delete the ith node
//Delete the ith data element in linear table L Status ListDelete_L(LinkList &L,int i,ElemType &e){ p=L;j=0; while(p->next&&j<i-1) {p=p->next; ++j;} //Find the ith node and point p to its precursor if(!(p->next)||j>i-1) return ERROR; q=p->next; //Temporarily save the address of the deleted node for release p->next=q->next; //Change the pointer field of the precursor node of the deleted node e=q->data; //Save delete node data field delete q; //Free up space for deleting nodes return OK; }
(Title No.: 0707) the title is omitted.
0707 problem solution: here refer to a code written by a teammate:
#include<iostream> using namespace std; class MyLinkedList { public: MyLinkedList() { //An empty table that contains only header nodes head = new LNode(0,NULL); } int get(int index) { LNode* p = head->next; //Initialization, p points to the first element node int k = 0; //For counting while (p && k < index) { //p points to the ith element node or p is empty to exit the loop p = p->next; k++; } //The condition for loop exit is k=i or reaching the end of the single linked list if (!p || k > index) return -1; //The index element does not exist int e = p->val; return e; } void addAtHead(int val) { LNode* p=new LNode(val,head->next); //New node head->next = p; } void addAtTail(int val) { LNode* p = head; //Positioning with p LNode* temp = new LNode(val, NULL); //To insert while (p->next) { //p points to the last node p = p->next; } p->next = temp; } void addAtIndex(int index, int val) { LNode* p = head; //p refers to the head node int k = -1; // k is the counter while (p && k < index - 1) { //Until p points to the i-1 or p is empty p = p->next; k++; } if (!p || k > index - 1) return; //Cannot insert LNode* s = new LNode(val, p->next); //New node p->next = s; } void deleteAtIndex(int index) { LNode* p = head; //p refers to the head node int k = -1; //k is the counter while (p->next && k < index - 1) { //Find the precursor of the deleted node p = p->next; k++; } if (!(p->next) || k > index - 1) return; LNode* q = p->next; //Make q point to the node to be deleted p->next = q->next; } void print() { LNode* p = head->next; while (p) { cout << p->val << " "; p = p->next; } cout << endl; } private: struct LNode { int val; struct LNode* next; LNode(int x,LNode* node) { val = x; next = node; } }; LNode* head; }; //Test process int main() { MyLinkedList* l = new MyLinkedList(); l->print(); l->addAtHead(1); l->print(); l->addAtTail(3); l->print(); l->addAtIndex(1, 2); l->print(); l->get(1); l->print(); l->deleteAtIndex(1); l->print(); }
(0206) reverse linked list
Title: give you the head node of the single linked list. Please reverse the linked list and return the reversed linked list.
Solution idea: turn the linked list in place. Set two pointers, curr and preNode, to reverse the single linked list. Except for the head node, the directions of other nodes are reversed. Use curr to point to the current node and save the address of the next node pointed by the node. Next, the node points to the previous node again. Here, use preNode to point to the previous node of the node, That is, the subsequent node of the node pointed to by curr is the node pointed to by preNode. Assign curr to preNode and next to curr, that is, move backward in sequence and repeat the above reverse operation until curr points to NULL, and the preNode points to the last node of the original linked list, and preNode is also the first node of the result linked list.
0206 problem solution:
class Solution { public: ListNode* reverseList(ListNode* head) { ListNode* preNode = NULL; ListNode* curr = head; while(curr != NULL){ ListNode* next = curr->next; curr->next = preNode; preNode = curr; curr = next; } return preNode; } };
(0148) sorting linked list
The following algorithm is bubble sorting. The test results are correct, but the force buckle submission results timeout. The reason is that the input linked list tested at the time of submission is too long, and the time complexity of bubble sorting is O(n^2), which can not meet the requirements.
/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode() : val(0), next(nullptr) {} * ListNode(int x) : val(x), next(nullptr) {} * ListNode(int x, ListNode *next) : val(x), next(next) {} * }; */ class Solution { public: ListNode* sortList(ListNode* head) { ListNode* node_i = head; ListNode* node_j = head; ListNode* tail = NULL; int value = 0; while(node_i){ node_j = head; while(node_j && node_j->next != tail){ if(node_j->val > node_j->next->val) {value = node_j->next->val; node_j->next->val = node_j->val; node_j->val = value;} node_j = node_j->next; } tail = node_j; node_i = node_i->next; } return head; } };
Reselect a sorting method:
The time complexity of linked list selection and sorting is O(n^2);
The time complexity of linked list insertion sorting is O(n^2);
The time complexity of linked list merging and sorting is O(nlogn);
The time complexity of linked list counting and sorting is O(n^2);
The time complexity of linked list selection and sorting is O(n^2);
Here, only the linked list merging and sorting method is selected for the test:
//Linked list merge sort /** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode() : val(0), next(nullptr) {} * ListNode(int x) : val(x), next(nullptr) {} * ListNode(int x, ListNode *next) : val(x), next(next) {} * }; */ class Solution { public: ListNode* sortList(ListNode* head) { if(!head || !head->next) return head; ListNode* slow = head; ListNode* fast = head->next; while(fast && fast->next){ fast = fast->next->next; slow = slow->next; } ListNode* mid = slow->next; slow->next = nullptr; return merge(sortList(head), sortList(mid)); } private: ListNode* merge(ListNode* l1, ListNode* l2){ ListNode dummy(0); ListNode* tail = &dummy; while(l1 && l2){ if(l1->val > l2->val) swap(l1, l2); tail->next = l1; l1 = l1->next; tail = tail->next; } if(l1) tail->next = l1; if(l2) tail->next = l2; return dummy.next; } };
The above merging algorithm is submitted for approval.
(0147) Title: insert and sort the linked list
/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode() : val(0), next(nullptr) {} * ListNode(int x) : val(x), next(nullptr) {} * ListNode(int x, ListNode *next) : val(x), next(next) {} * }; */ class Solution { public: ListNode* insertionSortList(ListNode* head) { if(!head && !head->next){ //If the linked list is empty, you can return it directly return head; } ListNode* dummyhead = new ListNode(0); /*This is to apply to the case of cur - > Val < prev - > val, If dummyhead = head directly, the initial prev = dummyhead, Prev - > next = cur cannot be directly used to indicate the above situation */ dummyhead->next = head; ListNode* sortedlist = head; ListNode* cur = head->next; while(cur){ if(sortedlist->val <= cur->val){ sortedlist = sortedlist->next; } else{ ListNode *prev = dummyhead; while(prev->next->val <= cur->val) prev = prev->next; sortedlist->next = cur->next; cur->next = prev->next; prev->next = cur; } cur = sortedlist->next; } return dummyhead->next; } };
(0141) circular linked list:
The title is omitted.
Solution idea: the fast and slow pointers are placed on the head pointer respectively. The pace of the slow pointer is 1 and the pace of the fast pointer is 2. If there are rings in the linked list, the fast pointer will catch up with the slow pointer, and return true. If there are no rings in the linked list (including empty linked list), return false.
0141 problem solving
/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ class Solution { public: bool hasCycle(ListNode *head) { if(head==NULL&&head->next==NULL){ return false; } ListNode* slow = head; ListNode* fast = head->next; while(slow!=fast){ if(fast==NULL&&fast->next==NULL){ return false; } slow = slow->next; fast = fast->next->next; } return true; } };
Application scope of linked list with inconsistent starting points:
Find the penultimate node in the linked list or delete the penultimate node in the linked list.
Application scope of linked list with inconsistent steps:
Find the middle node of the linked list, judge and detect whether there are links in the linked list, and find the intersection of the two linked lists.
Examples for the above two conclusions: 0019 and 0876 (omitted).
(0019) delete the penultimate node in the linked list
Title: give you a linked list, delete the penultimate node of the linked list, and return the head node of the linked list.
Solution idea: use speed pointers with different starting points. Place the fast pointer at the n nodes of the linked list first than the slow pointer, and then move the fast and slow pointers back at the same time until the fast pointer traverses to the end of the linked list (fast=NULL). At this time, the slow pointer just traverses to the penultimate node.
0019 problem solving
/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode() : val(0), next(nullptr) {} * ListNode(int x) : val(x), next(nullptr) {} * ListNode(int x, ListNode *next) : val(x), next(next) {} * }; */ class Solution { public: ListNode* removeNthFromEnd(ListNode* head, int n) { ListNode* fast = head; ListNode* newhead = new ListNode(0, head); ListNode* slow = newhead; while(n){ fast = fast->next; n -= 1; } while(fast){ fast = fast->next; slow = slow->next; } slow->next = slow->next->next; return newhead->next; } };
(0160) intersecting linked list
Title: give you the head nodes headA and headB of two single linked lists. Please find and return the starting node where the two single linked lists intersect. If the two linked lists do not have intersecting nodes, null is returned.
I didn't think of the correct way to solve this problem. After reading the answer, I felt that the way of thinking was very clever. Here I record the way of thinking. Here I write it in my own words below. Below is the official answer code.
Solution idea: use the fast and slow pointer method. Suppose the length of a linked list is m and the length of b linked list is n. The two linked lists are not necessarily the same length. Assuming that the two linked lists intersect and have the same length, the two pointers will reach the intersection node at the same time and directly return the intersection address. If the intersection is unequal in length, one linked list will transfer to the head node of the other linked list after traversing its own node. When the two linked lists traverse the length (the node of its own non intersecting part + the node of the non intersecting part of the other linked list + the node of the intersecting part), they just meet at the intersecting node. At this time, the address of the intersection will be returned. If the two linked lists do not intersect, when traversing its own linked list and another linked list, the two pointers just point to null and return null (if the linked list is of the same length, after traversing its own node, the two pointers will become null at the same time).
0160 problem solution
/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ class Solution { public: ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) { ListNode* pA = headA; ListNode* pB = headB; if(pA==nullptr||pB==nullptr){ return NULL; } while(pA != pB){ pA = pA==nullptr? headB:pA->next; pB = pB==nullptr? headA:pB->next; } return pA; } };