【数据结构】单链表-->详细讲解,后赋源码

欢迎来到我的Blog,点击关注哦💕

前面已经介绍顺序表,顺序表存在一定的局限性,空间管理上存在一定的缺陷,今天介绍新的存储结构单链表。

前言:

单链表是一种基本的数据结构,它由一系列节点组成,每个节点包含数据部分和一个指向下一个节点的指针。在单链表中,每个节点的地址不一定是连续的,而是通过指针相互链接起来。单链表的特点是存储灵活,可以动态地添加或删除节点,不需要预先分配固定大小的存储空间。

一、单链表基本介绍

单链表创建

1.定义节点结构体 :首先需要定义一个结构体来表示链表的节点,通常包括数据域和指针域。

2.动态创建节点 :使用malloc函数为每个节点分配内存空间,并初始化数据域和指针域。

3.插入节点 :根据需要将新节点插入到链表的适当位置。插入操作可以是头插法或尾插法。

4.遍历链表:通过遍历链表,可以访问链表中的每个节点,通常用于打印或搜索特定数据。

单链表的操作

单链表的常见操作包括插入、删除、查找和遍历。这些操作通常涉及到对链表节点的指针进行操作,以实现数据的动态管理。

单链表的理解

链表同名字一样,像一个链子一样,有一个一个节点相连接。
A B C D E ....

二、单链表的实现

2.1 单链表的功能

分装成函数,有助于我们一一管理。

c 复制代码
// 动态申请一个节点
SListNode* BuySListNode(SLTDateType x);
// 单链表打印
void SListPrint(SListNode* plist);
// 单链表尾插
void SListPushBack(SListNode** pplist, SLTDateType x);
// 单链表的头插
void SListPushFront(SListNode** pplist, SLTDateType x);
// 单链表的尾删
void SListPopBack(SListNode** pplist);
// 单链表头删
void SListPopFront(SListNode** pplist);
// 单链表查找
SListNode* SListFind(SListNode* plist, SLTDateType x);
// 单链表在pos位置之后插入x
void SListInsertAfter(SListNode* pos, SLTDateType x);
// 在pos的前面插入
void SLTInsert(SListNode** pplist, SListNode* pos, SLTDateType x);
// 单链表删除pos位置之后的值
void SListEraseAfter(SListNode* pos); 
// 删除pos位置
void SLTErase(SListNode** pplist, SListNode* pos);
///删除pos前面的值
void SListEraseFront(SListNode* pos);
//单链表的销毁
void SLTDestory(SListNode** pphead);

2.2 定义节点结构体

数域 和 指针域

c 复制代码
typedef int SLTDateType;

typedef struct SListNode
{
	SLTDateType data;
	struct SListNode* next;
}SListNode;

2.3 动态创建节点

c 复制代码
SListNode* BuySListNode(SLTDateType x)
{
	SListNode* newcode = (SLTDateType*)malloc(sizeof(SListNode));
	if (newcode == NULL)
	{
		perror("malloc fail");
		return NULL;
	}
	newcode->data = x;
	newcode->next = NULL;

	return newcode;
}

2.3 单链表便历

注意:链表和顺序表的区别顺序表内存是连续的,链表是由一个一个节点连接的,'cur' 指向的下一个节点赋值给'cur'cur = cur->next;

c 复制代码
void SListPrint(SListNode* plist)
{
	SListNode* cur = plist;
	while (cur != NULL)
	{
		printf("%d->", cur->data);
		cur = cur->next;
	}
	printf("NULL\n");
}

2.4 单链表的尾插

断言 pplist 避免传参时候造成不必要的麻烦

c 复制代码
//单链表尾插
void SListPushBack(SListNode** pplist, SLTDateType x)
{
	assert(pplist);
	SListNode* newcode = BuySListNode(x);

	if (*pplist == NULL)
	{
		*pplist = newcode;
	}
	else
	{
		//找尾
		SListNode* tail = *pplist;
		while (tail->next != NULL)
		{
			tail = tail->next;
		}
		tail->next = newcode;
	}
}

2.5 单链表的尾删除

