# [data structure] linked list (single linked list implementation + detailed explanation + original code)

catalogue

preface

Concept and structure of linked list

1. One way or two-way

3. Cyclic or non cyclic

Application node

Tail insertion

Tail deletion

Insert after specified position

Delete at specified location

lookup

Complete code

Difference between sequential list and linked list

# preface

In the last chapter, we learned the sequence table and realized the addition, deletion, query and modification of the sequence table.

Of course, some advantages and disadvantages of the sequence table are also found. Let's review:

1. Support random access, which can be accessed directly through subscript.
2. You can sort.

1. Insertion and deletion of middle / head, with time complexity of O(N)
2. To increase capacity, you need to apply for new space, copy data and release old space. There will be a lot of consumption.
3. The capacity increase is generally double, which is bound to waste some space.

So in order to make up for these shortcomings, there is a linked list, so what is a linked list?

## Concept and structure of linked list

Concept: linked list is a non continuous and non sequential storage structure in physical storage structure. The logical order of data elements is realized through the pointer link order in the linked list.
It is still abstract only according to the text description. Observe directly from the above figure:

In the figure: 2.3.4.5 are all structures, called nodes. Different from the sequence table, each node in the linked list does not simply store one data. It is a structure whose members include a stored data and the address of the next node. In addition, the addresses in the sequential list are continuous, while the addresses of nodes in the linked list are randomly assigned.

How does the linked list work?

The address of the first node is stored in the phead pointer in the figure, so we can find the structure according to the pointing address. Because the address of the next structure is stored in the structure, we can find the second structure. We can find all nodes in a cycle until the structure with empty address is stored.

Note: the arrows in the figure do not actually exist. They are only here for convenience of understanding.

be careful:

1. As can be seen from the figure, the chain structure is logically continuous, but not necessarily continuous physically.
2. In reality, nodes are generally applied from the heap.
3. The space applied from the heap is allocated according to certain strategies. The space applied for two times may or may not be continuous.

In practice, the structure of linked list is very diverse. There are eight linked list structures when the following situations are combined:

### 3. Cyclic or non cyclic

Although there are so many linked list structures, two structures are most commonly used in practice:

1. 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. In addition, this structure appears a lot in the written interview.
2. Lead two-way circular linked list: it has the most complex structure and is generally used to store data separately. The linked list data structure used in practice is a two-way circular linked list. In addition, although the structure is complex, you will find that the structure will bring many advantages after code implementation, but the implementation is simple. We will know after code implementation.

# Implementation of single linked list

```#pragma once

#include<stdio.h>
#include<assert.h>
#include<stdlib.h>

typedef int SLTDataType;

typedef struct SListNode
{
SLTDataType data;
struct SListNode* next;//Address of the next node

}SListNode;

//Application node

//Because we point to the head node of the linked list through a pointer, and because the head node of the linked list may be changed during insertion and deletion, the following parameters need to pass the secondary pointer
//Tail insertion

//Tail deletion

//lookup

//Insert after specified position
void SListInsert(SListNode** pphead, SListNode* pos, SLTDataType x);

//Delete after specified location

```

In the sequence table, each element is accessed through subscript. The linked list is different from the sequence table. After accessing the data of this node, you need to find the next node through the address stored in this node.

```//Print linked list
{
SListNode* cur = phead;//Generally, instead of directly moving the head pointer, create a pointer variable to move it
while (cur)//Ends the loop when the pointer is null
{
printf("%d->", cur->data);//Print the data of this node
cur = cur->next;//Point the pointer to the next node
}
printf("NULL\n");
}```

## Application node

Each node of the linked list is dynamically opened (malloc), and the size of each node is the size of the structure.

After successful development, the data stored in the node shall be set to the value to be stored, and the address stored in the node shall be set to NULL.

```//Application node
{
SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));
if (newnode == NULL)//Judge whether the node is successfully opened
{
perror("malloc:");
exit(-1);
}
newnode->data = x;
newnode->next = NULL;
}```

## Tail insertion

Because the element cannot be accessed according to the subscript (that is, it cannot be accessed randomly), of course, we do not know the location of the last node. When tailoring, we need to traverse to find the location of the last node.

At the same time, there are two situations:

1. If the linked list is empty, you can insert it directly.
2. If the linked list is not empty, you need to find the tail node and insert it.
```//Tail insertion
{
{
*pphead = newnode;//Directly set the head node as the node to be inserted
}
else
{
while (cur->next)//Tail finding node
{
cur = cur->next;
}
cur->next = newnode;//Set the address stored in the tail node as the address of the insertion node
}
}```

Header insertion is relatively simple. Directly set the next of the application node as the header node, and then change the header node to the application node

Note: there is no need to consider whether the linked list is empty.

```//Head insert
{
}```

## Tail deletion

Like tail insertion, we do not know the address of the tail node, so we need to find the tail node first.

At the same time, three situations need to be considered:

