c语言单链表的实现(增,删,改,查等功能)

该代码实现了一个简单的单链表数据结构,包括插入、删除、查找、打印等操作。函数库包括头插、尾插、头删、尾删、查找、插入、删除等方法,并提供了测试用例进行验证。代码中强调了在操作链表头节点时需要使用二级指针的原因。
摘要由CSDN通过智能技术生成

SList.h结构体函数声明文件

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

typedef int SLDateType;

//单链表  一个表中有两个属性,值和下一个链表的地址
typedef struct SListNode
{
	SLDateType data;
    struct SLDateType* next;//类型为结构体目的是为下一个链表储存地址

}SLTNode;

void SListPrint(SLTNode* phead);//打印单链表

void SlistPushBack(SLTNode** phead, SLDateType x);//尾插

void SlistPushFront(SLTNode** phead, SLDateType x);//头插

SLTNode* BuyListNode(SLDateType x);//创建新的空间,存储新的链表

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

void SListPopFront(SLTNode** pphead);//头删

SLTNode* SListFind(SLTNode* phead, SLDateType x);//查找,返回地址

void SListInsert(SLTNode** pphead, SLTNode* pos, SLDateType x);//在pos前面插入

void SListInsertAfter(SLTNode* pos, SLDateType x);//在pos之后插入


SLTNode* removeElements(SLTNode* head, SLDateType x);//指定删除一样的元素

void SListErase(SLTNode** pphead, SLTNode* pos);//指定位置删除

void SListEraseAfter(SLTNode* pos);//删除指定位置的后面一个

void SLisDestory(SLTNode** pphead);//摧毁链表

SList.c函数实现文件

#include"SList.h"

void SListPrint(SLTNode* phead)//打印单链表,传入首节点的地址,只是打印不用二级指针
{
	SLTNode* cur = phead;   //把第一个链表的地址传给首指针   cur代表了第一个节点的地址
	while (cur != NULL)
	{
		printf("%d->", cur->data);
		cur = cur->next;  //next存储的是下一个节点的地址
	}
	printf("NULL\n");
}

SLTNode* BuyListNode(SLDateType x)//创建新的空间存储链表
{

	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));//用malloc函数开辟一片空间在堆上,不在栈上,返回一个地址,
	newnode->data = x;
	newnode->next = NULL;  //以NULL作为结束的标志
	if (newnode == NULL)
	{
		printf("malloc fail\n");
		exit(-1);
	}

	return newnode;
}
/*先前理解 首先,你创建的是结构体指针,你要改变结构体指针,就要把结构体指针的地址传进来,所以要用二级指针
其次你要为结构体里面的指针创建空间,本题为结构体指针,所以先判断,先给结构体一个空间,然后判断如果
结构体有空间,就给结构体里面的结构指针空间,依次递归*/

/*2023.1.12理解  关于尾删可以传一级指针,但是我们要操作头节点时还要传二级指针,所以尾删还要传二级指针的理解。
传二级指针是因为首先我们用malloc创建了一片空间(一个结构体的地址),
然后呢我们要把malloc创建的空间给我们创建的结构体指针,这时我们要区分一个东西,
整个结构体的地址,和结构体里面成员的地址,首先我们让头节点结构体指针置为NULL,
然后要改变头节点结构体地址,
所以要传二级指针,二级指针存储的是头节点结构体地址,然后我们要尾删时要注意,
我们创建的结构体里面包含下一个结构体的地址,
所以要改变下一个结构体的地址,我们可以用一级指针来改变*/


void SlistPushBack(SLTNode** pphead, SLDateType x)  //因为要改变传入指针的值,所以要用二级指针,pphead是局部变量
{
	SLTNode* newnode = BuyListNode(x);

	if (*pphead == NULL)  //*pphead代表 SLTNode* pphead 为一个地址。首先要给传入的指针一个地址
	{
		*pphead = newnode;
	}
	else    //代表已经初始化过传入的指针
	{
		SLTNode* tail = *pphead; 
		while(tail->next!=NULL)
		{
			tail = tail->next;  //把结构体中的下一个链表的地址给tail,找到最后一个链表

		}
		tail->next = newnode;  //把新创建的链表链接
	}
}


void SlistPushFront(SLTNode** phead, SLDateType x)//头插
{
	SLTNode* newnode = BuyListNode(x);

	newnode->next = *phead;
	*phead = newnode;
}


void SListPopBack(SLTNode** pphead)//尾删
{
	//温柔的
	if (*pphead == NULL)//先判断链表为空
	{
		return;
	}
	//粗暴的
	//assert(*pphead != NULL);

	if ((*pphead)->next == NULL)//链表只有一个时,按照普通的删会有野指针
	{
		free(*pphead);
		*pphead = NULL;
	}
	else//链表有2个及2个以上时
	{
		SLTNode* prev = NULL;
		SLTNode* tail = *pphead;//把传入的头指针所储存的第一个链表的地址给tail存储

		while (tail->next)
		{
			prev = tail;
			tail = tail->next;
		}
		free(tail);
		tail = NULL;

		prev->next = NULL;
	}
}

void SListPopFront(SLTNode** pphead)//头删
{
	if (*pphead == NULL)//判断链表是否为空
	{
		return;
	}

	SLTNode* next = (*pphead)->next;//不用判断链表只有一个,因为如果只有一个的话  (*pphead)->next 为NULL next=NULL
	free(*pphead);
	*pphead=next;
}

SLTNode* SListFind(SLTNode* phead, SLDateType x)
{
	SLTNode* cur = phead;
	while (cur)
	{
		if (cur->data == x)
		{
			return cur;
		}
		else
		{
			cur = cur->next;
		}
	}
	return NULL;
}