c 复制代码
//单链表尾删
void SListPopBack(SListNode** pplist)
{
	//节点断言
	assert(pplist);
	assert(*pplist);
	//存在节点 1.存在一个节点 2.存在两个节点
	if ((*pplist)->next == NULL)
	{
		free(*pplist);
		*pplist = NULL;
	}
	else
	{
		//找尾

		//SListNode* tail = *pplist;
		//while (tail->next->next != NULL)
		//{
		//	tail = tail->next;
		//}
		//free(tail->next);
		//tail->next = NULL;

		SListNode* prv = NULL;
		SListNode* tail = *pplist;
		while (tail->next != NULL)
		{
			prv = tail;
			tail = tail->next;
		}

		free(tail);
		prv->next = NULL;
	}
}

2.6 单链表的头插

这个相对简单

c 复制代码
//单链表头插
void SListPushFront(SListNode** pplist, SLTDateType x)
{
	SListNode* newcode = BuySListNode(x);
	newcode->next = *pplist;
	*pplist = newcode;
}

2.6 单链表的头删

c 复制代码
//单链表头删
void SListPopFront(SListNode** pplist)
{
	//节点断言
	assert(*pplist);

	SListNode* first = *pplist;
	*pplist = first->next;
	free(first);
	first = NULL;
}

2.7 单链表的查找

