【数据结构】单链表的简单实现

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
  • 一、单链表的定义
  • 二、链表中数据元素的构成
  • 三、链表的基本操作
  • 四、单链表的功能实现
    • 4.1 打印单链表
    • 4.2、销毁链表
    • 4.3、创建新结点
    • 4.4、单链表尾插
    • 4.5、单链表头插
    • 4.6、单链表尾删
    • 4.7、单链表的头删
    • 4.8、单链表数据查找
    • 4.9、在pos前面插入
    • 4.10、删除链表pos位置
    • 4.11、在链表pos后面插入
    • 4.12、在链表pos后面删除
  • 总结


前言

一、单链表的定义

单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素。链表中的数据是以结点来表示的,每个结点的构成:元素(数据元素的映象) + 指针(指示后继元素存储位置),元素就是存储数据的存储单元,指针就是连接每个结点的地址数据。

【数据结构】单链表的简单实现_第1张图片

二、链表中数据元素的构成

每个元素本身由两部分组成:
1、本身的信息,称为"数据域"
2、指向直接后继的指针,称为"指针域"

【数据结构】单链表的简单实现_第2张图片

三、链表的基本操作

SeqList.h头文件代码如下

typedef int SLDatetype;
typedef struct SListNode
{
    SLDatetype val;//数据域
    struct SListNode* next;//指针域
}SListNode;

//打印链表
void SLTPrint(SListNode*phead);
//销毁链表
void SLTDestroy(SLNode** pphead);
//尾插
void SLTPushBack(SListNode** pphead,SLDatetype x);

//头插
void SLTPushFront(SListNode** pphead,SLDatetype x);

//尾删
void SLTPopBack(SListNode** pphead);

//头删
void SLTPopFront(SListNode** pphead);

//查找
SListNode*SLTFind(SListNode* plist,SLDatetype x);

//在pos前面插入
void SLTInsert(SListNode** pphead, SListNode* pos, SLDatetype x);
//在pos前面删除
void SLTErase(SListNode** pphead,SListNode* pos);

//在pos后面位置插入
void* SLTInsertAfter(SListNode* pos,SLDatetype x);
//在pos后面删除
void* SLTEraseAfter(,SListNode* pos)



四、单链表的功能实现

4.1 打印单链表

链表打印不需要断言的,链表和顺序表不同,当链表中没有元素时,链表为空链表,头指针指向的就是NULL了,所以断言就会出问题。

void SLTPrint(SListNode*phead)
{
	SListNode* tail=phead;
	//遍历链表
	while(tail->next!=NULL)
	{
		printf("%d->",tail->val);
		//同时更新tail
		tail=tail->next;
	}
	printf("NULL");
}

4.2、销毁链表

注意:销毁操作不允许直接free(phead),因为链表在物理结构上不是连续存放的,必须要一个结点一个结点去释放

void SLTDestroy(SLNode** pphead)
{
	//断言,防止传空指针
	assert(pphead);
	SLNode* cur = *pphead;
	//空链表不进循环
	while (cur)
	{
		SLNode* next = cur->next;
		//释放每一个结点
		free(cur);
		cur = next;
	}
	//最后记得一定要置空
	*pphead = NULL;
}

4.3、创建新结点

因为我们后面插入部分都需要创建结点,所以我们专门设计一个创建结点的功能函数,既方便又减少了重复代码。

SListNode* CreaetNode(SLDatetype x)
{
	SListNode* newnode=(SListNode*)malloc(sizeof(SListNode));
	//判断malloc是否开辟成功结点
	if(newnode==NULL)
	{
		//开辟失败直接退出程序
		printf("malloc fail");
		exit(-1);
	}
	newnode->val=x;
	newnode->next=NULL;
	return newnode;
}

4.4、单链表尾插

思想

【数据结构】单链表的简单实现_第3张图片

