Data structure (C language version - sequential list and linked list)

1. Sequence list and linked list

  1. Linear table
  2. Sequence table
  3. Linked list
  4. 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:

  1. Enough space
  2. Not enough space
    1. Originally empty
    2. 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:

  1. There is not enough space to expand, which is consumed.
  2. 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
  3. 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:

  1. Apply for space on demand and release space when not in use (more rational use of space)
  2. 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:

  1. There is not enough space to expand, which is consumed.
  2. 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
  3. 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:

  1. Apply for space on demand and release space when not in use (more rational use of space)
  2. 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:

  1. Unidirectional and bidirectional

  2. Take the lead, don't take the lead

  3. Cyclic and non cyclic

  4. 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.

  5. 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.

    1. 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:

    1. Two variables alternate
    2. 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.

  1. Many OJ questions are tested by single linked list (because the single linked list is defective)
  2. 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

  1. Single node
  2. The operation happens to be the first node
  3. Empty linked list

Handling methods for RE and other errors:

  1. Read the code and draw a comparison example

  2. I really can't put VS debugging (note that there is no debugging in the interview)

    1. Delete the structure annotation defined by oj

    2. Manually create a linked list, malloc node

    3. The sample board is as follows:

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

Reverse linked list

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:

  1. Random access is supported. Algorithms that require random access structure support can be well applied.
  2. Higher cpu cache hit rate

Disadvantages:

  1. The insertion and deletion time in the middle of the head is inefficient. O(N)
  2. Continuous physical space. When the space is insufficient, it needs to be increased
    1. Capacity increase will consume to a certain extent
    2. 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:

  1. High efficiency of insertion and deletion at any position
  2. Apply for free space on demand

Disadvantages:

  1. Random access is not supported. (subscript access means that some sorting, binary search, etc. are not applicable to this structure)
  2. The linked list stores a value and the connection pointer, which also has a certain consumption.
  3. 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 ]

differenceSequence tableLinked list
On storage spacePhysically continuousLogically continuous, but not necessarily physically continuous
Random accessSupport O (1)I won't support it; O(n)
Insert or delete elements anywhereMay need to move elements, inefficient O (N)Just modify the pointer
insertDynamic sequential table. Capacity expansion is required when space is insufficientThere is no concept of capacity
Application scenarioElement efficient storage + frequent accessFrequent insertion and deletion at any location
Cache utilizationhighlow

Tags: data structure

Posted on Sat, 20 Nov 2021 05:03:48 -0500 by wtech