π Write in front
- π Blog home page: kikoking's Jianghu background
- π Welcome to pay attention π give the thumbs-up π Collection β Leave a message π
- π This article is original by kikokingzz and launched by CSDN!
- π Starting time: πΉ November 29, 2021 πΉ
- π Last updated: π November 29, 2021 π
- βοΈ Persistence and hard work will surely bring poetry and distance!
- π The author's level is very limited. If you find an error, please leave a message! Thank you very much!
- Introduction:
- First, thoroughly understand the logical structure & storage structure in the data structure
- Second, thoroughly understand the algorithm complexity of data structure
- Third, 408 must see the sequence table. Life is not a "sequence table"Β
catalogue
π₯ Storage structure: single linked list
π₯ Header pointer and header node
π Similarities and differences between head pointer and head node
β 2. Output single linked list (traverse single linked list) · SListPrint
β 3. Dynamically apply for a node · BuySListNode
ππ» How to distinguish between passing a secondary pointer or a primary pointer
β 5. Single chain meter insertion · SListPushFront
β 6. Single linked list lookup · slitfind
β 7. Insert (slightly troublesome) · slitinsertfront before specifying the pos position
β 8. Insert SListInsertAfter after specifying the pos position
β 10. Header deletion · SListPopBack
β 11. Delete SListEraseAfter the specified position
β 12. Delete SListEraseFront before the specified position
π Exercise 2. Removing linked list elements
π Exercise 3. Reverse linked list
π Idea 1: directly use three pointers to flip
π Idea 2: head insertion method
π: Before understanding the single chain list of this lesson, let me review the sequence list of last lesson
Defects of sequence table:
- Dynamic capacity increase with linear consumption
- With a certain degree of space waste
- Insert and delete data to be moved
π: The linked list improves these defects of the sequence list
Linked list:
- Space on demand
- Inserting and deleting data does not need to move the data, just modify the pointer
- There is no need to use storage units with continuous addresses - the logical structure and physical structure are inconsistent
π: So in what order should we understand a data structure?
50: Analyze from its logical structure -- > physical structure (storage structure) - > operations performed
π: Boy, I didn't expect that the previous experience of the sequence table made you mature so much at once! The analysis is very comprehensive!
π: Then let's analyze them one by one
π₯ Logical structure
The logical structure of a single linked list is a linear list, that is, it is a linear structure in logic, that is, a straight line
50: So what does it have to do with the storage structure?
π: As the saying goes, the definition of operation is for the logical structure, and the implementation of operation is for the storage structure
50: How profound! What operation definition and operation implementation??
π: Let's take an example to explain the logical one-to-one relationship:
·Title: arrange a string of six English letters in alphabetical order (c d e a b f)
π: The operation definition is simply: how do you plan to sort these six English letters in your mind? In general, there are only the following four methods:
Β
Β
Β
Obviously, the best choice of arrangement that meets our requirements is linear structure
β¨β¨β¨ I am the dividing line β¨β¨β¨
π₯ Storage structure: single linked list
Concept: it refers to storing data elements in a linear table through a group of arbitrary storage units. In order to establish the linear relationship between data elements, each linked list node needs to store not only the information of the element itself, but also a pointer to its successor
The node structure of single linked list is shown in the following figure:
Data field: a field that stores data element information
Pointer field: a field that stores the position of a direct successor element
β
Single linked list is to connect the data elements of linear table in logical order by relying on the pointer field of each node
Because it is very simple to use the linked list of the leading node, the more difficult one-way linked list without the leading node is selected as the operation case.
β¨β¨β¨ I am the dividing line β¨β¨β¨
π₯ Header pointer and header node
π Head pointer
The head pointer is the storage location of the first node in the linked list. The access of the whole linked list starts from the pointer. In fact, each subsequent node is the location pointed by the previous subsequent pointer; the last node points to "NULL" (usually represented by NULL or "^")
π Head node
The data field of the head node can not store any information, and the pointer field of the head node points to the pointer of the first node
π Similarities and differences between head pointer and head node
β¨β¨β¨ I am the dividing line β¨β¨β¨
π₯ Lead and no lead nodes
π No lead node
Β
π Leading node
β¨β¨β¨ I am the dividing line β¨β¨β¨
The following is a single linked list of non leading nodes!!! Because if you master the non leading nodes, you can kill the leading nodes casually
β 1. Create a node · SLN
typedef int SLDataType; typedef struct SListNode { SLDataType data;//Data field, storing data struct SListNode* next;//Pointer to the next node //The type pointed to by the pointer is struct SListNode }SLN;//Replace this structure with SLN
Suppose p is a pointer to the ith element of the linear table
node a(i){ p->data The value of is the node a(i)A data element of p->next The value of is a pointer, node a(i)The successor node of, pointing to the second node i+1 Two elements, i.e. nodes a(i+1) }
β¨β¨β¨ I am the dividing line β¨β¨β¨
β 2. Output single linked list (traverse single linked list) · SListPrint
Because the elements of a single linked list are discretely distributed in the storage space, a specific node in the table cannot be found directly. When outputting a single linked list, you need to traverse from the header and print out in turn
void SListPrint(SLN* plist)//Output single linked list {//The essence is to traverse the single linked list SLN* cur = plist;//Assign header pointer to cur while (cur)//Only cur is true will it traverse downward { printf("%d->", cur->data);//Output data field cur = cur->next;//cur points to the next node } printf("NULL\n");//The last node points to NULL }
β¨β¨β¨ I am the dividing line β¨β¨β¨
β 3. Dynamically apply for a node · BuySListNode
Because subsequent insertion operations need to create a new node, writing a function module can facilitate subsequent calls.
SLN* BuySListNode(SLDataType x)//Create a new node with data field x { SLN* node = (SLN*)malloc(sizeof(SLN));//Apply for a node space of SLN type node->data = x; node->next = NULL; return node; //The return type is a pointer to the SLN structure type //This pointer points to the new node created }
β¨β¨β¨ I am the dividing line β¨β¨β¨
ππ» How to distinguish between passing a secondary pointer or a primary pointer
1. If you want to change the value of the incoming pointer itself ----- > you need to pass in the secondary pointer
Change arguments int Value of variable————>pass int* void swap(int* x, int* y); int main() { int a = 1; int b = 2; swap(&a, &b); } Relative, change int* Value of variable————>pass int** void swap(int** x, int** y); int main() { int* a; int* b; swap(&a, &b); }
2. For headless single linked list. As long as the first node will change (that is, the value of the incoming pointer will change), the secondary pointer needs to be passed
Note: the single linked list of the leading node does not need to pass the secondary pointer, because the leading node pointed to by the incoming pointer will not change
β¨β¨β¨ I am the dividing line β¨β¨β¨
β 4. SListPushBack
Because we write the single linked list without head node, we need to consider the case of NULL pointer. When the original linked list has no data elements, the head pointer points to NULL. We need to change the value of the head pointer. Therefore, we need to use the secondary pointer to receive the head pointer address to change the value of the head pointer
void SListPushBack(SLN** pplist, SLDataType x) { SLN* newnode = BuySListNode(x);//Use the create node function and use newnode to receive if (*pplist == NULL) { *pplist = newnode;//Change the header pointer value to the new node address } else { SLN* tail = *pplist;//Assign the header pointer to tail while (tail->next != NULL) { tail = tail->next;//Traversal tail finding } tail->next = newnode;//The original tail node points to the new node } }
Commissioning:
β¨β¨β¨ I am the dividing line β¨β¨β¨
β 5. Single chain meter insertion · SListPushFront
Because the single linked list of headless nodes we write is header insertion, the value of the header pointer must be changed once for each node inserted. Therefore, the secondary pointer should be used to receive the address of the header pointer, and then change the value of the header pointer
void SListPushFront(SLN** pplist, SLDataType x) { SLN* newnode = BuySListNode(x);//Dynamically apply for a new node newnode->next = *pplist;//The new node refers to the position pointed to the head node *pplist = newnode;//Point the head node to the new node }
Commissioning:
β
β¨β¨β¨ I am the dividing line β¨β¨β¨
β 6. Single linked list lookup · slitfind
Find the position pos of the specified element in the linked list
SLN* SListFind(SLN* plist, SLDataType x)//Function returns a pointer to the location of the node to be found { SLN* cur = plist; while (cur)//Only if the linked list is not empty can the search process be carried out { if (cur->data == x) { return cur;//Returns a pointer to this node when found } cur = cur->next;//Traverse backward one by one } return NULL; }
Find debug
β¨β¨β¨ I am the dividing line β¨β¨β¨
β 7. Insert (slightly troublesome) · slitinsertfront before specifying the pos position
void SListInsertFront(SLN* pos,SLDataType x) { assert(pos);//Assert to prevent pos from being a null pointer SLN* newnode = BuySListNode(x);//Dynamically apply for a node newnode->next = pos->next;//Point the new node to the back node of pos pos->next = newnode;//Point pos to new node int tmp = pos->data;//Use temporary variables to exchange the data of pos and newnode nodes pos->data = newnode->data; newnode->data = tmp; }
debugging
β¨β¨β¨ I am the dividing line β¨β¨β¨
β 8. Insert SListInsertAfter after specifying the pos position
It can be seen that the final results of step 7 and step 8 are the same, but it is more convenient to insert after pos
void SListInsertAfter(SLN* pos, SLDataType x) { SLN* newnode = BuySListNode(x);//Dynamically apply for a node newnode->next = pos->next;//The new node points to the node that the pos points to pos->next = newnode;//pos points to the new node }
debugging
β¨β¨β¨ I am the dividing line β¨β¨β¨
β 9. SListPopBack
Three cases considered in tail deletion
1. The header pointer is null ----- > return directly
2. After there is only one node - > free, set the header pointer to null (change the value of the header pointer - > use the secondary pointer to receive)
3. There are many nodes - > Free lose the last node and set the next of the penultimate node to NULL
void SListPopBack(SLN** pplist) { if (*pplist == NULL) return;//The header pointer is null and returns directly else if ((*pplist)->next == NULL) { free(*pplist);//Put the first node free *pplist = NULL;//Set the value of the header pointer to null } else { SLN* tail = *pplist; SLN* prev = NULL; while (tail->next != NULL) { prev = tail; tail = tail->next; } free(tail);//free tailed node tail = NULL;//Set the value of the tail pointer to null prev->next = NULL;//Set the next of the penultimate node to NULL } }
Commissioning:
β¨β¨β¨ I am the dividing line β¨β¨β¨
β 10. Header deletion · SListPopBack
As can be seen from the figure below, only two situations need to be considered for header deletion:
1. The header pointer is null ----- > return directly
2. Change the value of the header pointer to the next of the first node (that is, change the value of the header pointer, and use the secondary pointer to receive the header pointer address)
void SListPopFront(SLN** pplist) { if (*pplist == NULL) return; else { SLN* cur = (*pplist)->next;//Declare a temporary variable pointing to the successor of the first node free(*pplist);//free drop the first node *pplist = cur;//The header pointer points to the node pointed to by the temporary variable } }
Commissioning:
β¨β¨β¨ I am the dividing line β¨β¨β¨
π Topic 1
Insert a value x in front of a node in a headless single linked list. How to insert it (don't tell you the header pointer!)
Solution: insert backward and exchange the two values
β¨β¨β¨ I am the dividing line β¨β¨β¨
β 11. Delete SListEraseAfter the specified position
void SListEraseAfter(SLN* pos) { assert(pos);//Assert that if pos is empty, it will not be executed downward if (pos->next == NULL) { return;//pos is the last node, delete air } else { SLN* next = pos->next;//Give the address of the node after pos to next pos->next = next->next; free(next); //free drops the node pointed to by next next = NULL;//It is a good habit to leave next blank } }
debugging
β¨β¨β¨ I am the dividing line β¨β¨β¨
β 12. Delete SListEraseFront before the specified position
void SListEraseFront(SLN** pplist,SLN* pos) { assert(pos); assert(*pplist); if (*pplist == pos) return;//pos is in the first node and cannot be deleted before SLN* cur = *pplist; SLN* prev = NULL; if (cur->next == pos)//The deleted position is the first node, and the value of the header pointer needs to be changed { *pplist = pos; free(cur); cur = NULL; } else//In other cases, double pointers are used for connection and free { while (cur->next != pos) { prev = cur; cur = cur->next; } prev->next = pos; free(cur); cur = NULL; } }
debugging
β¨β¨β¨ I am the dividing line β¨β¨β¨
π Exercise 2. Removing linked list elements
203. Remove linked list elements
β
Train of thought analysis:
π Conventional method
struct ListNode* removeElements(struct ListNode* head, int val){ //Why not use secondary pointers? Didn't the value of the header pointer here change? //A return value struct ListNode * is used, so the value of the head node changed inside the function can also be obtained outside the function struct ListNode* cur = head; struct ListNode* prev =NULL ; while(cur) { if(cur->val==val) { struct ListNode* next=cur->next; if(prev==NULL)//cur is the head { free(cur); head=next; cur=next; } else { prev->next=next; free(cur); cur=next; } } else { prev=cur;//When prev is not NULL, the first element must not be val cur=cur->next; } } return head; }
Note: why not use secondary pointers? Isn't the value of the head node here changed?
A return value struct ListNode * is used, so the value of the head node changed inside the function can also be obtained outside the function
π Sentinel method
If we add a sentinel position, we don't need to discuss it according to whether the first element is val
(whether the first element is val) ------- > the essence is the null pointer problem of prev
struct ListNode* reverseList(struct ListNode* head){ struct ListNode* cur=head , *newHead=NULL; while(cur) { struct ListNode* next=cur->next; cur->next=newHead; newHead=cur; cur=next; } return newHead; }
β¨β¨β¨ I am the dividing line β¨β¨β¨
π Exercise 3. Reverse linked list
β
π Idea 1: directly use three pointers to flip
1->2->3->4->NULL Through 3 pointers n1\n2\n3 Flip n1 n2 n3 NULL 1->2->3->4->NULL n1 n2 n3 NULL<-1 2->3->4->NULL n1 n2 n3 NULL<-1<-2 3->4->NULL n1 n2 n3 NULL<-1<-2<-3 4->NULL n1 n2 n3 NULL<-1<-2<-3<-4 NULL
struct ListNode* reverseList(struct ListNode* head){ if(head==NULL||head->next==NULL)//Empty table, or when there is only one node return head; struct ListNode* n1=NULL, *n2=head, *n3=head->next; while(n2) { n2->next=n1;//Flip n1=n2;//iteration n2=n3; if(n3==NULL)//Prevent null pointer break; n3=n3->next; } return n1; }
β
π Idea 2: head insertion method
Note that the header does not create a new node here
cur next 1 ->2 ->3 ->4->NULL newHead==NULL Each time, take the node in the original linked list and insert it into the new node Two pointers are required, one for header insertion and one for identifying the next node cur->newHead 1->NULL cur=newhead -----step2----- cur next 1->2 ->3->4->NULL cur->newHead->NULL 2->1->NULL cur=newHead
struct ListNode* reverseList(struct ListNode* head){ struct ListNode* cur=head , *newHead=NULL; while(cur) { struct ListNode* next=cur->next; cur->next=newHead; newHead=cur; cur=next; } return newHead; }