数据结构-单链表的基本实现(C语言,简单易懂,含全部代码)

链表的概念和结构

概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。

结构:实际中链表的结构非常多样,以下情况组合起来就有8种链表结构。

(1)单向、双向
(2)带头、不带头
(3)循环、非循环

本篇主要详解单链表,结构如图:
在这里插入图片描述
无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结
构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。

链表的实现

首先在头文件中定义结构体和提供接口:

#include<stdio.h>
#include<stdlib.h>
typedef int SLTDataType;

typedef struct SListNode//定义结构体
{
	SLTDataType data;
	struct SListNode* next;
}SLTNode;

//单向+不带头+非循环 
//凡是要改变指针内容plist的值时,就用二级指针接收
void SListPrint(SLTNode* plist);//打印

void SListPushBack(SLTNode** plist, SLTDataType x);//尾插

void SListPushFront(SLTNode** plist, SLTDataType x);//头插

void SListPopBack(SLTNode** pplist);//尾删

void SListPopForint(SLTNode** pplist);//头删

SLTNode* SListFind(SLTNode* plist, SLTDataType x);//查找

void SListInsertAfter(SLTNode* pos, SLTDataType x);//pos位置后插入x

void SListInsertBefore(SLTNode* plist, SLTNode* pos, SLTDataType x);//pos位置前插入x(很麻烦 不适合)

void SListEraseAfter(SLTNode* pos);//删除pos后的数据

其次在源文件中实现接口功能:
(1)单链表打印

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

(2)单链表尾插

//需要改变plist的内容(NULL),所以用二级指针
void SListPushBack(SLTNode** pplist, SLTDataType x)
{
	SLTNode* newnode = BuySLTNode(x);

	if (*pplist == NULL)//1.空
	{
		*pplist = newnode;
	}
	else找尾 2.非空
	{
		SLTNode* tail = *pplist;
		while (tail->next != NULL)
		{
			tail = tail->next;
		}
		tail->next = newnode;
	}
}

(3)单链表头插

//头插 空和非空都能解决
void SListPushFront(SLTNode** pplist, SLTDataType x)
{
	SLTNode* newnode = BuySLTNode(x);
	newnode->next = *pplist;
	*pplist = newnode;
}

(4)单链表尾删

void SListPopBack(SLTNode** pplist)
{
	if (*pplist == NULL)//1.没有节点,plist内容为NULL
	{
		return;
	}
	else if((*pplist)->next==NULL)//2.一个节点
	{
		free(*pplist);
		*pplist = NULL;
	}
	else//3.多个节点
	{
		SLTNode* prev = NULL;
		SLTNode* tail = *pplist;
		while (tail->next != NULL)
		{
			prev = tail;
			tail = tail->next;
		}
		free(tail);
		tail = NULL;//局部变量 这条代码有没有无所谓,养成置空好习惯
		prev->next = NULL;
	}
}

(5)单链表头删

void SListPopForint(SLTNode** pplist)
{
	if (*pplist == NULL)//无节点
	{
		return;
	}
	else//同时满足一个节点和多个节点
	{
		SLTNode* next = (*pplist)->next;
		free(*pplist);
		*pplist = next;
	}
}

(6)单链表查找

SLTNode* SListFind(SLTNode* plist, SLTDataType x)
{
	SLTNode* cur = plist;
	while (cur)//相当于while(cur != NULL)
	{
		if (cur->data == x)
		{
			return x;
		}
		else
		{
			cur = cur->next;
		}
		return NULL;//遍历一遍都没找到
	}
}

(7)单链表在pos位置后插入x

void SListInsertAfter(SLTNode* pos, SLTDataType x)
{
	assert(pos);
	SLTNode* newnode = Buynode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}

(8)单链表在pos位置前插入x

void SListInsertBefore(SLTNode** pplist, SLTNode* pos, SLTDataType x)
{
	assert(pos);
	SLTNode* newnode = BuySLTNode(x);

	if (pos == *pplist)//相当于头插,仅一个节点
	{
		newnode->next = pos;
		*pplist = newnode;
	}
	else//多个节点
	{
		SLTNode* prev = NULL;
		SLTNode* cur = *pplist;
		while (cur != pos)
		{
			prev = cur;
			cur = cur->next;
		}
		prev->next = newnode;
		newnode->next = pos;
	}
}

(9)删除pos后的数据

void SListEraseAfter(SLTNode* pos)
{
	assert(pos);
	if (pos->next == NULL)
	{
		return;
	}
	else
	{
		SLTNode* next = pos->next;
		pos->next = next->next;
		free(next);
		next = NULL;
	}
}

主函数的设计大家可以自由发挥,做个简单的测试功能调用函数或是创建菜单栏实现交互都可以。我水平有限,请朋友们谅解!写的不好的地方还请大佬们指出。最后,如果这篇文章对你有帮助,就点个赞或者收藏评论一下吧,谢谢大家支持😁。

下期预告——结构最复杂的带头双向循环链表