void SLTPushBack(SListNode**pphead,SLDatetype x)
{
	//断言,防止传空指针
	assert(pphead);
	//创建结点
	SListNode*newnode=CreatNode(x);
	if(*pphead==NULL)
	{
		*pphead=newnode;
	}
	else
	{
		SListNode*tail=*pphead;
		//找尾,指针为空不进循环
		while(tail->next!=NULL)
		{
			//更新tail
			tail=tail->next;
		}
		//指向新的结点
		tail->next=newnode;
	}
}

测试及测试结果
尾插

void Test1()
{
	SListNode*plist=NULL;
	SLTPushBack(&plist, 1);
	SLTPushBack(&plist, 2);
	SLTPushBack(&plist, 3);
	SLTPushBack(&plist, 4);
	SLTPrint(plist);
}

在这里插入图片描述

4.5、单链表头插

思想

【数据结构】单链表的简单实现_第4张图片

void SLTPushFront(SListNode** pphead, SLDatetype x)
{
	//断言,防止传过来的是空指针
	assert(pphead);
	SListNode*newnode=CreatNode(x);
	newnode->next=*pphead;
	*pphead=newnode;
}

测试及测试结果
头插

void Test2()
{
	SListNode* plist = NULL;
	SLTPushFront(&plist, 1);
	SLTPushFront(&plist, 2);
	SLTPushFront(&plist, 3);
	SLTPushFront(&plist, 4);
	SLTPrint(plist);
}

在这里插入图片描述

4.6、单链表尾删

思想

【数据结构】单链表的简单实现_第5张图片

void SLTPopBack(SListNode** pphead)
{
	//断言,防止传过来空指针
	assert(pphead);
	//暴力检查法
	assert(*pphead);
	//一个结点
	if((*pphead)->==NULL)
	{
		free(*pphead);
		*pphead=NULL;
	}
	//多个结点
	else
	{
		//双指针,找尾
		SListNode*prve=NULL;
		SListNode*tail=*pphead;
		while(tail->next==NULL)
		{
			//tail始终在prev的前面
			prev=tail;
			//更新tail
			tail=tail->next;
		}
		//释放空间
		free(tail);
		//置空
		prve->next=NULL;
	}
}

测试及测试结果
我们直接在Test1的基础上测试尾删

void Test1()
{
	SListNode* plist = NULL;
	SLTPushBack(&plist, 1);
	SLTPushBack(&plist, 2);
	SLTPushBack(&plist, 3);
	SLTPushBack(&plist, 4);
	SLTPrint(plist);//打印1、2、3、4
	
	SLTPopBack(&plist);//尾删一个
	SLTPrint(plist);//打印1、2 、3

	SLTPopBack(&plist);//尾删一个
	SLTPrint(plist);//1、 2

	SLTPopBack(&plist);//再删后面一个
	SLTPrint(plist);//1

	SLTPopBack(&plist);//删空了
	SLTPrint(plist);//打印NULL
}

【数据结构】单链表的简单实现_第6张图片

4.7、单链表的头删

思想

【数据结构】单链表的简单实现_第7张图片

void SLTPopFront(SListNode** pphead)
{
	assert(pphead);
	//多个结点
	SListNode*tmp=*pphead;
	*pphead=(*pphead)->next;
	//释放指向的空间
	free(tmp);
}

测试

void TestSLT2()
{
	SListNode* plist = NULL;
	SLTPushFront(&plist, 1);
	SLTPushFront(&plist, 2);
	SLTPushFront(&plist, 3);
	SLTPushFront(&plist, 4);
	SLTPrint(plist);//头插打印4->3->2->1->NULL

	SLTPopFront(&plist);//头删一个
	SLTPrint(plist);//3->2->1->NULL
}

4.8、单链表数据查找

从链表头结点开始遍历,依次对比链表中数据域的值是否等于x,等于返回该节点指针,若链表中找不到、不存在就返回空并退出程序。

