The strongest data structure in history----C simulation implementation of singly linked list (illustration + code)

3.1 Concept and structure of linked list

Concept: Linked list is a non-consecutive and non-sequential storage structure in physical storage structure. The logical order of data elements is realized through the link order of pointers in the linked list.

image-20220312183503652

Notice:

1. As can be seen from the above figure, the chain structure is logically continuous, but not necessarily physically continuous
2. In reality, nodes are generally applied for from the heap
3. From the space applied for on the top, It is allocated according to a certain strategy, and the space for the two applications may or may not be continuous.

3.2 Classification of linked lists

In practice, the structure of linked lists is very diverse. There are 8 linked list structures in combination of the following situations:

1. One-way or two-way linked list

2. Linked list with or without head

3. Circular or acyclic linked list

There are two most commonly used: headless one-way acyclic linked list, headed two-way circular linked list

  1. Headless one-way acyclic linked list: The structure is simple, and it is generally not used to store data alone. In practice, it is more of a substructure of other data structures, such as hash buckets, graph adjacency lists, and so on. In addition, this structure appears a lot in the written test interview.
  2. Headed doubly circular linked list: The structure is the most complex and is generally used to store data separately. The linked list data structures used in practice are all leading doubly circular linked lists. In addition, although this structure is complex, it will be found that the structure will bring many advantages after using the code to implement, and the implementation is simple, and we will know after the code is implemented.

3.3 Implementation of Linked List Headless + One-way + Acyclic Linked List Addition, Deletion, Search and Modification Implementation

3.3.1 Definition of Linked List

typedef int SLTDataType;//
typedef struct SListNode
{
	int data;//val,存储的数据,此处假设存储的数据为int型
	struct SListNode* next;//存储下一个节点的位置
}SListNode,SLN;

3.3.2 Printing of linked list data

void SListPrint(SListNode* phead)
{
	SListNode* cur = phead;
	while (cur != NULL)
	{
		printf("%d->", cur->data);
		cur = cur->next;
	}
	printf("NULL\n");
}

3.3.3 Tail insertion of linked list

void SListPushBack(SListNode** pphead, SLTDataType x)
{
	SListNode* newnode = BuySListNode(x);
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else
	{
		//找尾
		SListNode* tail = *pphead;
		while (tail->next != NULL)
		{
			tail = tail->next;
		}
		tail->next = newnode;
	}
}

In the process of finding the end, be sure not to write the following code:

while(tail!=NULL)
{
	tail = tail->next;
}
tail->next = newnode;

image-20220315113511719

Of course, the above description is the case of tail deletion.

The tail insertion is actually similar. The tail insertion is like the above code. When it tail!=NULLis not established, the tail is equal to empty, and then the assignment operation is performed. tail->next = newnodeThis line of code is equivalent to the following code:

(*tail).next, here is equivalent to dereferencing the null pointer, which is actually illegal access, and also attempts to illegally modify the data in the unauthorized memory, which will inevitably lead to the crash of the program. And it does not store the address of the new node in the previous node's next.

This place needs to understand the fundamental principle of linked list traversal:

image-20220315113946661

The linked list is a relatively static data space stored in the heap area. We traverse by changing the data in the local variable tail in the stack area (that is, the address of each linked list node). The reason why we can access it through the tail variable And the reason for modifying the node data is because the data type of tail is SListNode*, that is, the pointer to the node. The type of the pointer determines the data type that can be accessed by dereferencing the pointer, so *tail can access the data of the node in the heap area and can be modified.

3.3.4 Dynamic application of linked list space

SListNode* BuySListNode(SLTDataType x)
{
	SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));
	if (newnode == NULL)
	{
		printf("malloc fail\n");
		exit(-1);
	}
	else
	{
		newnode->data = x;
		newnode->next = NULL;
	}
	return newnode;
}

3.3.5 Header of linked list

void SListPushFront(SListNode** pphead, SLTDataType x)
{
	SListNode* newnode = BuySListNode(x);
	newnode->next = *pphead;
	*pphead = newnode;
}

3.3.6 Tail deletion of linked list

Three situations need to be considered:

  1. null
  2. a node
  3. multiple nodes

Two ways to write:

The first:

void SListPopBack(SListNode** pphead)
{
	assert(pphead);
	if (*pphead == NULL)//空链表
	{
		return;
	}
	else if ((*pphead)->next == NULL)//一个节点
	{
		free(*pphead);//*pphead就是plist的值
		*pphead = NULL;
	}
	else//多个节点
	{
		SListNode* tail = *pphead;
		SListNode* prev = NULL;//为什么要置为空呢?因为这个地方相当于是指向第一个节点之前的节点,这个节点并不存在,设为空
		while (tail->next != NULL)
		{
			prev = tail;
			tail = tail->next;
		}
		free(tail);
		tail = NULL;
		prev->next = NULL;
	}
}