c 复制代码
//单链表查找
SListNode* SListFind(SListNode* plist, SLTDateType x)
{
	SListNode* cur = plist;
	while (cur)
	{
		if (cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}

2.8 单链表在指定位置(pos)的插入

2.8.1 在pos后面
c 复制代码
// 单链表在pos位置之后插入x
void SListInsertAfter(SListNode* pos, SLTDateType x)
{
	assert(pos);
	SListNode* newcode = BuySListNode(x);
	newcode->next = pos->next;
	pos->next = newcode;
}
2.8.2 在pos前面插入
c 复制代码
// 在pos的前面插入
void SLTInsert(SListNode** pplist, SListNode* pos, SLTDateType x)
{
	assert(pplist);
	assert(*pplist);

	SListNode* newcode = BuySListNode(x);
	if (pos == *pplist)
	{
		SListPushFront(pplist,x);
	}
	else
	{
		SListNode* cur = *pplist;
		while (cur->next != pos)
		{
			cur = cur->next;
		}
		newcode->next = pos;
		cur->next = newcode;
	}
}
2.8.3 在pos后面插入
c 复制代码
// 单链表在pos位置之后插入x
void SListInsertAfter(SListNode* pos, SLTDateType x)
{
	assert(pos);
	SListNode* newcode = BuySListNode(x);
	newcode->next = pos->next;
	pos->next = newcode;
}

2.9 在指定位置(pos)删除

2.9.1 删除在pos位置前面
c 复制代码
// 单链表删除pos位置之后的值
void SListEraseAfter(SListNode* pos)
{
	SListNode* del = pos->next;
	pos->next = pos->next->next;
	free(del);
	del = NULL;
}
2.9.2 删除 pos 本位上的值
c 复制代码
// 删除pos位置
void SLTErase(SListNode** pplist, SListNode* pos)
{
	assert(pplist);
	assert(*pplist);

	SListNode* tail = *pplist;
	while (tail->next != pos)
	{
		tail = tail->next;
	}
	tail->next = pos->next;
	free(pos);
	pos = NULL;
}
2.9.3 删除pos位置后面的值
c 复制代码
//删除pos前面的值
void SListEraseFront(SListNode** pplist,SListNode* pos)
{
	assert(pplist);
	assert(*pplist);

	SListNode* tail = *pplist;
	while (tail->next->next != pos)
	{
		tail = tail->next;
	}
	SListNode* del = tail->next;
	tail->next = pos;
	free(del);
	del = NULL;
}

2.10 单链表的销毁

不可以直接销毁*pplist,内存存贮不是连续的需要一个一个销毁。

c 复制代码
//销毁链表
void SLTDestory(SListNode** pplist)
{
	assert(pphead);
	SListNode* cur = *pphead;
	while (cur)
	{
		SListNode* next = cur->next;
		free(cur);
		cur = next;
	}
	*pphead = NULL;

源码

SLT.h

c 复制代码
#pragma once


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


typedef int SLTDateType;

typedef struct SListNode
{
	SLTDateType data;
	struct SListNode* next;
}SListNode;

// 动态申请一个节点
SListNode* BuySListNode(SLTDateType x);
// 单链表打印
void SListPrint(SListNode* plist);
// 单链表尾插
void SListPushBack(SListNode** pplist, SLTDateType x);
// 单链表的头插
void SListPushFront(SListNode** pplist, SLTDateType x);
// 单链表的尾删
void SListPopBack(SListNode** pplist);
// 单链表头删
void SListPopFront(SListNode** pplist);
// 单链表查找
SListNode* SListFind(SListNode* plist, SLTDateType x);
// 单链表在pos位置之后插入x
void SListInsertAfter(SListNode* pos, SLTDateType x);
// 在pos的前面插入
void SLTInsert(SListNode** pplist, SListNode* pos, SLTDateType x);
// 单链表删除pos位置之后的值
void SListEraseAfter(SListNode* pos); 
// 删除pos位置
void SLTErase(SListNode** pplist, SListNode* pos);
///删除pos前面的值
void SListEraseFront(SListNode* pos);
//单链表的销毁
void SLTDestory(SListNode** pplist);

SLT.c

c 复制代码
#define _CRT_SECURE_NO_WARNINGS
#include"SLT.h"


//动态申请一个节点
SListNode* BuySListNode(SLTDateType x)
{
	SListNode* newcode = (SLTDateType*)malloc(sizeof(SListNode));
	if (newcode == NULL)
	{
		perror("malloc fail");
		return NULL;
	}
	newcode->data = x;
	newcode->next = NULL;

	return newcode;
}

//打印单链表
void SListPrint(SListNode* plist)
{
	SListNode* cur = plist;
	while (cur != NULL)
	{
		printf("%d->", cur->data);
		cur = cur->next;
	}
	printf("NULL\n");
}

//单链表尾插
void SListPushBack(SListNode** pplist, SLTDateType x)
{
	SListNode* newcode = BuySListNode(x);

	if (*pplist == NULL)
	{
		*pplist = newcode;
	}
	else
	{
		//找尾
		SListNode* tail = *pplist;
		while (tail->next != NULL)
		{
			tail = tail->next;
		}
		tail->next = newcode;
	}
}


//单链表尾删
void SListPopBack(SListNode** pplist)
{
	//节点断言
	assert(*pplist);
	//if((*pplist)==NULL)
	// return ;

	//存在节点 1.存在一个节点 2.存在两个节点
	if ((*pplist)->next == NULL)
	{
		free(*pplist);
		*pplist = NULL;
	}
	else
	{
		//找尾

		//SListNode* tail = *pplist;
		//while (tail->next->next != NULL)
		//{
		//	tail = tail->next;
		//}
		//free(tail->next);
		//tail->next = NULL;

		SListNode* prv = NULL;
		SListNode* tail = *pplist;
		while (tail->next != NULL)
		{
			prv = tail;
			tail = tail->next;
		}

		free(tail);
		prv->next = NULL;
	}
}


//单链表头插
void SListPushFront(SListNode** pplist, SLTDateType x)
{
	SListNode* newcode = BuySListNode(x);
	newcode->next = *pplist;
	*pplist = newcode;
}

//单链表头删
void SListPopFront(SListNode** pplist)
{
	//节点断言
	assert(*pplist);

	SListNode* first = *pplist;
	*pplist = first->next;
	free(first);
	first = NULL;
}

//单链表查找
SListNode* SListFind(SListNode* plist, SLTDateType x)
{
	SListNode* cur = plist;
	while (cur)
	{
		if (cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}

// 单链表在pos位置之后插入x
void SListInsertAfter(SListNode* pos, SLTDateType x)
{
	assert(pos);
	SListNode* newcode = BuySListNode(x);
	newcode->next = pos->next;
	pos->next = newcode;
}

// 在pos的前面插入
void SLTInsert(SListNode** pplist, SListNode* pos, SLTDateType x)
{
	assert(pplist);
	assert(*pplist);

	SListNode* newcode = BuySListNode(x);
	if (pos == *pplist)
	{
		SListPushFront(pplist,x);
	}
	else
	{
		SListNode* cur = *pplist;
		while (cur->next != pos)
		{
			cur = cur->next;
		}
		newcode->next = pos;
		cur->next = newcode;
	}
}

// 单链表删除pos位置之后的值
void SListEraseAfter(SListNode* pos)
{
	SListNode* del = pos->next;
	pos->next = pos->next->next;
	free(del);
	del = NULL;
}

// 删除pos位置
void SLTErase(SListNode** pplist, SListNode* pos)
{
	assert(pplist);
	assert(*pplist);

	SListNode* tail = *pplist;
	while (tail->next != pos)
	{
		tail = tail->next;
	}
	tail->next = pos->next;
	free(pos);
	pos = NULL;
}

//删除pos前面的值
void SListEraseFront(SListNode** pplist,SListNode* pos)
{
	assert(pplist);
	assert(*pplist);

	SListNode* tail = *pplist;
	while (tail->next->next != pos)
	{
		tail = tail->next;
	}
	SListNode* del = tail->next;
	tail->next = pos;
	free(del);
	del = NULL;
}


//销毁链表
void SLTDestory(SListNode** pplist)
{
	assert(pplist);
	SListNode* cur = *pplist;
	while (cur)
	{
		SListNode* next = cur->next;
		free(cur);
		cur = next;
	}
	*pplist = NULL;

Test.c

c 复制代码
#define _CRT_SECURE_NO_WARNINGS


#include "SLT.h"

void testSList1()
{
	SListNode* plist = NULL;
	SListPushBack(&plist, 1);
	SListPushBack(&plist, 2);
	SListPushBack(&plist, 3);
	SListPushBack(&plist, 4);

	SListNode* ret = SListFind(plist, 2);
	//ret->data = (ret->data) * 3;

	//SListInsertAfter(ret, 66);

	SLTInsert(&plist, ret, 77);

	//SListEraseAfter(ret);

	//SLTErase(&plist, ret);

	SListEraseFront(&plist, ret);

	SListPrint(plist);

}

void testSList2()
{
	SListNode* plist = NULL;
	SListPushFront(&plist, 1);
	SListPushFront(&plist, 2);
	SListPushFront(&plist, 3);
	SListPushFront(&plist, 4);


	SListPopFront(&plist);
	SListPopFront(&plist);

	SListPrint(plist);



}

int main()
{

	testSList1();
	//testSList2();


	return 0;
}
相关推荐
云手机掌柜1 小时前
从0到500账号管理:亚矩阵云手机多开组队与虚拟定位实战指南
数据结构·线性代数·网络安全·容器·智能手机·矩阵·云计算
没书读了4 小时前
考研复习-数据结构-第八章-排序
数据结构
waveee1236 小时前
学习嵌入式的第三十四天-数据结构-(2025.7.29)数据库
数据结构·数据库·学习
jie*7 小时前
小杰数据结构(one day)——心若安,便是晴天;心若乱,便是阴天。
数据结构
伍哥的传说8 小时前
React & Immer 不可变数据结构的处理
前端·数据结构·react.js·proxy·immutable·immer·redux reducers
ZTLJQ10 小时前
专业Python爬虫实战教程:逆向加密接口与验证码突破完整案例
开发语言·数据结构·爬虫·python·算法
努力的小帅11 小时前
C++_红黑树树
开发语言·数据结构·c++·学习·算法·红黑树
逐花归海.11 小时前
『 C++ 入门到放弃 』- 哈希表
数据结构·c++·程序人生·哈希算法·散列表
qqxhb12 小时前
零基础数据结构与算法——第六章:算法设计范式与高级主题-设计技巧(上)
java·数据结构·算法·分解·空间换时间·时空平衡
xiaofann_13 小时前
【数据结构】用堆实现排序
数据结构