SListNode* SLTFind(SListNode* phead, SLDateType x)
{
	//断言,防止传空指针
	assert(phead);
	SListNode* cur=phead;
	//遍历
	while (cur)
	{
		if (cur->val == x)
		{
			return cur;
		}
		else
		{
			cur=cur->next;
		}
	}
	return NULL;
}

4.9、在pos前面插入

思想

【数据结构】单链表的简单实现_第8张图片

void SLTInsert(SListNode**pphead,SListNode*pos,SLDatetype x)
{
	assert(pphead);
	assert(*pphead);
	assert(pos);
	if(*pphead==pos)
	{
		//头插
		SLTPushFront(pphead,x);
	}
	else
	{
		SListNode*prev=*pphead;
		while(prev->next!=pos)
		{
			prev=prev->next;
		}
		SListNode*newnode=CreatNode(x);
		prve->next=newnode;
		newnode->next=pos;
	}
}

测试及测试结果

void TestSLT3()
{
	SListNode* plist = NULL;
	SLTPushFront(&plist, 1);
	SLTPushFront(&plist, 2);
	SLTPushFront(&plist, 3);
	SLTPushFront(&plist, 4);
	SLTPrint(plist);

	SListNode* pos = SLTFind(plist, 4);
	SLTInsert(&plist, pos, 40);
	SLTPrint(plist);
	
	pos = SLTFind(plist, 2);
	SLTInsert(&plist, pos, 20);
}

【数据结构】单链表的简单实现_第9张图片


4.10、删除链表pos位置

思想

【数据结构】单链表的简单实现_第10张图片

void SLTErase(SListNode** pphead, SListNode* pos)
{
	assert(pphead);
	assert(*pphead);
	assert(pos);
	if(*pphead==pos)
	{
		//头删
		SLTPopFront(pphead);
	}
	else
	{
		SListNode*prve=*pphead;
		while(prev->next=pos)
		{
			prev=prev->next;
		}
		prev->next=pos->next;
		free(pos);
		pos=NULL;
	}
}

测试及测试结果

void TestSLT4()
{
	SLNode* plist = NULL;
	//插入数据
	SLTPushBack(&plist, 1);
	SLTPushBack(&plist, 2);
	SLTPushBack(&plist, 3);
	SLTPushBack(&plist, 4);
	SLTPrint(plist);

	SLNode* pos = SLTFind(plist, 1);
	SLTErase(&plist, pos);
	SLTPrint(plist);
	
	pos = SLTFind(plist, 3);
	SLTErase(&plist, pos);
	SLTPrint(plist);
}

【数据结构】单链表的简单实现_第11张图片

4.11、在链表pos后面插入

【数据结构】单链表的简单实现_第12张图片

void SLTInsertAfter(SListNode* pos, SLDateytype x)
{
	//断言,空指针NULL不能指向下一个结点
	assert(pos);
	SListNode* newnode = CreatNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}

4.12、在链表pos后面删除

void SLTEraseAfter(SListNode* pos)
{
	assert(pos);
	//断言,如果pos是最后一个结点,就不用再删除了
	assert(pos->next);

	SListNode* tmp = pos->next;
	pos->next = pos->next->next;

	free(tmp);
	tmp = NULL;
}

总结

