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

欢迎来到我的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;
}
相关推荐
JSU_曾是此间年少14 分钟前
数据结构——线性表与链表
数据结构·c++·算法
sjsjs1121 分钟前
【数据结构-合法括号字符串】【hard】【拼多多面试题】力扣32. 最长有效括号
数据结构·leetcode
blammmp1 小时前
Java:数据结构-枚举
java·开发语言·数据结构
昂子的博客2 小时前
基础数据结构——队列(链表实现)
数据结构
lulu_gh_yu2 小时前
数据结构之排序补充
c语言·开发语言·数据结构·c++·学习·算法·排序算法
~yY…s<#>4 小时前
【刷题17】最小栈、栈的压入弹出、逆波兰表达式
c语言·数据结构·c++·算法·leetcode
XuanRanDev5 小时前
【每日一题】LeetCode - 三数之和
数据结构·算法·leetcode·1024程序员节
代码猪猪傻瓜coding5 小时前
力扣1 两数之和
数据结构·算法·leetcode
南宫生6 小时前
贪心算法习题其三【力扣】【算法学习day.20】
java·数据结构·学习·算法·leetcode·贪心算法
weixin_432702267 小时前
代码随想录算法训练营第五十五天|图论理论基础
数据结构·python·算法·深度优先·图论