链表就是许多节点在逻辑上串起来的数据存储方式 是通过结构体中的指针将后续的节点串联起来
typedef int SLTDataType;//数据类型
typedef struct SListNode//节点
{
	SLTDataType data;//存储的数据
	struct SListNode* next;//指向下一个节点地址的指针
}SLTNode;//结构体类型的简化上面的代码只是声明 并没有定义 也就是没有创建结构体变量
SListNode* pc = NULL;//定义结构体下面开始实现链表的各个接口 让链表可以存储和删除数据
链表的打印
void SListPrint(SListNode* phead)
{
	while (phead != NULL)
	{
		printf("%d->", phead->val);
		phead = phead->next;
	}
	printf("NULL");
}链表的插入会向内存申请空间 去创造一个新的节点 所以后续的 头部插入 尾部插入 还是任意位置的插入 都会进行相同的操作 所以这里将开辟节点这个过程单独分装为一个函数 方便后面的操作
static SListNode* CreatNode(DataType x)
{
	SListNode* tmp = (SListNode*)malloc(sizeof(SListNode));
	if (tmp == NULL)
	{
		printf("failed to allocate memory.");
		exit(-1);
	}
	tmp->val = x;
	tmp->next = NULL;
	return tmp;
}
这里使用了static将函数的作用域限制在该函数所在的项目中 在其它项目中无法访问
链表的头部插入
void SListFront(SListNode** phead, DataType x)
{
	SListNode* tmp = CreatNode(x);
	tmp->next = *phead;
	tmp->val = x;
	*phead = tmp;
}
链表的尾部插入
链表的尾部插入相比于头部插入就显得不是那么好写
因为你要考虑 链表中是否有数据 也就是*phead是否为空 并且还要考虑当链表不为空时 尾插该怎样插入 我们可以使用if语句进行判断
void SListBackPush(SListNode** phead, DataType x)
{
	SListNode* newnode = CreatNode(x);
	if (*phead == NULL)
	{
		*phead = newnode;
	}
	else
	{
		SListNode* tail = *phead;//定义一个变量去向后寻找尾部
		while (tail->next != NULL)
		{
			tail = tail->next;
		}
		tail->next = newnode;
	}
}
任意位置的插入(从1位置向后插 超出范围 将该节点插入到尾部)
当链表有数据时 尾部插入和头部插入是一样的流程 用一个临时变量tmp 向后寻找pos位置 再将该位置的下一个节点的地址给 要插入节点的指针 最后将要插入节点的地址给前一个节点的指针 这样就将该节点插进去了
但是头部插入不太一样 因为头部之前没有节点了 就只能将头节点先给要插入的节点 再将要插入的节点作为头部 这样就完成了头部插入
void SListPosPush(SListNode** phead, DataType x , int pos)
{
	SListNode* newnode = CreatNode(x);
	if (pos == 1)
	{
		newnode->next = *phead;
		*phead = newnode;
	}
	else
	{
		SListNode* tmp = *phead;
		//如果所给的pos位置超出了链表的长度就将其插入到尾部
		while (tmp->next!=NULL && pos>2)
		{
			tmp = tmp->next;
			--pos;
		}
		newnode->next = tmp->next;
		tmp->next = newnode;
	}
}运行结果如下 以上就是插入接口函数的实现