//打印
void SLTPrint(SListNode*phead)
{
	SListNode* tail=phead;
	//遍历链表
	while(tail->next!=NULL)
	{
		printf("%d->",tail->val);
		//同时更新tail
		tail=tail->next;
	}
	printf("NULL");
}
//销毁
void SLTDestroy(SLNode** pphead)
{
	//断言,防止传空指针
	assert(pphead);
	SLNode* cur = *pphead;
	//空链表不进循环
	while (cur)
	{
		SLNode* next = cur->next;
		//释放每一个结点
		free(cur);
		cur = next;
	}
	//最后记得一定要置空
	*pphead = NULL;
}
//创建结点
SListNode* CreaetNode(SLDatetype x)
{
	SListNode* newnode=(SListNode*)malloc(sizeof(SListNode));
	//判断malloc是否开辟成功结点
	if(newnode==NULL)
	{
		//开辟失败直接退出程序
		printf("malloc fail");
		exit(-1);
	}
	newnode->val=x;
	newnode->next=NULL;
	return newnode;
}
//尾插
void SLTPushBack(SListNode**pphead,SLDatetype x)
{
	//断言,防止传空指针
	assert(pphead);
	//创建结点
	SListNode*newnode=CreatNode(x);
	if(*pphead==NULL)
	{
		*pphead=newnode;
	}
	else
	{
		SListNode*tail=*pphead;
		//找尾,指针为空不进循环
		while(tail->next!=NULL)
		{
			//更新tail
			tail=tail->next;
		}
		//指向新的结点
		tail->next=newnode;
	}
}
//头插
void SLTPushFront(SListNode** pphead, SLDatetype x)
{
	//断言,防止传过来的是空指针
	assert(pphead);
	SListNode*newnode=CreatNode(x);
	newnode->next=*pphead;
	*pphead=newnode;
}
//尾删
void SLTPopBack(SListNode** pphead)
{
	//断言,防止传过来空指针
	assert(pphead);
	//暴力检查法
	assert(*pphead);
	//一个结点
	if((*pphead)->==NULL)
	{
		free(*pphead);
		*pphead=NULL;
	}
	//多个结点
	else
	{
		//双指针,找尾
		SListNode*prve=NULL;
		SListNode*tail=*pphead;
		while(tail->next==NULL)
		{
			//tail始终在prev的前面
			prev=tail;
			//更新tail
			tail=tail->next;
		}
		//释放空间
		free(tail);
		//置空
		prve->next=NULL;
	}
}
//头删
void SLTPopFront(SListNode** pphead)
{
	assert(pphead);
	//多个结点
	SListNode*tmp=*pphead;
	*pphead=(*pphead)->next;
	//释放指向的空间
	free(tmp);
}
//查找
SListNode* SLTFind(SListNode* phead, SLDateType x)
{
	//断言,防止传空指针
	assert(phead);
	SListNode* cur=phead;
	//遍历
	while (cur)
	{
		if (cur->val == x)
		{
			return cur;
		}
		else
		{
			cur=cur->next;
		}
	}
	return NULL;
}
//在pos前面插入
void SLTInsert(SListNode**pphead,SListNode*pos,SLDatetype x)
{
	assert(pphead);
	assert(*pphead);
	assert(pos);
	if(*pphead==pos)
	{
		//头插
		SLTPushFront(pphead,x);
	}
	else
	{
		SListNode*prev=*pphead;
		while(prev->next!=pos)
		{
			prev=prev->next;
		}
		SListNode*newnode=CreatNode(x);
		prve->next=newnode;
		newnode->next=pos;
	}
}
//删除pos位置
void SLTErase(SListNode** pphead, SListNode* pos)
{
	assert(pphead);
	assert(*pphead);
	assert(pos);
	if(*pphead==pos)
	{
		//头删
		SLTPopFront(pphead);
	}
	else
	{
		SListNode*prve=*pphead;
		while(prev->next=pos)
		{
			prev=prev->next;
		}
		prev->next=pos->next;
		free(pos);
		pos=NULL;
	}
}
//在链表pos后面插入
void SLTInsertAfter(SListNode* pos, SLDateytype x)
{
	//断言,空指针NULL不能指向下一个结点
	assert(pos);
	SListNode* newnode = CreatNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}
//在链表pos后面删除
void SLTEraseAfter(SListNode* pos)
{
	assert(pos);
	//断言,如果pos是最后一个结点,就不用再删除了
	assert(pos->next);

	SListNode* tmp = pos->next;
	pos->next = pos->next->next;

	free(tmp);
	tmp = NULL;
}

你可能感兴趣的:(数据结构,c语言,算法)