image-20220315191516068

This way there is no problem when faced with only one node.

The second:

void SListPopBack(SListNode** pphead)
{
	assert(pphead);
	if (*pphead == NULL)//空链表
	{
		return;
	}
	else if ((*pphead)->next == NULL)//一个节点
	{
		free(*pphead);//*pphead就是plist的值
		*pphead = NULL;
	}
	else//多个节点
	{
		SListNode* tail = *pphead;
		while (tail->next->next != NULL)
		{
			tail = tail->next;
		}
		free(tail->next);//释放尾节点
		tail->next = NULL;//将新尾节点的next置为NULL
	}
}

image-20220315192821887

3.3.7 Header deletion of linked list

void SListPopFront(SListNode** pphead)
{
	assert(pphead);
	if (*pphead == NULL)//空链表
	{
		return;
	}
	else//非空链表
	{
		SListNode* next = (*pphead)->next;//next作为临时变量存放的是被删除的节点中next存储的第二个节点的地址
		free(*pphead);
		*pphead = next;
	}
}

image-20220315211854659

3.3.7 Pre-insertion at any position in the linked list

void SListInsertBefore(SListNode** pphead, SListNode* pos,SLTDataType x)
{
	assert(pphead);
	if (*pphead == pos)//pos是第一个节点,相当于头插
	{
		SListPushFront(pphead, x);
	}
	else
	{
		SListNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		SListNode* newnode = BuySListNode(x);
		prev->next = newnode;
		newnode->next = pos;
	}
}

image-20220315221207436

3.3.8 Post-insertion at any position in the linked list

Two implementations:

method one:

void SListInsertAfter(SListNode* pos, SLTDataType x)
{
	assert(pos);
	SListNode* newnode = BuySListNode(x);
	
	newnode->next = pos->next;
	pos->next = newnode;
	//这两行代码顺序是固定的,只能这个顺序,无法进行改变
}

Icon:

image-20220316165627436

Method two:

void SListInsertAfter(SListNode* pos, SLTDataType x)
{
	assert(pos);
	SListNode* next = pos->next;
	SListNode* newnode = BuySListNode(x);

	newnode->next = next;
	pos->next = newnode;
	//这两行代码可以任意改变顺序,谁先谁后都不影响最后的结果
}

Icon:

image-20220316170549333

3.3.8 Deletion of any position in the linked list

void SListErase(SListNode** pphead, SListNode* pos)
{
	assert(pphead);
	assert(pos);
	if (pos == *pphead)//当pos为头节点的时候
	{
		SListPopFront(pphead);
	}
	else//当pos为非头节点的时候
	{
		SListNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		prev->next = pos->next;
		free(pos);
		pos = NULL;
	}
}

Icon:

image-20220316173644510

3.3.9 Predeletion at any position in the linked list

void SListEraseBefore(SListNode** pphead, SListNode* pos)//pos即为任意位置
{
	assert(pphead);
	assert(pos);
	if (pos == *pphead)
	{
		return;
	}
	else if(pos==(*pphead)->next)
	{
		SListPopFront(pphead);
	}
	else
	{
		SListNode* prev = *pphead;//prev用来存储pos的前一个位置的前一个位置
		while (prev->next->next != pos)
		{
			prev = prev->next;
		}
		SListNode* next = prev->next;//保存pos前一个节点的地址
		prev->next = prev->next->next;//将prev和pos的两个节点进行连接
		free(next);//删除pos的前一个节点
	}
}

image-20220316180318172

3.3.10 Post deletion of any position in the linked list

void SListEraseAfter(SListNode* pos)
{
	assert(pos);
	SListNode* next = pos->next;
	if (next == NULL)//当pos是最后一个节点的时候
	{
		return;
	}
	else
	{
		pos->next = next->next;
		free(next);
		next = NULL;
	}
}

Icon:

image-20220316191805389

3.3.11 Destruction of linked list

void SListDestory(SListNode** pphead)
{
	assert(pphead);
	SListNode* cur = *pphead;
	SListNode* next = *pphead;//是为了存储cur下一个节点的地址,因为free(cur)之后,cur指针指向的内存中的数据可能已经称为乱码了,即不能再正常的使用
	while (cur)
	{
		next = cur->next;
		free(cur);
		cur = next;
	}
	*pphead = NULL;
}

3.3.12 Summary of Linked Lists

Summary: Singly linked list structure, suitable for head plug deletion. Insertion and deletion at the end or somewhere in the middle are not suitable. If you want to use a linked list structure to store data separately, it is more suitable to use a doubly linked list.

The meaning of singly linked list learning:

  1. The singly linked list will be used as a substructure of complex data structures we will learn later (adjacency list of graph, hash bucket)
  2. There will be many classic practice questions in the singly linked list, and there will be many related questions in the written test interview.

Guess you like

Origin blog.csdn.net/m0_57304511/article/details/123549365