删除的相关节点
头删
void SListPopFront(SListNode** phead)
{
	assert(*phead);
	SListNode* tmp = *phead;
	*phead = (*phead)->next;
	free(tmp);
	tmp = NULL;
}尾删
尾删和头删不一样 因为当链表只有一个节点时 尾删就变成了头删 这样就和有多个数据时 尾部删除不一样 这就分情况 讨论
void SListPopBack(SListNode** phead)
{
	assert(*phead);
	if ((*phead)->next == NULL)
	{
		free(*phead);
		*phead = NULL;
	}
	else
	{
		//找尾
		SListNode* tmp = *phead;//防止头节点丢失
		SListNode* str = *phead;
		while (tmp->next != NULL)
		{
			str = tmp;
			tmp = tmp->next;
		}
		free(tmp);
		tmp = NULL;
		str->next = NULL;
	}
}
上面是两个指针配合删除 下面是一个指针去完成
void SListPopBack(SListNode** phead)
{
	assert(*phead);
	if ((*phead)->next == NULL)
	{
		free(*phead);
		*phead = NULL;
	}
	else
	{
		//找尾
		SListNode* tmp = *phead;//防止头节点丢失
		SListNode* str = *phead;
		while (tmp->next->next!=NULL)
		{
			tmp = tmp->next;
		}
		free(tmp->next);
		tmp->next = NULL;
	}
}链表pos位置删除
void SListPopPos(SListNode** phead, int pos)
{
	assert(*phead);//空链表就不用删除了
	if (pos == 1)
	{
		SListNode* tmp = *phead;
		*phead = (*phead)->next;
		free(tmp);
		tmp = NULL;
	}
	else
	{
		SListNode* tmp = *phead;
		SListNode* str = tmp;
		while (pos > 1 && tmp->next != NULL)
		{
			str = tmp;
			tmp = tmp->next;
			--pos;
		}
		//找到pos位置之后将 pos的下一个节点的地址给pos前面的节点的指针
		//再将pos所在的节点进行释放
		str->next = tmp->next;
		free(tmp);
		tmp = NULL;
	}
}
对链表的查找 修改 其实就是将链表遍历一遍 上面的寻找尾部 打印链表就是遍历链表 所以大家可以将其修改修改
对单链表就介绍到这里 后续还有双向链表
带头结点的单链表 没动态开辟头节点 只是定义了一个 结构体变量 用来存储后续插入的节点 用一级指针去传参 你也可以动态开辟头节点 传二级指针 这里只是说 带头结点的很好实现链表的增删查改
头文件 函数各个接口
#pragma once
#include<stdio.h>
#include<stdlib.h>
typedef int DataType;
typedef struct SList
{
	DataType val;
	struct SList* next;
}SListNode;
//单链表打印
void SListPrint(SListNode* pc);
//单链表的尾部插入
void SListPushBack(SListNode* pc, DataType x);
//链表的头部插入
void SListPushFront(SListNode* pc, DataType x);
//单链表头删
void SListPopFront(SListNode* pc);
//单链表尾删
void SListPopBack(SListNode* pc);
//删除pos位置
void SListPopPos(SListNode* pc, int pos);
//pos之后插入
void SListPushPos(SListNode* pc, int pos, DataType x);
//链表的查找
void SListFind(SListNode* pc, DataType x);
//链表的销毁
void SListDestroy(SListNode* pc);函数实现 接口实现
#define _CRT_SECURE_NO_WARNINGS 1
#include"SList.h"
static SListNode* CreatNode(DataType x)
{
	SListNode* tmp = (SListNode*)malloc(sizeof(SListNode));
	if (tmp == NULL)
	{
		printf("Failed to allocate SListNode.");
		exit(-1);
	}
	tmp->next = NULL;
	tmp->val = x;
	return tmp;
}
void SListPrint(SListNode* pc)
{
	SListNode* tmp = pc->next;
	while (tmp)
	{
		printf("%d->", tmp->val);
		tmp = tmp->next;
	}
	printf("NULL\n");
}
void SListPushBack(SListNode* pc, DataType x)
{
	while (pc->next != NULL)
	{
		pc = pc->next;
	}
	pc->next = CreatNode(x);
}
void SListPushFront(SListNode* pc, DataType x)
{
	SListNode* tmp = pc->next;
	pc->next = CreatNode(x);
	pc->next->next = tmp;
}
void SListPopFront(SListNode* pc)
{
	SListNode* tmp = pc->next;
	pc->next = pc->next->next;
	free(tmp);
	tmp = NULL;
}
void SListPopBack(SListNode* pc)
{
	SListNode* tmp = pc,*cur = pc;
	while (cur->next != NULL)
	{
		tmp = cur;
		cur = cur->next;
	}
	if (cur != pc)//不释放哨兵卫
	{
		free(cur);
		cur = NULL;
	}
	tmp->next = NULL;
}
void SListPopPos(SListNode* pc, int pos)
{
	SListNode* tmp = pc->next,*str = pc;
	while (tmp->next != NULL&&pos>1)
	{
		str = tmp;
		tmp = tmp->next;
		--pos;
	}
	if (pos != 1)
	{
		printf("pos位置超出链表范围.");
		return;
	}
	str->next = tmp->next;
	free(tmp);
	tmp = NULL;
}
void SListPushPos(SListNode* pc, int pos, DataType x)
{
	SListNode* newnode = CreatNode(x);
	SListNode* tmp = pc;
	while (tmp->next != NULL&&pos>1)
	{
		tmp = tmp->next;
		--pos;
	}
	if (pos != 1)
	{
		printf("要插入的位置超出了链表的长度.\n");
		return;
	}
	newnode->next = tmp->next;
	tmp->next = newnode;
}
void SListFind(SListNode* pc, DataType x)
{
	SListNode* tmp = pc->next;
	int pos = 0;
	while (tmp)
	{
		pos++;
		if (tmp->val == x)
		{
			printf("找到了该元素在第%d个\n", pos);
			return;
		}
		tmp = tmp->next;
	}
	printf("没有找到.\n");
}
void SListDestroy(SListNode* pc)
{
	//所有申请的空间都要释放
	SListNode* tmp = pc->next;
	pc = pc->next->next;
	while (pc)
	{
		free(tmp);
		tmp = NULL;
		tmp = pc;
		pc = pc->next;
	}
}测试文件
#define _CRT_SECURE_NO_WARNINGS 1
#include"SList.h"
void test1()
{
	//不是哨兵卫 就是一个结构体变量
	SListNode pc;
	pc.next = NULL;
	SListPushBack(&pc, 1);
	SListPushBack(&pc, 2);
	SListPushBack(&pc, 3);
	SListPushFront(&pc, -1);
	SListPushFront(&pc, -2);
	SListPopFront(&pc);
	SListPopBack(&pc);
	SListPushPos(&pc, 1, 5);
	SListPushPos(&pc, 5, 6);
	SListPopPos(&pc, 1);
	SListFind(&pc, 6);
	SListPrint(&pc);
	SListDestroy(&pc);
}
int main()
{
	test1();
	return 0;
}

















