1. Sequence list and linked list
- Linear table
- Sequence table
- Linked list
- The difference and relation between sequential list and linked list
1. Static sequence table
#pragma once #define N 1000 typedef double SLDataType //Static sequence table typedef struct SeqList{ SLDataType a[N]; int size; //Indicates how many functions are stored in the array }SL; ///Interface function - naming style follows STL void SeqInit(SL* ps); //Static feature: if it is full, it will not be inserted. Disadvantage: how much is appropriate? It's hard to be sure //N is too small to use, n is too big to waste void SeqPushBack(SL* ps,SLDataType x); void SeqPopBack(SL* ps); void SeqPushFront(SL* ps,SLDataType x); void SeqPopFront(SL* ps); void SeqPrintf(SL* ps); //
2. Dynamic sequence table
#pragma once typedef int SLDataType //Dynamic sequence table typedef struct SeqList{ SLDataType *a; int size; //Indicates how many functions are stored in the array int capacity; //How much space can data actually be stored }SL; ///Interface function - naming style follows STL void SeqListInit(SL* ps); //Static feature: if it is full, it will not be inserted. Disadvantage: how much is appropriate? It's hard to be sure //N is too small to use, n is too big to waste void SeqListPushBack(SL* ps,SLDataType x); void SeqListPopBack(SL* ps); void SeqListPushFront(SL* ps,SLDataType x); void SeqListPopFront(SL* ps); //
2.1 initialization
- When a function passes a parameter, the formal parameter is a copy of the argument, and the change of the formal parameter will not affect the argument
#include "SeqList.h" void SeqListInit(SL ps){ ps.a=NULL; ps.size=ps.capacity=0; } void SeqListPushBack(SL* ps,SLDataType x); void SeqListPopBack(SL* ps); void SeqListPushFront(SL* ps,SLDataType x); void SeqListPopFront(SL* ps);
#include "SeqList.h" void SeqListInit(SL* ps){ ps.a=NULL; ps.size=ps.capacity=0; } void SeqListPushBack(SL* ps,SLDataType x); void SeqListPopBack(SL* ps); void SeqListPushFront(SL* ps,SLDataType x); void SeqListPopFront(SL* ps);
#include "SeqList.h" void TestSeqList1() { SL sl; SeqListInit(&sl); } int main(){ return 0; }
2.2 tail insertion
Discuss the situation:
- Enough space
- Not enough space
- Originally empty
- It's full
- malloc can also be achieved when realloc is used alone
#include "SeqList.h" void SeqListInit(SL* ps){ ps.a=NULL; ps.size=ps.capacity=0; } void SeqPushBack(SL* ps,SLDataType x){ if(ps->capacity=ps->size){ int newcapacity=(capacity==0?(4:capacity*2)); SLDataType* tmp=(SLDataType*)realloc(ps->a,newcapacity*sizeof(SLDataType)); if(tmp==NULL){ perror("realloc failed"); exit(-1); } ps->a=tmp; ps->capacity=newcapacity; } ps->a[size]=x; ps->size++; } void SeqPrintf(SL* ps){ for(int i=0;i<ps->size;i++){ printf("%d ",ps->a[i]); }printf("\n"); } void SeqPopBack(SL* ps); void SeqPushFront(SL* ps,SLDataType x); void SeqPopFront(SL* ps);
#include "SeqList.h" void TestSeqList1() { SL sl; SeqInit(&sl); SeqPushBack(&s1,1); SeqPushBack(&s1,2); SeqPushBack(&s1,3); SeqPushBack(&s1,4); SeqPushBack(&s1,5); } int main(){ return 0; }
2.3 destroy clear space
void SeqListDestroy(SL* ps){ free(ps->a); ps->a=NULL; ps->size=ps->capacity=0; }
#include "SeqList.h" void TestSeqList1() { SL sl; SeqInit(&sl); SeqPushBack(&s1,1); SeqPushBack(&s1,2); SeqPushBack(&s1,3); SeqPushBack(&s1,4); SeqPushBack(&s1,5); SeqListDestroy(&s1); } int main(){ return 0; }
2.4 tail deletion
void SeqListPopBack(ST* ps){ assert(ps->size!=0); ps->size--; }
#include "SeqList.h" void TestSeqList1() { SL sl; SeqInit(&sl); SeqPushBack(&s1,1); SeqPushBack(&s1,2); SeqPushBack(&s1,3); SeqPushBack(&s1,4); SeqPushBack(&s1,5); SeqPushPop(); SeqPushPop(); SeqPushPop(); SeqPushPop(); SeqPushPop(); SeqPushPop(); SeqListDestroy(&s1); } int main(){ TestSeqList1(); return 0; }
2.5 plug
void SeqPushFront(Sq* ps){ if(ps->sz==ps->capacity){ int newcapacity=(capacity==0?4:capacity*2); SLDataType* tmp=(SLDataType*)realloc(capacty*sizeof(SLDataType)); if(tmp==NULL){ perror("realloc"); exit(-1); } ps->a=tmp; ps->capacity=newcapacity; } int end=ps->size-1; while(end>=0){ ps->a[end+1]=ps->a[end]; end--; } ps->a[0]=x; ps->sz++; }
void TestSeqList2(){ SL sl; SeqInit(&sl); SeqPushBack(&s1,1); SeqPushBack(&s1,2); SeqPushBack(&s1,3); SeqPushBack(&s1,4); SeqPushBack(&s1,5); SeqListPushFront(&s1,10); SeqListPushFront(&s1,20); SeqListPushFront(&s1,30); SeqListPushFront(&s1,40); SeqListPushFront(&s1,50); SeqPrintf(&s1); SeqListDestroy(&s1); } int main(){ TestSeqList1(); TestSeqList2(); }
2.6 expansion function
void SeqListCheckCapacity(Sq* ps){ if(ps->capacity==ps->size){ int newcapacity=(capacity==0?4:capacity*2); SLDataType* tmp=(DataType*)realloc(newcapacity*sizeof(SLDataType)); if(tmp==NULL){ perror("SeqListCheckCapacity"); exit(-1); } ps->a=tmp; ps->capacity=newcapacity; } }
2.7 header deletion
void SeqPopFront(SL* sq){ assert(ps->size>0); int begin= 1; while(begin<ps->size){ sq->a[begin-1]=sq->a[begin]; begin++; } ps->size--; }
2.8 other interfaces
2.8.1 find
int SeqListFind(SL* ps,SLDataType x){ for(int i=0;i<ps->size;i++){ if(ps->a[i]==x){ return i; } } return -1; }
2.8.2 insert
Head and tail inserts can reuse inserts.
// Specify pos subscript position insertion void SeqListInsert(SL* ps,int pos,SLDataType x){ assert(pos>=0&&pos<=ps->size); SeqCheckList(ps); LL ed=ps->size; while(ed>pos){ ps->a[ed]=ps->a[ed-1]; ed--; } ps->a[pos]=x; ps->size++; }
2.8.3Erase
Header deletion and tail deletion can reuse erase
void SeqListErase(SL* ps,int pos,SLDataType x){ assert(pos>=0&&pos<ps->size); ps->size--; while(pos<ps->size){ ps->a[pos]=ps->a[pos+1]; pos++; } }
3. Relevant interview questions
4. Defects of sequence list and proposal of linked list
Sequence table defects:
- There is not enough space to expand, which is consumed.
- The sequence table requires the data to be stored continuously from the starting position, so we need to move the data when we insert and delete the data in the head or in the middle, and the moving data is consumed
- Avoid frequent capacity expansion, which is generally expanded by 2 times at a time. There may be a waste of space.
Advantages of sequence table:
Support random access
In view of the defects of the sequence list, the linked list is designed.
Advantages of linked list:
- Apply for space on demand and release space when not in use (more rational use of space)
- Insert and delete data in the middle of the header without moving the data
Disadvantages of linked list:
For each data, a pointer should be stored to link the following data nodes
Random access is not supported (subscript is used to directly access the ith) [for example, dichotomy, optimized fast scheduling, etc. all need this property]
2. Linked list
preface:
Defects of sequence table:
- There is not enough space to expand, which is consumed.
- The sequence table requires the data to be stored continuously from the starting position, so we need to move the data when we insert and delete the data in the head or in the middle, and the moving data is consumed
- Avoid frequent capacity expansion, which is generally expanded by 2 times at a time. There may be a waste of space.
In view of the defects of the sequence list, the linked list is designed.
advantage:
- Apply for space on demand and release space when not in use (more rational use of space)
- Insert and delete data in the middle of the header without moving the data
Disadvantages:
For each data, a pointer should be stored to link the following data nodes
Random access is not supported (subscript is used to directly access the ith) [for example, dichotomy, optimized fast scheduling, etc. all need this property]
In practice, the structure of the linked list to be implemented is very diverse. There are eight linked list structures combined with the following situations:
-
Unidirectional and bidirectional
-
Take the lead, don't take the lead
-
Cyclic and non cyclic
-
Headless one-way acyclic linked list: it has a simple structure and is generally not used to store data alone. In fact, it is more used as a substructure of other data structures, such as hash bucket, adjacency table of graph and so on. There are many problems in the written interview.
-
Lead two-way circular linked list: the structure is the most complex. It is generally used to store data separately. The linked list data structure used in practice is a two-way circular linked list. Although the structure of this structure is complex, you will find that the structure will bring many advantages after code implementation, but the implementation is simple.
- For example, when inserting the first node, the single linked list also needs secondary pointers and tail finding. And take the lead in one step.
1. Headless single cycle linked list
Logic diagram and physical memory diagram of linked list
Logical structure: imagined, more vivid and easy to understand.
Physical structure:
2. Simulation of linked list
2.1 interface of single linked list
//Headless + unidirectional + acyclic linked list addition, deletion, query and modification typedef int SLTDateType; typedef struct SListNode { SLTDateType data; struct SListNode* next; }SListNode; // Dynamically apply for a node SListNode* BuySListNode(SLTDateType x); // Single linked list printing void SListPrint(SListNode* plist); // Single chain table tail insertion void SListPushBack(SListNode** pplist, SLTDateType x); // Header insertion of single linked list void SListPushFront(SListNode** pplist, SLTDateType x); // Tail deletion of single linked list void SListPopBack(SListNode** pplist); // Single chain header deletion void SListPopFront(SListNode** pplist); // Single linked list lookup SListNode* SListFind(SListNode* plist, SLTDateType x); // The single linked list inserts an x after the pos position // Analyze and think why not insert before the pos position? void SListInsertAfter(SListNode* pos, SLTDateType x); // Value after deleting pos position in single linked list // Analyze and think why not delete the pos location? void SListEraseAfter(SListNode* pos);
2.2 structure definition of single linked list
//SList.h #pragma once #include <stdio.h> #include <stdlib.h> typedef int SLTDateType; typedef struct SListNode { SLTDateType data; struct SListNode* next; }SLTNode; void SListPrint(SLTNode* phead); void SListPushBack(SLTNode** pphead, SLTDateType x);
2.3 tail insertion of single linked list
#include "SList.h" //This can also be a secondary pointer, but it's not necessary void SListPrint(SLTNode* phead) { SLTNode* cur = phead; while (cur != NULL) { printf("%d->", cur->data); cur = cur->next; } } // Single chain table tail insertion // The flag of the tail node is next==NULL // Note that since the initial null node will become a node, a secondary pointer is required void SListPushBack(SListNode** pphead, SLTDateType x){ STLNode* newnode =(STLNode*)malloc(sizeof(STLNode)); newnode->val=x; newnode->next=NULL; if(*pphead==NULL){ *pphead=newnode; return; } //Tail node found SLTNode* tail=*pphead; while(tail->next!=NULL){ tail=tail->next; } // tail->next=newnode; } //However, when empty - > is to dereference and access the corresponding memory, the start of space-time must collapse.
Note that there are problems with arguments and formal parameters. That is, the old value copy.
Since direct transfer of the pointer will only assign the value of the pointer, to change the external pointer variable, it is necessary to transfer the pointer address in the function parameter, which is received by the secondary pointer.
//Cannot change argument i void fun(int i){ i=3; } //Can change argument i void fun(int* i){ *i=3; } //The table cannot the argument int* i void fun(int* i){ i=NULL; } //Can change argument int* i void fun(int** i){ *i=NULL; }
2.4 head insertion of single chain table
void SListPushFront(SLTNode** pphead, SLTDateType x) { SLTNode* newnode = BuyListNode(x); newnode->next = *pphead; *pphead = newnode; }
2.5 tail deletion of single linked list
Think clearly first: do you want a secondary pointer for header deletion and tail deletion
Header deletion must be changed to plist.
When the tail is deleted to a node, the secondary pointer is required.
So we should unify the two
-
The method of setting the previous node NULL for tail deletion processing:
- Two variables alternate
- Judge whether tail - > next - > next is empty
However, both methods have the defect of single node
void SlistPopBack(STLNode** pphead){ STLNode* prev=NULL; STLNode* tail=*pphead; while(tail->next){ prev=tail; tail=tail->next; } free(tail); tail=NULL; prev->next=NULL; }
STLNode* tail=**pphead; while(tail->next->next){ tail=tail->next; } free(tail->next); tail->next=NULL;
-
Therefore, to classify and discuss, the linked list is empty, single node and multiple nodes
void SlistPopBack(STLNode** pphead){ assert(*pphead!=NULL);//The linked list is empty STLNode* prev=NULL; STLNode* phead=*pphead; //One node if((*pphead)->next==NULL){ free(*pphead); *phead=NULL; }//Two or more nodes else{ while(tail->next){ prev=tail; tail=tail->next; } free(tail); tail=NULL; prev->next=NULL; } }
2.6 header deletion of single linked list
Similarly, compared with header deletion, consider the case where the linked list is empty, a single node and two or more nodes
void SListPopFront(STLNode** pphead) { //Empty linked list assert( *(pphead)!=NULL ); //1 if((*pphead)->Next==NULL){ free(*pphead); *pphead=NULL; } else{ STLNode* phead=(*pphead)->next; free(*pphead); *pphead=phead; } }
- However, it is found that one node and multiple nodes deleted by this header can be merged
void SListPopFront(STLNode** pphead) { //Empty linked list assert( *(pphead)!=NULL ); //1 and more STLNode* phead=(*pphead)->next; free(*pphead); *pphead=phead; }
2.7 search and modification of single linked list
The return node pointer can be modified
SLTNode* SListFind(SLTNode* phead,STLDateType x){ SLTNode* cur = phead; while(cur){ if(cur->data==x){ return cur; } else{ cur=cur->next; } } return NULL; } void TestSList4(){ SLTNode* pos =SListFind(plist,2); int i=1; while(pos){ printf("The first%d individual pos node:%p->%d\n",i++,pos,pos->data); pos=SListFind(pos->next,2); } //The first 3 is changed to 30 pos=SListFind(plist,3); if(pos) { pos->data=30; } SListPrint(plist); }
2.8 pre insertion and STL post insertion of single linked list (InsertAfter)
There are roughly two ways. The first is also the way in many books
void SListInsert(SLTNode* phead,int pos,SLTDateType x){ };
The second way is to insert a node before the pos position. [follow the method of STL library forward_list]
void SListInsert(SLTNode** phead,SLTNode* pos,SLTDateType x);
The pos position is found by the find function.
However, it should be noted that if the searched element is the first element, it should be transformed into a header interpolation function
void TestSList5() { SLTNode* plist = NULL; SListPushFront(&plist, 1); SListPushFront(&plist, 2); SListPushFront(&plist, 3); SListPushFront(&plist, 4); SListPrint(plist); SLTNode* pos = SListFind(plist, 3); if (pos) { SListInsert(&plist, pos, 30); } SListPrint(plist); }
void SListInsert(SLTNode** phead,SLTNode* pos,SLTDateType x) { assert(pos!=NULL); STLNode* newnode=BuyListNode(x); //Find the previous position of pos if(*pphead==pos){ SListPushFront(phead,x); } else{ STLNode* posPrev=*pphead; while(posPrev->next!=pos) { posPrev=posPrev->next; } posPrev->next=newnode; newnode->next=pos; }u }
But the front insertion is troublesome, so the implementation of STL is pos back insertion. One is for simplicity, and the second is to save a layer of constant of ${O(n)} $(search once by using O(n) of find)
//Insert after pos, which is simpler and more suitable void SListInsert_After(STLNode* pos,SLTDateType x){ assert(pos!=NULL); STLNode* newnode=(STLNode*)malloc(sizeof(STLNode)); STLNode* tmp=pos->next; pos->next=newnode; newnode->next=tmp; }
2.9 deletion and post deletion of single linked list (EraseAfter)
void SListErase(SLTNode** phead,SLTNode* pos){ STLNode* head=*SLTNode phead; assert(head!=NULL); if(head==pos){ *phead=head->next; free(head); //SListPopFront(pphead); Reuse! } else{ STLNode* prev=*STLNode phead; while(prev->next!=pos){ prev=prve->next; } prev->next=pos->next; free(pos); } }
Although EraseAfter is a little counterintuitive
void SListEraseAfter(SLTNode* phead,STLNode* pos){ assert(pos!=NULL); assert(phead->next!=NULL) STLNode* tmp=pos->next; pos->next=pos->next->next; free(tmp); }
2.10 destruction of single linked list
void SListDestroy(SLTNode** phead){ assert(phead); STLNode* head=*phead; while(head->next!=NULL){ STLNode* tmp=head; head=head->next; free(tmp); } *phead=NULL; }
2. * test code
#include "SList.h" void TestSList1() { SLTNode* plist = NULL; SListPushBack(&plist, 1); SListPushBack(&plist, 2); SListPushBack(&plist, 3); SListPushBack(&plist, 4); SListPrint(plist); SListPushFront(&plist, 1); SListPushFront(&plist, 2); SListPushFront(&plist, 3); SListPushFront(&plist, 4); SListPrint(plist); } void TestSList2() { SLTNode* plist = NULL; SListPushFront(&plist, 1); SListPushFront(&plist, 2); SListPushFront(&plist, 3); SListPushFront(&plist, 4); SListPopBack(&plist); SListPopBack(&plist); SListPopBack(&plist); SListPopBack(&plist); //SListPopBack(&plist); SListPrint(plist); } void TestSList3() { SLTNode* plist = NULL; SListPushFront(&plist, 1); SListPushFront(&plist, 2); SListPushFront(&plist, 3); SListPushFront(&plist, 4); SListPopFront(&plist); SListPrint(plist); SListPopFront(&plist); SListPrint(plist); SListPopFront(&plist); SListPrint(plist); SListPopFront(&plist); SListPrint(plist); } void TestSList4() { SLTNode* plist = NULL; SListPushFront(&plist, 1); SListPushFront(&plist, 2); SListPushFront(&plist, 3); SListPushFront(&plist, 2); SListPushFront(&plist, 4); SListPushFront(&plist, 2); SListPushFront(&plist, 2); SListPushFront(&plist, 4); SListPrint(plist); // look for SLTNode* pos = SListFind(plist, 2); int i = 1; while (pos) { printf("The first%d individual pos node:%p->%d\n",i++, pos, pos->data); pos = SListFind(pos->next, 2); } // Modify 3 - > 30 pos = SListFind(plist, 3); if (pos) { pos->data = 30; } SListPrint(plist); } void TestSList5() { SLTNode* plist = NULL; SListPushFront(&plist, 1); SListPushFront(&plist, 2); SListPushFront(&plist, 3); SListPushFront(&plist, 4); SListPrint(plist); SLTNode* pos = SListFind(plist, 3); if (pos) { SListInsert(&plist, pos, 30); } SListPrint(plist); pos = SListFind(plist, 1); if (pos) { SListInsert(&plist, pos, 10); } SListPrint(plist); pos = SListFind(plist, 4); if (pos) { SListInsert(&plist, pos, 40); } SListPrint(plist); } //int main() //{ // //TestSList1(); // //TestSList2(); // //TestSList3(); // //TestSList4(); // TestSList5(); // // // return 0; //} // Definition for singly-linked list. struct ListNode { int val; struct ListNode *next; }; struct ListNode* removeElements(struct ListNode* head, int val){ struct ListNode* prev = NULL, *cur = head; while (cur) { if (cur->val == val) { // 1. Header deletion // 2. Intermediate deletion prev->next = cur->next; free(cur); cur = prev->next; } else { // Iteration back prev = cur; cur = cur->next; } } return head; } int main() { // Easy and fast debugging oj code struct ListNode* n1 = (struct ListNode*)malloc(sizeof(struct ListNode)); struct ListNode* n2 = (struct ListNode*)malloc(sizeof(struct ListNode)); struct ListNode* n3 = (struct ListNode*)malloc(sizeof(struct ListNode)); struct ListNode* n4 = (struct ListNode*)malloc(sizeof(struct ListNode)); n1->val = 7; n2->val = 7; n3->val = 7; n4->val = 7; n1->next = n2; n2->next = n3; n3->next = n4; n4->next = NULL; struct ListNode* head = removeElements(n1, 7); return 0; }
3. Learning purpose of single linked list
There are still many defects in a single linked list. The addition, deletion, query and modification of a simple single linked list is of little significance.
- Many OJ questions are tested by single linked list (because the single linked list is defective)
- Single linked list is more as a substructure of complex structure, such as hash bucket and adjacency table
The linked list is still used to store data.
4. Linked list exercises
Due to the structure of single linked list, it should be considered
- Single node
- The operation happens to be the first node
- Empty linked list
Handling methods for RE and other errors:
-
Read the code and draw a comparison example
-
I really can't put VS debugging (note that there is no debugging in the interview)
-
Delete the structure annotation defined by oj
-
Manually create a linked list, malloc node
-
The sample board is as follows:
-
struct ListNode* removeElements(struct ListNode* head, int val){ struct ListNode* prev = NULL, *cur = head; while (cur) { if (cur->val == val) { // 1. Header deletion // 2. Intermediate deletion prev->next = cur->next; free(cur); cur = prev->next; } else { // Iteration back prev = cur; cur = cur->next; } } return head; } int main() { // Easy and fast debugging oj code struct ListNode* n1 = (struct ListNode*)malloc(sizeof(struct ListNode)); struct ListNode* n2 = (struct ListNode*)malloc(sizeof(struct ListNode)); struct ListNode* n3 = (struct ListNode*)malloc(sizeof(struct ListNode)); struct ListNode* n4 = (struct ListNode*)malloc(sizeof(struct ListNode)); n1->val = 7; n2->val = 7; n3->val = 7; n4->val = 7; n1->next = n2; n2->next = n3; n3->next = n4; n4->next = NULL; struct ListNode* head = removeElements(n1, 7); return 0; }
-
2. Take the lead in two-way circular linked list
- Sentinel position, no valid data stored
- The traversal starts from the next of the first node, goes down and ends at phead
//Lead + two-way + circular linked list addition, deletion, query and modification framework typedef int LTDataType; typedef struct ListNode { LTDataType _data; struct ListNode* _next; struct ListNode* _prev; }ListNode; // Create the head node of the return linked list ListNode* ListCreate(); // Bidirectional linked list destruction void ListDestory(ListNode* plist); // Bidirectional linked list printing void ListPrint(ListNode* plist); // Bidirectional linked list tail insertion void ListPushBack(ListNode* plist, LTDataType x); // Bidirectional linked list tail deletion void ListPopBack(ListNode* plist); // Bidirectional chain header insertion void ListPushFront(ListNode* plist, LTDataType x); // Bidirectional linked list header deletion void ListPopFront(ListNode* plist); // Bidirectional linked list lookup ListNode* ListFind(ListNode* plist, LTDataType x); // The bidirectional linked list is inserted in front of the pos void ListInsert(ListNode* pos, LTDataType x); // A bidirectional linked list deletes a node at the pos position void ListErase(ListNode * pos);
#pragma once typedef int LTDataType; typedef struct ListNode { struct ListNode* next; struct ListNode* prev; LTDataType data; }ListNode; ListNode* BuyListNode(LTDataType x){ ListNode* node=(ListNode*)malloc(sizeof(ListNode)); node->next=node->prev=NULL; node->data=x; return node; } ListNode* ListInit(){ ListNode* phead=BuyListNode(0); phead->next=phead; phead->prev=phead; } //Later, it was found that insert(phead,x) could be reused; void ListPushBack(ListNode* phead,LTDataType x){ assert(phead); ListNode* tail=phead->prev; ListNode* newNode=BuyListNode(x); tail->next=newNode; newNode->prev=tail; newNode->next=phead; phead->prev=newNode; } //Later, it is found that insert can be reused (phead - > next, x); void ListPushFront(ListNode* phead,LTDataType x){ assert(phead); ListNode* right=phead->next; ListNode* newnode =BuyListNode(x); phead->next=newcode; newcode->prev=phead; newcode->next=right; right->prev=newcode; } ///Later, it is found that listerase (phead prev) can be reused void ListPopBack(ListNode* phead){ assert(phead); assert(phead->next!=phead);///Ensure that the data is not the header node itself ListNode* tail=phead->prev; ListNode* right=tail->prev; free(tail); phead->prev=right; right->next=phead; } //Later, it is found that listerease (phead - > next) can be reused; void ListPopFront(ListNode* phead){ assert(phead); assert(phead->next!=phead);//Ensure that the data header node is not itself ListNode* de=head->next; ListNode* ne=de->next; free(de); ne->prev=phead; head->next=ne; } void ListPrint(ListNode* phead){ ListNode* cur=phead->next; while(cur!=phead){ printf("%d ",phead->data); cur=cur->next; }printf("\n"); } ListNode* Listfind(ListNode* phead,LTDataType x){ assert(phead); ListNode* cur=phead->next; while(cur!=phead){ if(cur->data==x){ return cur; } cur=cut->next; } } void ListInsert(ListNode* pos,LTDataTyped x){ assert(pos); ListNode* prev=pos->prev; ListNode* newnode=BuyListNode(x); prev->next=newnode; newnode->prev=prev; newnode->next=pos; pos->prev=newnode; } void ListErase(ListNode* pos){ assert(pos); ListNode* pre=pos->prev; ListNode* next=pos->next; pre->next=next; next->prev=prev; free(pos); } int ListEmpty(); int ListSize(ListNode* phead); void ListDestroy(ListNode* phead){ assert(phead); ListNode* cur=phead->next; while(cur!=phead){ ListNode* next=cur->next;///Save in advance free(cur); cur=next; } free(phead); phead=NULL;//The first level pointer is useless. This is a temporary variable //1. Transfer secondary pointer //2. Assign NULL after use; Maintain interface consistency }
void TestList1() { ListNode* plist=ListInit(); ListPushBack(plist,1); ListPushBack(plist,2); ListPushBack(plist,3); ListPushBack(plist,4); ListPrint(plist); } void TestList2() { ListNode* plist=ListInit(); ListPushBack(plist,1); ListPushBack(plist,2); ListPushBack(plist,3); ListPushBack(plist,4); ListPrint(plist); ListPushFront(plist,1); ListPushFront(plist,2); ListPushFront(plist,3); ListPushFront(plist,4); ListPrint(plist); } void TestList2(){ ListNode* plist=ListInit(); ListPushBack(plist,1); ListPushBack(plist,2); ListPushBack(plist,3); ListPushBack(plist,4); ListPushBack(plist,5); ListPushBack(plist,6); ListNode* pos=ListFind(plist,4); if(pos){ ListInsert(pos,40); } ListPrint(plist); }
- 10~15min to achieve a two-way linked list
- Come up and write insert and erase, and then you can reuse them.
3. Comparison of sequence list and linked list
Sequence table:
advantage:
- Random access is supported. Algorithms that require random access structure support can be well applied.
- Higher cpu cache hit rate
Disadvantages:
- The insertion and deletion time in the middle of the head is inefficient. O(N)
- Continuous physical space. When the space is insufficient, it needs to be increased
- Capacity increase will consume to a certain extent
- In order to avoid frequent capacity increase, we generally increase by multiple. If we can't use up, there may be a certain waste of space.
Linked list (two-way cyclic linked list)
advantage:
- High efficiency of insertion and deletion at any position
- Apply for free space on demand
Disadvantages:
- Random access is not supported. (subscript access means that some sorting, binary search, etc. are not applicable to this structure)
- The linked list stores a value and the connection pointer, which also has a certain consumption.
- Lower cpu cache hit rate
For cache structure:
Assuming no hit, load 20byte s into the cache at a time (the specific load size depends on the hardware system)
When accessing data, you will find it in the cache first, and then go to the memory if you can't find it. (i.e. cache miss)
According to the locality principle, the hit rate of sequential list is certainly greater than that of linked list. Because the space storage of the linked list itself is discontinuous, hitting one piece may not hit another. According to the replacement algorithm, it will also cause cache pollution.
For specific information, please refer to[ https://coolshell.cn/articles/20793.html ]
difference | Sequence table | Linked list |
---|---|---|
On storage space | Physically continuous | Logically continuous, but not necessarily physically continuous |
Random access | Support O (1) | I won't support it; O(n) |
Insert or delete elements anywhere | May need to move elements, inefficient O (N) | Just modify the pointer |
insert | Dynamic sequential table. Capacity expansion is required when space is insufficient | There is no concept of capacity |
Application scenario | Element efficient storage + frequent access | Frequent insertion and deletion at any location |
Cache utilization | high | low |