Data structure - implementation of singly linked list (c language version)

Preface 

        As a type of sequence list, singly linked list is a kind of sequence list. Understanding and being familiar with its structure is of certain significance for us to learn more complex data structures. Although singly linked lists have certain flaws, singly linked lists also have their own value. They also appear as part of other data structures, such as graphs and hash tables.

Table of contents

1. Structure of linked list nodes

2. Delete the header plug

3. End insertion and end deletion

4. Insertion and deletion at any position

5. Find the value of the linked list and modify the value of the linked list node

6. Destroy the linked list

7. Test the code

8. All codes

9. Summary 


1. Structure of linked list nodes

        A singly linked list consists of the value of the node and the next pointer of the node, as shown in the figure:

 

typedef int SListDatatype;
typedef struct SListNode
{
	SListDatatype _data;//存储节点的数据
	struct SListNode* _next;
}SListNode;

2. Delete the header plug

        The head plug is divided into two situations, the first is the situation without nodes, and the second is the situation with nodes. As shown in the picture:

                Head deletion is also divided into two situations. If there is only one node, just delete it directly, and then leave the head node empty. If there are multiple nodes, you need to record the head node first and then delete it.

void SListPushFront(SListNode** ppHead, SListDatatype data)//头插
{
	SListNode* newNode = SlistBuyNode(data);//申请一个新的节点
	if (*ppHead == NULL)
	{
		//链表为空
		*ppHead = newNode;
		return;
	}
	newNode->_next = (*ppHead);
	*ppHead = newNode;//对头结点进行链接
}
void SListPopFront(SListNode** ppHead)//头删
{
	assert(*ppHead);//确保指针的有效性
	if ((*ppHead)->_next == NULL)
	{
		//链表只有一个节点
		free(*ppHead);
		*ppHead = NULL;
		return;
	}
	//删除头结点,然后更新头结点
	SListNode* newHead = (*ppHead)->_next;
	free(*ppHead);
	*ppHead = newHead;
	return;
}

3. End insertion and end deletion

Tail insertion is also divided into cases when the linked list is empty and when the pointer is not empty. If the linked list is empty, apply for a node, let the head node of the linked list point to the requested node, and then set the _next         of this node to empty. If the linked list is not empty , First, you need to find the tail node, then link the tail node with this node, and then leave _next of the newly applied node blank. As shown in the picture:

 

        Tail deletion is also divided into two situations: 1 only one node and 2 multiple nodes.

If there is only one node, you need to leave the head node empty after deletion to prevent the problem of wild pointers.

If there are multiple nodes, you need to leave the new tail node blank after deleting the tail node.

As shown in the picture: 