1. The linked list is empty.
2. There is only one node in the linked list.
3. There is more than one node in the linked list.
```//Tail deletion
{
//1. If the linked list is empty, the node cannot be deleted, and the pointer cannot be empty

//2. There is only one node in the linked list. Release the node directly, and then set the node to NULL
{
return;
}

//3. If there is more than one node in the linked list, find the tail node first, release the tail node and set it to NULL
//  But this is not enough, because the penultimate node still has the address of the tail node, so it needs to be set to NULL
SListNode* cur = *pphead;//Used to mark the penultimate node
SListNode* next = (*pphead)->next;//Tag tail node
while (next->next)
{
next = next->next;
cur = cur->next;
}
cur->next = NULL;//Set the address stored in the penultimate node to NULL
free(next);//Release tail node
next = NULL;
}```

Header deletion is also relatively simple, which is equivalent to moving the header pointer to the second node.

There are two situations:

1. The linked list is empty.
2. The linked list is not empty.
```//Header deletion
{
*pphead = next;//Point the pointer to the second node
}
```

## Insert after specified position

The reason for inserting after the specified position rather than before is that when inserting in front, you need to find the address in front of the insertion position, and this will traverse the linked list again. The time complexity is O(N), while when inserting in the back, you can insert directly, and the time complexity is O(1).

```//Insert after specified position
void SListInsert(SListNode** pphead, SListNode* pos, SLTDataType x)
{
SListNode* next = pos->next;//Find the address of the next node at the insertion location
pos->next = newnode;//Insert Knot
newnode->next = next;//Connect to the following linked list

}```
```// Insert a node before the pos position
void SListInsert(SLTNode** pphead, ListNode* pos, SLTDateType x)
{
assert(pos);

{
}
else
{
// Find the previous position of pos
while (posPrev->next != pos)
{
posPrev = posPrev->next;
}

posPrev->next = newnode;
newnode->next = pos;
}
}```

## Delete at specified location

Here, you can delete at the specified location instead of before or after, because you will encounter some difficulties in header deletion and tail deletion.

```//Delete at specified location
{
if (*pphead == pos)//If the head node is the node to be deleted
{
free(pos);
pos = NULL;
}
else
{
while (cur->next != pos)//Find the node to delete
{
cur = cur->next;
}
cur->next = pos->next;//Point the next of the previous node of the node to be deleted to the next node to be deleted
free(pos);
pos = NULL;
}
}```

## lookup

According to the data provided, traverse each node in the linked list. If the data in a node is the same, return the address of the node; NULL if not found.

```//lookup
{
{
{
}
}
return NULL;
}```

Save the address of the next node, release the current node, point the pointer to the next node, and release the next node until the linked list is empty.

```//Destroy linked list
{
{
}
}```

## Complete code

```//Print linked list
{
while (cur)
{
printf("%d->", cur->data);
cur = cur->next;
}
printf("NULL\n");
}

//Application node
{
SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));
if (newnode == NULL)
{
perror("malloc:");
exit(-1);
}
newnode->data = x;
newnode->next = NULL;
return newnode;
}

{
while (cur)
{
SListNode* next = cur->next;
free(cur);
cur = next;
}
}

//Tail insertion
{
{
}
else
{
while (cur->next)
{
cur = cur->next;
}
cur->next = newnode;
}
}

{
}

//Tail deletion
{
{
return;
}
while (next->next)
{
next = next->next;
cur = cur->next;
}
cur->next = NULL;
free(next);
next = NULL;
}

{
}

//lookup
{
{
{
}
}
return NULL;
}

//Insert after specified position
void SListInsert(SListNode** pphead, SListNode* pos, SLTDataType x)
{
SListNode* next = pos->next;
pos->next = newnode;
newnode->next = next;

}

//Delete at specified location
{
{
free(pos);
pos = NULL;
}
else
{
while (cur->next != pos)
{
cur = cur->next;
}
cur->next = pos->next;
free(pos);
pos = NULL;
}
}```

assert(pphead) means that the secondary pointer of the parameter cannot be null, because the null pointer cannot be dereferenced!

# Force buckle linked list OJ

Force buckle --- remove linked list elements

Idea:

Similar to the tail deletion of a single linked list, first find the element to be deleted, and point the next of the previous node to the next of the deleted node,

Then free deletes the current node.

Of course, there are three situations:

1. The linked list is empty and NULL is returned.
2. The node to be deleted is the head node. Release the head node directly and take the next node as the head node.
3. If the node to be deleted is in, use the conventional method.

The code is as follows:

```typedef struct ListNode ListNode;//Rename for easy handling
struct ListNode* removeElements(struct ListNode* head, int val){
//1. If the linked list is empty, NULL will be returned directly
{
return NULL;
}

ListNode*cur =head;//Mark nodes to be deleted
ListNode*prev = NULL;//Mark the previous node of the node to be deleted
while(cur)
{
//Find the node to delete
if(cur->val==val)
{
//2. If the node to be deleted is the head node
{
//Point the header pointer to the second node
free(cur);
}
//3. The node to be deleted is in the middle
else
{
//Point the next of the previous node to the next of the deleted node
prev->next = cur->next;
free(cur);
cur = prev->next;
}
}
//You don't need to delete nodes to move backward
else
{
prev = cur;
cur = cur->next;
}
}