void SListInsert(SLTNode** pphead, SLTNode* pos, SLDateType x)//插入

{
	SLTNode* newnode = BuyListNode(x);
	if(*pphead==pos)
	{
		newnode->next = *pphead;
		*pphead = newnode;
	}
	else
	{
		SLTNode* posPrev = *pphead;
		while (posPrev->next != pos)
		{
			posPrev = posPrev->next;
		}
		posPrev->next = newnode;
		newnode->next = pos;
	}
	
}



void SListInsertAfter(SLTNode* pos, SLDateType x)//在pos之后插入
{
	SLTNode* newnode = BuyListNode(x);

	SLTNode* posAfter = pos->next;
	pos->next = newnode;
	newnode->next = posAfter;
}






SLTNode* removeElements(SLTNode** head, SLDateType x)//oj题
{
	SLTNode* prev = NULL;
	SLTNode* cur = *head;

	while (cur)
	{
		if (cur->data==x)
		{
			//1.头删
			//2.中间删
			if (cur == *head)
			{
				*head = cur->next;
				free(cur);
				cur = *head;
			}
			else
			{
				prev->next = cur->next;
				free(cur);
				cur = prev->next;
			}
		}
		else
		{
			prev = cur;
			cur = cur->next;
		}
	}
	return head;
}


void SListErase(SLTNode** pphead, SLTNode* pos)//删除指定元素
{
	if (*pphead == pos)
	{
		*pphead = pos->next;
		free(pos);
	}
	else
	{
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		prev->next = pos->next;
		free(pos);
		pos = NULL; 
	}
}


void SListEraseAfter(SLTNode* pos)//删除指定元素的后一个
{
	SLTNode* next = pos->next;
	pos->next = next->next;
	free(next);
}


void SLisDestory(SLTNode** pphead)
{
	SLTNode* cur = *pphead;
	while (cur)
	{
		SLTNode* next = cur->next;
		free(cur);
		cur = next;
	}
	*pphead = NULL;
}

test.c测试函数文件

#include"SList.h"

void test1()
{
	SLTNode* plist = NULL;
	SlistPushBack(&plist, 1);
	SlistPushBack(&plist, 2);
	SlistPushBack(&plist, 3);
	SlistPushBack(&plist, 4);

	SListPrint(plist);

	SlistPushFront(&plist, 1);
	SlistPushFront(&plist, 2);
	SlistPushFront(&plist, 3);
	SlistPushFront(&plist, 4);

	SListPrint(plist);


	SListPopBack(&plist);
	SListPopBack(&plist);
	SListPopBack(&plist);
	SListPopBack(&plist);

	SListPrint(plist);
}

void test2()
{
	SLTNode* plist = NULL;
	SlistPushBack(&plist, 1);
	SlistPushBack(&plist, 2);
	SlistPushBack(&plist, 3);
	SlistPushBack(&plist, 2);
	SlistPushBack(&plist, 4);
	SListPrint(plist);
	//多值查找,还可以修改
	SLTNode* pos=SListFind(plist,2);
	int i = 1;
	while (pos)
	{
		printf("第%d个pos节点:%p->%d\n",i++,pos,pos->data);
		pos = SListFind(pos->next, 2);
	}
	//修改
	pos = SListFind(plist, 3);
	if (pos)
	{
		pos->data = 30;
	}
	SListPrint(plist);

}

void test3()
{
	SLTNode* plist = NULL;
	SlistPushBack(&plist, 1);
	SlistPushBack(&plist, 2);
	SlistPushBack(&plist, 3);
	SlistPushBack(&plist, 4);
	SlistPushBack(&plist, 5);
	SListPrint(plist);

	SLTNode* pos = SListFind(plist, 3);//在3的前面插入
	if (pos)
	{
		SListInsert(&plist, pos, 30);
	}


	
}


void test4()
{
	SLTNode* plist = NULL;
	SlistPushBack(&plist, 1);
	SlistPushBack(&plist, 2);
	SlistPushBack(&plist, 3);
	SlistPushBack(&plist, 4);
	SlistPushBack(&plist, 5);
	SListPrint(plist);

	SLTNode* pos = SListFind(plist, 5);
	if (pos)
	{
		SListInsertAfter(pos, 30);
	}
	SListPrint(plist);
}

void test5()
{

	SLTNode* plist = NULL;
	SlistPushBack(&plist, 2);
	SlistPushBack(&plist, 5);
	SlistPushBack(&plist, 2);
	SlistPushBack(&plist, 3);
	SlistPushBack(&plist, 5);
	SlistPushBack(&plist, 4);
	SlistPushBack(&plist, 5);

	SListPrint(plist);
    removeElements(&plist, 2);
	SListPrint(plist);

}


void test6()
{
	SLTNode* plist = NULL;
	SlistPushBack(&plist, 1);
	SlistPushBack(&plist, 2);
	SlistPushBack(&plist, 3);
	SlistPushBack(&plist, 4);
	SlistPushBack(&plist, 5);
	SListPrint(plist);

	SLisDestory(&plist);
	SListPrint(plist);
}
int main()
{
	//test1();

	//test2();

	//test3();

	//test4();

	test5();

	//test6();

	return 0;
}

总结:本代码为没有哨兵位的单链表所以要传二级指针,只要遇到要操作第一个节点都要传二级指针,还要注意在增删情况下,大部分要考虑链表为空,链表只有一个节点的情况,在学习链表的时候,要多画图。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值