void SListPushBack(SListNode** ppHead, SListDatatype data)//尾插
{
	SListNode*newNode =  SlistBuyNode(data);//申请一个新的节点
	
	if (*ppHead == NULL)//链表为空
	{
		*ppHead = newNode;
		return;
	}
	if ((*ppHead)->_next == NULL)//链表只存在一个节点
	{
		(*ppHead)->_next = newNode;
		return;
	}
	SListNode* cur = *ppHead;
	while (cur->_next)//找到尾节点
	{
		cur = cur->_next;
	}
	cur->_next = newNode;//进行链接
	return;
}
void SListPopBack(SListNode** ppHead)//尾删
{
	assert(*ppHead);
	if (*ppHead == NULL)//链表为空不需要删除
	{
		return;
	}
	if ((*ppHead)->_next == NULL)
	{
		free(*ppHead);//链表只有一个节点
		(*ppHead) = NULL;
		return;
	}
	SListNode* cur = *ppHead;
	SListNode* prev = NULL;

	while (cur->_next)//找到尾结点
	{
		prev = cur;//保存上一个节点
		cur = cur->_next;
	}
	free(cur);//释放尾结点所在的空间
	prev->_next = NULL;//将上一个节点的_next置空
	return;

4. Insertion and deletion at any position

        Due to the limitations of the singly linked list structure, only insertion and deletion after pos are implemented here. If you delete the node after pos, you need to ensure that the node after pos exists, otherwise problems will occur.

void SListInsertAfter(SListNode*pos, SListDatatype data)//任意位置的插入,在pos之后插入
{
	assert(pos);//确保指针不为空
	SListNode* newNode = SlistBuyNode(data);
	SListNode* next = pos->_next;
	pos->_next = newNode;
	newNode->_next = next;
}
void SListErase(SListNode*pos)//任意位置的删除,pox位置之后的删除
{
	assert(pos);//确保节点的有效性
	//如果只有一个节点
	if (pos->_next )//pos节点的下一个节点存在
	{
		SListNode* next = pos->_next;
		SListNode* nextNext = next->_next;
		free(next);//删除节点,重新链接
		pos->_next = nextNext;
	}
}

5. Find the value of the linked list and modify the value of the linked list node

        By traversing the linked list, you can search for the data in the linked list. If you find the value you are looking for, you can modify the value of the node. 

SListNode* SListFind(SListNode* pHead, SListDatatype data)//查找
{
	SListNode* cur = pHead;
	while (cur)
	{
		if (cur->_data == data)
			return cur;
		cur = cur->_next;//迭代向后走
	}
	return NULL;//找不到
}

 

void testSList()
{
	//查找和修改的测试
	SListNode* pHead = NULL;
	SListPushFront(&pHead, 1);
	SListPushFront(&pHead, 2);
	SListPushFront(&pHead, 3);
	SListPushFront(&pHead, 4);
	SListPushFront(&pHead, 5);
	SListPushFront(&pHead, 6);
	SListPrint(pHead);
	SListNode* node = SListFind(pHead, 5);//查找
	if (node)
	{
		//节点的数据
		node->_data = 50;
	}
	SListPrint(pHead);
}

6. Destroy the linked list

void SListDestory(SListNode** ppHead)//销毁
{
	assert(*ppHead);
	//确保指针有效性
	SListNode* cur = *ppHead;
	while (cur)
	{
		SListNode* freeNode = cur;
		cur = cur->_next;
		free(freeNode);

	}
	*ppHead = NULL;
}

 

7. Test the code

void testSListBack()
{
	//尾插尾删的测试代码
	SListNode* pHead = NULL;
	SListPushBack(&pHead, 1);
	SListPushBack(&pHead, 2);
	SListPushBack(&pHead, 3);
	SListPushBack(&pHead, 4);
	SListPushBack(&pHead, 5);
	SListPushBack(&pHead, 6);
	SListPrint(pHead);
	SListPopBack(&pHead);
	SListPopBack(&pHead);
	SListPopBack(&pHead);
	SListPopBack(&pHead);
	SListPopBack(&pHead);
	SListPopBack(&pHead);


}
void testSListFront()
{
	//头插头删的测试代码
	SListNode* pHead = NULL;
	SListPushFront(&pHead, 1);
	SListPushFront(&pHead, 2);
	SListPushFront(&pHead, 3);
	SListPushFront(&pHead, 4);
	SListPushFront(&pHead, 5);
	SListPushFront(&pHead, 6);
	SListPrint(pHead);
	SListPopFront(&pHead);
	SListPopFront(&pHead);
	SListPopFront(&pHead);
	SListPopFront(&pHead);
	SListPopFront(&pHead);
	SListPopFront(&pHead);
}
void testSList()
{
	//查找和修改的测试
	SListNode* pHead = NULL;
	SListPushFront(&pHead, 1);
	SListPushFront(&pHead, 2);
	SListPushFront(&pHead, 3);
	SListPushFront(&pHead, 4);
	SListPushFront(&pHead, 5);
	SListPushFront(&pHead, 6);
	SListPrint(pHead);
	SListNode* node = SListFind(pHead, 5);//查找
	if (node)
	{
		//节点的数据
		node->_data = 50;
	}
	SListPrint(pHead);
}
void TestSList1()
{
	//对在pos节点之后进行插入和删除的测试
	SListNode* pHead = NULL;
	SListPushFront(&pHead, 1);
	SListPushFront(&pHead, 2);
	SListPushFront(&pHead, 3);
	SListPushFront(&pHead, 4);
	SListPushFront(&pHead, 5);
	SListPushFront(&pHead, 6);
	SListPrint(pHead);
	SListNode* node = SListFind(pHead, 5);//查找
	if (node)
	{
		
		//插入节点
		SListInsertAfter(node, -2);
		SListPrint(pHead);

		SListErase(node);
		SListPrint(pHead);

	}
	SListDestory(&pHead);
}

8. All codes

//SList.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<assert.h>
typedef int SListDatatype;
typedef struct SListNode
{
	SListDatatype _data;//存储节点的数据
	struct SListNode* _next;
}SListNode;
SListNode* SlistBuyNode(SListDatatype data);


void SListDestory(SListNode** ppHead);//销毁
void SListPushBack(SListNode**ppHead,SListDatatype data);//尾插
void SListPopBack(SListNode** ppHead );//尾删

void SListPushFront(SListNode** ppHead, SListDatatype data);//头插
void SListPopFront(SListNode** ppHead);//头删

void SListInsertAfter(SListNode* pos, SListDatatype data);//任意位置的插入

void SListErase(SListNode*pos);//任意位置的删除

SListNode* SListFind(SListNode*pHead, SListDatatype data);//查找
void SListPrint(SListNode* pHead);//显示链表数据
//void SListDestory(SListNode** ppHead);//删除链表

 //SList.c

#include"SList.h"

SListNode* SlistBuyNode(SListDatatype data)
{
	 SListNode*newNode = (SListNode*)malloc(sizeof(SListNode));
	 if (newNode == NULL)
	 {
		 //申请节点失败
		 printf("申请节点失败\n");
		 exit(-1);//暴力返回
	 }
	 newNode->_data = data;//给节点赋值
	 newNode->_next = NULL;

	 return newNode;
}

void SListDestory(SListNode** ppHead)//销毁
{
	assert(*ppHead);
	//确保指针有效性
	SListNode* cur = *ppHead;
	while (cur)
	{
		SListNode* freeNode = cur;
		cur = cur->_next;
		free(freeNode);

	}
	*ppHead = NULL;
}
void SListPushBack(SListNode** ppHead, SListDatatype data)//尾插
{
	SListNode*newNode =  SlistBuyNode(data);//申请一个新的节点
	
	if (*ppHead == NULL)//链表为空
	{
		*ppHead = newNode;
		return;
	}
	if ((*ppHead)->_next == NULL)//链表只存在一个节点
	{
		(*ppHead)->_next = newNode;
		return;
	}
	SListNode* cur = *ppHead;
	while (cur->_next)//找到尾节点
	{
		cur = cur->_next;
	}
	cur->_next = newNode;//进行链接
	return;
}
void SListPopBack(SListNode** ppHead)//尾删
{
	assert(*ppHead);
	if (*ppHead == NULL)//链表为空不需要删除
	{
		return;
	}
	if ((*ppHead)->_next == NULL)
	{
		free(*ppHead);//链表只有一个节点
		(*ppHead) = NULL;
		return;
	}
	SListNode* cur = *ppHead;
	SListNode* prev = NULL;

	while (cur->_next)//找到尾结点
	{
		prev = cur;//保存上一个节点
		cur = cur->_next;
	}
	free(cur);//释放尾结点所在的空间
	prev->_next = NULL;//将上一个节点的_next置空
	return;
}
void SListPushFront(SListNode** ppHead, SListDatatype data)//头插
{
	SListNode* newNode = SlistBuyNode(data);//申请一个新的节点
	if (*ppHead == NULL)
	{
		//链表为空
		*ppHead = newNode;
		return;
	}
	newNode->_next = (*ppHead);
	*ppHead = newNode;//对头结点进行链接
}
void SListPopFront(SListNode** ppHead)//头删
{
	assert(*ppHead);//确保指针的有效性
	if ((*ppHead)->_next == NULL)
	{
		//链表只有一个节点
		free(*ppHead);
		*ppHead = NULL;
		return;
	}
	//删除头结点,然后更新头结点
	SListNode* newHead = (*ppHead)->_next;
	free(*ppHead);
	*ppHead = newHead;
	return;
}
void SListInsertAfter(SListNode*pos, SListDatatype data)//任意位置的插入,在pos之后插入
{
	assert(pos);//确保指针不为空
	SListNode* newNode = SlistBuyNode(data);
	SListNode* next = pos->_next;
	pos->_next = newNode;
	newNode->_next = next;
}
void SListErase(SListNode*pos)//任意位置的删除,pox位置之后的删除
{
	assert(pos);//确保节点的有效性
	//如果只有一个节点
	if (pos->_next )//pos节点的下一个节点存在
	{
		SListNode* next = pos->_next;
		SListNode* nextNext = next->_next;
		free(next);//删除节点,重新链接
		pos->_next = nextNext;
	}
}

SListNode* SListFind(SListNode* pHead, SListDatatype data)//查找
{
	SListNode* cur = pHead;
	while (cur)
	{
		if (cur->_data == data)
			return cur;
		cur = cur->_next;//迭代向后走
	}
	return NULL;//找不到
}
void SListPrint(SListNode* pHead)//显示链表数据
{
	assert(pHead);//确保指针的有效性
	SListNode* cur = pHead;
	while (cur)
	{
		printf("%d ", cur->_data);
		printf("->");
		cur = cur->_next;
	}
	printf("NULL\n");
}

//test.c

#include"SList.h"
void testSListBack()
{
	//尾插尾删的测试代码
	SListNode* pHead = NULL;
	SListPushBack(&pHead, 1);
	SListPushBack(&pHead, 2);
	SListPushBack(&pHead, 3);
	SListPushBack(&pHead, 4);
	SListPushBack(&pHead, 5);
	SListPushBack(&pHead, 6);
	SListPrint(pHead);
	SListPopBack(&pHead);
	SListPopBack(&pHead);
	SListPopBack(&pHead);
	SListPopBack(&pHead);
	SListPopBack(&pHead);
	SListPopBack(&pHead);


}
void testSListFront()
{
	//头插头删的测试代码
	SListNode* pHead = NULL;
	SListPushFront(&pHead, 1);
	SListPushFront(&pHead, 2);
	SListPushFront(&pHead, 3);
	SListPushFront(&pHead, 4);
	SListPushFront(&pHead, 5);
	SListPushFront(&pHead, 6);
	SListPrint(pHead);
	SListPopFront(&pHead);
	SListPopFront(&pHead);
	SListPopFront(&pHead);
	SListPopFront(&pHead);
	SListPopFront(&pHead);
	SListPopFront(&pHead);
}
void testSList()
{
	//查找和修改的测试
	SListNode* pHead = NULL;
	SListPushFront(&pHead, 1);
	SListPushFront(&pHead, 2);
	SListPushFront(&pHead, 3);
	SListPushFront(&pHead, 4);
	SListPushFront(&pHead, 5);
	SListPushFront(&pHead, 6);
	SListPrint(pHead);
	SListNode* node = SListFind(pHead, 5);//查找
	if (node)
	{
		//节点的数据
		node->_data = 50;
	}
	SListPrint(pHead);
}
void TestSList1()
{
	//对在pos节点之后进行插入和删除的测试
	SListNode* pHead = NULL;
	SListPushFront(&pHead, 1);
	SListPushFront(&pHead, 2);
	SListPushFront(&pHead, 3);
	SListPushFront(&pHead, 4);
	SListPushFront(&pHead, 5);
	SListPushFront(&pHead, 6);
	SListPrint(pHead);
	SListNode* node = SListFind(pHead, 5);//查找
	if (node)
	{
		
		//插入节点
		SListInsertAfter(node, -2);
		SListPrint(pHead);

		SListErase(node);
		SListPrint(pHead);

	}
	SListDestory(&pHead);
}
int main()
{
	TestSList1();
	return 0;
}

 

9. Summary 

        Differences and connections between linked lists and sequence lists. The sequence table implements addition, deletion, query and modification based on the array. And it can grow dynamically when inserted. Disadvantages of the sequence table: there may be a waste of space, there will be a certain efficiency loss when increasing the capacity, and the time complexity of deleting intermediate or header data is O(n) because the data needs to be moved. These problems are solved by linked lists, but linked lists also have their own flaws, such as random access and memory fragmentation . In fact, no data structure is perfect. They all have their own flaws, and their actual use complements each other.

Guess you like

Origin blog.csdn.net/m0_68641696/article/details/132253402