数据结构入门指南:单链表(附源码)

这篇具有很好参考价值的文章主要介绍了数据结构入门指南:单链表(附源码)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

前言

尾删

头删

查找

位置前插入

 位置后插入

 位置删除

 位置后删除

 链表销毁

总结


前言

        前边关于链表的基础如果已经理解透彻,那么接下来就是对链表各功能的实现,同时也希望大家能把这部分内容熟练于心,这部分内容对有关链表部分的刷题很有帮助。废话不多讲我们步入正题。


        前边已经实现了头插、尾插的操作,今天主要的内容是:头删、尾删、位置删除、位置后删除、查找、位置前插入、位置后插入。

尾删

        要想进行尾删,就要先找到链表的尾。

数据结构入门指南:单链表(附源码),数据结构,c语言,经验分享,算法

        我们知道单链表的缺点之一就是只可以单向遍历,所以要想删除最后一个节点,就要先找到倒数第二个节点,然后释放掉最后一个节点,将倒数第二个节点的next(指针域)置为NULL。

 数据结构入门指南:单链表(附源码),数据结构,c语言,经验分享,算法

 具体代码实现:

void SLPopBlack(SLNode** pphead)
{
	
	assert(*pphead);
	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	SLNode* tail = *pphead;

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

        这里为什么要传二级指针?有人可能会有这样的疑惑, 传二级指针就是为了防止链表被删空的情况,在链表的许多情节中都要考虑像链表删空等这种极端情况,要做到面面俱到。

        当然我们也可以选择不使用二级指针,而是直接返回头指针的地址。但这样函数的类型就变成了结构体指针类型,而要调用这个函数还需要相同类型的结构体指针变量接收,这种情况在刷题中经常遇到,但在写链表时不推荐,这样写调用函数会比较麻烦。

头删

头删大家可以先思考一下,需不需要使用二级指针。

有这样一个链表:

数据结构入门指南:单链表(附源码),数据结构,c语言,经验分享,算法

         想要删除第一个节点,只需要把头指针指向的位置改为指向第二个节点。把头指针修改,这是修改结构体指针,到这里想必大家已经清楚,需要使用二级指针。

        接下来我们理一下删除的逻辑,直接将头指针指向第二个节点,这样就会造成第一个节点丢失没办法释放掉空间。如果先将第一个节点释放就会使第二个节点丢失,头指针无法连接剩余节点。

        这要怎么解决呢?这里就需要创建一个新的变量来存储一下第二个节点的地址,然后再将第一个节点释放。 

具体代码实现:

void SLPopFront(SLNode** pphead)
{
	assert(*pphead);

	SLNode* newhead = (*pphead)->next;
	free(*pphead);
	*pphead = newhead;
}

查找

        查找很简单,顺序表的查找返回的是下标,而链表返回的是节点的地址,后续的操作也是比较简单,我就不再画逻辑图。

SLNode* SLFind(SLNode* phead, Datatype x)
{
	SLNode* cur = phead;
	while (cur)
	{
		if (cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}

位置前插入

        位置前插入,如果是在第一个节点位置前插入,就是头插,其次是要想在位置前插入就必须要知道前有个节点,单链表是无法逆向遍历的,所以要想知道前一个节点就必须要传头指针。然后将前一个节点的next置为新节点的地址,新节点的next置为pos位置节点的地址。

void SLFrontInsert(SLNode** pphead, SLNode* pos, Datatype x)
{
	
	assert(pos);
	if (pos == *pphead)
	{
		SLPushFront(pphead, x);
	}
	else
	{
		SLNode* prev = *pphead;
		SLNode* newnode = NewNode(x);
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		newnode->next = pos;
		prev->next = newnode;
	}
}

 位置后插入

        位置后插入,可以不需要头指针。操作也非常简单,把pos位置的下一个节点赋给新节点的next,把新节点的地址赋给pos位置节点的next。这里有人可能会有疑惑,不考虑极端情况吗?位置后插入是无法进行头插的,如果链表为空,传进来pos就为空,就会直接保错,至于尾插,这段代码也是可以解决的。

void SLAfterInsert(SLNode* pos, Datatype x)
{
	assert(pos);
	SLNode* newnode = NewNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
	
}

 位置删除

        位置删除,需要把pos位置前一个节点的next置为pos位置下一个节点的地址,同时还需要将删除的节点释放空间。考虑极端情况,如果删除位置是第一个节点,这种方法就失效了,因为没有第一个节点的前一个节点,这时也就是头删,我们可以调用前边已经实现的函数接口。

void SLErase(SLNode** pphead, SLNode* pos) {
	assert(pphead);
	assert(pos);
	if (pos == *pphead)
	{
		SLPopFront(pphead);
	}
	else
	{
		SLNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		prev->next = pos->next;
		free(pos);
		pos = NULL;
	}
}

 位置后删除

        位置后删除,如果位置为最后一个节点,就不需要删除,且位置后删除无法进行头删,然后是正常情况,把pos位置节点的next置为pos位置后第二个节点的地址,就完成了。那是否可以这样写呢?

pos->next = pos->next->next

 答案是不可以,这样会造成pos后一个节点丢失,无法释放。所以这里我们需要分成两步来写:

void SLEraseAfter(SLNode* pos)
{
	assert(pos);
	if (pos->next == NULL)
	{
		return;
	}
	else
	{
		SLNode* posnext = pos->next;//不可以写成一步,否则pos后一个节点就会丢失,无法释放
		pos->next = posnext->next;
		free(posnext);
		posnext = NULL;
	}

}

 链表销毁

        执行完所有操作后,就需要将链表销毁了

void SLDestory(SLNode** pphead)
{
	assert(pphead);
	SLNode* cur = *pphead;
	
	while (cur)
	{
		SLNode* next =cur->next;
		free(cur);
		cur = next;
	}
	*pphead = NULL;
}

 完整代码:

SList.c

#include"SList.h"
void SLprint(SLNode* phead)
{
	SLNode* cur = phead;
	while (cur)
	{
		printf("%d->", cur->data);
		cur = cur->next;
	}
	printf("NULL\n");
}
SLNode* NewNode(Datatype x)
{
	SLNode* newnode = (SLNode*)malloc(sizeof(SLNode));
	if (newnode == NULL)
	{
		perror("malloc");
		exit(-1);
	}
	newnode->data = x;
	newnode->next = NULL;
	return newnode;
}
void SLPushBlack(SLNode** pphead, Datatype x)
{
	assert(pphead);
	SLNode* newnode = NewNode(x);
	SLNode* tail = *pphead;
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else
	{
		while (tail->next)
		{
			tail = tail->next;
		}
		tail->next = newnode;
	}
}
void SLPushFront(SLNode** pphead, Datatype x)
{
	assert(pphead);
	SLNode* newnode = NewNode(x);
	newnode->next = *pphead;
	*pphead = newnode;
}
void SLPopBlack(SLNode** pphead)
{
	assert(pphead);
	assert(*pphead);
	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	SLNode* tail = *pphead;

	while (tail->next->next)
	{
		tail = tail->next;
	}
	free(tail->next);
	tail->next = NULL;
}
void SLPopFront(SLNode** pphead)
{
	assert(pphead);
	assert(*pphead);


	SLNode* newhead = (*pphead)->next;
	free(*pphead);
	*pphead = newhead;
}
SLNode* SLFind(SLNode* phead, Datatype x)
{
	SLNode* cur = phead;
	while (cur)
	{
		if (cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}
void SLFrontInsert(SLNode** pphead, SLNode* pos, Datatype x)
{
	assert(pphead);
	assert(pos);
	if (pos == *pphead)
	{
		SLPushFront(pphead, x);
	}
	else
	{
		SLNode* prev = *pphead;
		SLNode* newnode = NewNode(x);
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		newnode->next = pos;
		prev->next = newnode;
	}
}
void SLAfterInsert(SLNode* pos, Datatype x)
{
	assert(pos);
	SLNode* newnode = NewNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
	
}
void SLErase(SLNode** pphead, SLNode* pos) {
	assert(pphead);
	assert(pos);
	if (pos == *pphead)
	{
		SLPopFront(pphead);
	}
	else
	{
		SLNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		prev->next = pos->next;
		free(pos);
		pos = NULL;
	}
}
void SLEraseAfter(SLNode* pos)
{
	assert(pos);
	if (pos->next == NULL)
	{
		return;
	}
	else
	{
		SLNode* posnext = pos->next;
		pos->next = posnext->next;
		free(posnext);
		posnext = NULL;
	}

}
void SLDestory(SLNode** pphead)
{
	assert(pphead);
	SLNode* cur = *pphead;
	
	while (cur)
	{
		SLNode* next =cur->next;
		free(cur);
		cur = next;
	}
	*pphead = NULL;
}

 SList.h

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

typedef int Datatype;
typedef struct SLNode
{
	Datatype data;
	struct SLNode* next;
}SLNode;
//打印链表
void SLprint(SLNode* phead);
//创建新节点
SLNode* NewNode(Datatype x);
//尾插
void SLPushBlack(SLNode** phead, Datatype x);
//头插
void SLPushFront(SLNode** pphead, Datatype x);

//尾删
void SLPopBlack(SLNode** pphead);
//头删
void SLPopFront(SLNode** pphead);
//查找
SLNode* SLFind(SLNode* phead, Datatype x);
//pos位置前插入
void SLFrontInsert(SLNode** pphead, SLNode* pos, Datatype x);
//pos位置后插入
void SLAfterInsert(SLNode* pos, Datatype x);
//pos位置后删除
void SLEraseAfter(SLNode* pos);
//pos位置删除
void SLErase(SLNode** pphead, SLNode* pos);

void SLDestory(SLNode** pphead);

 test.c

这里基本都是测试接口,没有什么太大的参考价值,代码如下,便于大家调试。

#include"SLNode.h"
void test1()
{
	SLNode* plist = NULL;
	int n = 0;
	printf("请输入链表的长度\n");
	scanf("%d", &n);
	printf("请输入数据\n");
	for (int i = 0; i < n; i++)
	{
		int val = 0;
		scanf("%d", &val);
		SLNode *newnode= NewNode(val);
		newnode->next = plist;
		plist = newnode;
	}
	SLNode* pos= SLFind(plist, 2);
	if (pos)
	{
		pos->data *= 10;
	}
	SLFrontInsert(&plist, pos, 10);
	SLprint(plist);

	SLPushBlack(&plist,100);
	SLprint(plist);

	SLPushFront(&plist, 200);
	SLprint(plist);

	SLPopBlack(&plist);
	SLprint(plist);

	SLPopFront(&plist);
	SLprint(plist);
}
void test2()
{
	SLNode* plist = NULL;
	SLPushBlack(&plist, 1);
	SLPushBlack(&plist, 2);
	SLPushBlack(&plist, 3);
	SLPushBlack(&plist, 4);
	SLPushBlack(&plist, 5);
	SLprint(plist);
	SLNode* pos = SLFind(plist, 5);
	SLAfterInsert(pos, 20);
	SLprint(plist);
	SLFrontInsert(&plist, pos, 10);
	SLprint(plist);
}
void test3()
{
	SLNode* plist = NULL;
	SLPushBlack(&plist, 1);
	SLPushBlack(&plist, 2);
	SLPushBlack(&plist, 3);
	SLPushBlack(&plist, 4);
	SLPushBlack(&plist, 5);
	SLprint(plist);
	SLNode* pos = SLFind(plist, 1);
	//SLErase(&plist, pos);
	SLEraseAfter(pos);
	SLprint(plist);
	SLDestory(&plist);
	
}

int main()
{
	test2();
	

	return 0;
}

总结

        好的,内容到这里就要结束了,这部分内容或许看来很繁琐,但在刷链表相关的题时就会惊奇的发现,题解都是这些操作的变形。熟练这部分内容,可以让你在刷链表相关的题时会感觉非常的爽,刷题也会更加顺利。最后,感谢阅读!文章来源地址https://www.toymoban.com/news/detail-626517.html

到了这里,关于数据结构入门指南:单链表(附源码)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【数据结构入门指南】二叉树

    二叉树是一棵特殊的树。一棵二叉树是结点的一个有限集合,该节点: ①:或者为空。 ②: 由一个根节点加上两棵别称为左子树和右子树的二叉树组成。 从上图可以看出: 二叉树不存在度大于2的结点. 二叉树的子树有左右之分,次序不能颠倒,因此二叉树是有序树。 Tip

    2024年02月11日
    浏览(37)
  • C语言笔记 | 数据结构入门指南

    文章目录 0x00 前言 0x01 百鸡百钱 0x1 题目描述 0x2 问题分析 0x3 代码设计 0x4 完整代码 0x5 运行效果 0x6 举一反三 [兔鸡百钱] 0x02 借书方案知多少 0x1 题目描述 0x2 问题分析 0x3 代码设计 0x4 完整代码 0x5 运行效果 0x6 举一反三 [领导小组方案] 0x03 打鱼还是晒网 0x1 题目描述 0x2 问题分

    2024年02月08日
    浏览(48)
  • Python基础数据结构入门必读指南

    作者主页:涛哥聊Python 个人网站:涛哥聊Python 大家好,我是涛哥,今天为大家分享的是Python中常见的数据结构。 含义:数组是一种有序的数据结构,其中的元素可以按照索引来访问。数组的大小通常是固定的,一旦创建就不能更改。 基本操作: 含义:列表是Python中内置的

    2024年02月07日
    浏览(55)
  • 数据结构入门指南:带头双向循环链表

    目录 文章目录 前言 1.结构与优势 2.链表实现       2.1 定义链表 2.2 创建头节点 2.3 尾插 2.4 输出链表 2.5 尾删 2.6 头插 2.7头删 2.8 节点个数 2.9 查找 2.10 位置插入 2.11 位置删除 2.12 销毁链表  3. 源码 总结         链表一共有8种结构,但最常用的就是无头单向链表、和带头

    2024年02月14日
    浏览(51)
  • 【数据结构入门指南】二叉树顺序结构: 堆及实现(全程配图,非常经典)

    普通的二叉树是不适合用数组来存储的,因为可能会存在大量的空间浪费。 而完全二叉树更适合使用顺序结构存储。   现实中我们通常把堆(一种二叉树)使用顺序结构的数组来存储 ,需要注意的是这里的堆和操作系统虚拟进程地址空间中的堆是两回事,一个是数据结构,一

    2024年02月12日
    浏览(38)
  • 【数据结构入门指南】二叉树链式结构的实现(保姆级代码思路解读,非常经典)

    其他数据结构不同,二叉树的增删查改接口实现的意义不大(后续搜索树的增删查改才有意义)。普通初阶二叉树更重要的是学习控制结构,为后续的AVL树、红黑树等高级数据结构打下基础。同时大部分OJ题也出在此处。 所谓二叉树遍历(Traversal)是按照某种特定的规则,依次

    2024年02月11日
    浏览(46)
  • 【Java--数据结构】提升你的编程段位:泛型入门指南,一看就会!

    泛型是一种编程概念,它允许我们编写可以适用于多种数据类型的代码。通过使用泛型,我们可以在编译时期将具体的 数据类型作为参数 传递给代码,从而实现代码 的复用和灵活性 。 在传统的编程中,我们通常需要为不同的数据类型编写不同的代码,这样会导致代码冗余

    2024年04月26日
    浏览(64)
  • 探索数据结构:单链表的实战指南

    ✨✨ 欢迎大家来到贝蒂大讲堂✨✨ 🎈🎈养成好习惯,先赞后看哦~🎈🎈 所属专栏:数据结构与算法 贝蒂的主页:Betty‘s blog 在上一章节中我们讲解了数据结构中的顺序表,知道了顺序表的空间是连续存储的,这与数组非常类似,为我们随机访问数据提供了便利的条件。但

    2024年03月09日
    浏览(65)
  • 数据结构入门——单链表

    目录 前言 链表的定义 链表的创建 头插法创建链表 尾插法创建链表 链表的删除 在头部删除元素 在尾部删除元素 查找固定元素 在链表中插入元素 在pos位置前插入 在pos位置后插入 删除链表结点 删除pos位置的结点 删除pos后的结点 链表的销毁 写在最后 前面我们学习了顺序表

    2024年02月20日
    浏览(40)
  • 数据结构入门 — 链表详解_单链表

    数据结构入门 — 单链表详解 * 博客主页链接:https://blog.csdn.net/m0_74014525 关注博主,后期持续更新系列文章 文章末尾有源码 *****感谢观看,希望对你有所帮助***** 第一篇:数据结构入门 — 链表详解_单链表 第二篇:数据结构入门 — 链表详解_双向链表 第三篇:数据结构入门

    2024年02月11日
    浏览(55)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包