【数据结构】单链表——单链表的定义及基本操作的实现(头插、尾插、头删、尾删、任意位置的插入与删除)

这篇具有很好参考价值的文章主要介绍了【数据结构】单链表——单链表的定义及基本操作的实现(头插、尾插、头删、尾删、任意位置的插入与删除)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

🧑‍💻作者: @情话0.0
📝专栏:《数据结构》
👦个人简介:一名双非编程菜鸟,在这里分享自己的编程学习笔记,欢迎大家的指正与点赞,谢谢!

设计一个单链表,该单链表具有头插法创建链表、插入元素、删除元素、显示及取值等,数据结构,数据结构,链表,算法


前言

  顺序表可以随时存取表中的任意一个元素,它的存储位置可以用一个简单直观的公式表示,但是插入和删除操作需要移动大量元素。链式存储线性表时,不需要使用地址连续的存储单元,即不要求逻辑上相邻的元素在物理位置上也相邻,它通过“链”建立起数据元素之间的逻辑关系,因此插入和删除操作不需要移动元素,而只需要修改指针,但也会失去顺序表可随机存取的优点。


一、单链表的定义

  线性表的链式存储又称为单链表,它是通过一组任意的存储单元来存储线性表表中的数据元素。为了建立数据元素之间的线性关系,对每个链表节点,除存放元素本身的信息外,还需要存放一个指向其后继的指针。单链表的结构如下图所示,其中data为数据域,存放数据元素;next为指针域,存放其后继节点的地址。
设计一个单链表,该单链表具有头插法创建链表、插入元素、删除元素、显示及取值等,数据结构,数据结构,链表,算法
  利用单链表可以解决顺序表需要大量连续存储单元的缺点,但单链表附加指针域,也存在浪费存储空间的缺点。由于单链表的元素离散地分布在存储空间中,所以单链表时非随机存取的存储结构,即不能直接找到表中某个特定的结点。查找某个特定的借点时,需要从头开始遍历,依次查找。

下图为一个带头结点的单链表,头指针pList,它指向的是头结点,除最后一个节点外,它们的next都指向下一个点的地址,对于最后一个节点的next指向NULL

设计一个单链表,该单链表具有头插法创建链表、插入元素、删除元素、显示及取值等,数据结构,数据结构,链表,算法

从上图可以看出,链式结构在逻辑上是连续的,但在物理上不一定连续。
链表中的结点一般都是从堆上申请的。
从堆上申请的空间是按照一定的策略来分配的,两次申请的空间有可能连续,有可能不连续。

头节点和头指针的区分: 不管带不带头节点,头指针都始终指向链表的第一个节点,而头节点是带头结点的链表的第一个结点,结点内通常不存储信息。

二、单链表上基本操作的实现(不带头结点)

单链表中结点类型的描述如下:

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

1. 动态申请一个节点

对于插入操作来说,需要先动态申请一个结点,并将该结点的数值域与指针域进行赋值,指针域都设置为NULL

SListNode* BuySListNode(SLTDateType data)
{
	SListNode* newNode = (SListNode*)malloc(sizeof(SListNode));
	if (newNode == NULL)
	{
		return NULL;
	}
	newNode->data = data;
	newNode->next = NULL;
	return newNode;
}

2. 单链表尾插

在进行尾插操作时,要考虑到的一点就是在插入该结点之前此单链表是否为空,为空时则直接将该结点设置为头结点就行,若不为空就循环遍历找到最后一个结点进行尾插就行。
还有一点最为重要,就是在进行函数传参时,第一个参数为二级指针,这是为什么呢?首先定义一个头指针,它是用来指向链表第一个节点,当你以一级指针进行传参时,那么在该函数内得到头指针是一份临时拷贝,所以对该指针的临时拷贝进行操作时就不会出现想要得到的效果,所以说,凡是有关头指针的操作就是传二级指针,对于后续结点的插入其实可以传递一级指针,因为所要改变的不是头指针,而是结点这个结构体。我感觉吧,可以把该链表想象为一个双头蛇,一个头为另一个头的拷贝,当你对那个拷贝的头操作后并不会影响另外一个头,但是对于后续节点来说就可以比作为蛇的身子。

void SListPushBack(SListNode** Head, SLTDateType data)
{
	assert(Head);
	SListNode* newNode = BuySListNode(data); //创建一个值为data的结点
	if (*Head == NULL)
	{
		*Head = newNode;
	}
	else
	{
		SListNode* cur = *Head;
		while (cur->next)
		{
			cur = cur->next;
		}
		cur->next = newNode;
	}
}

3. 单链表尾删

在尾删时同样也要传递二级指针,有可能该链表只有一个结点。

void SListPopBack(SListNode** Head)
{
	if (*Head == NULL) //链表为空
	{
		return;
	}
	else if ((*Head)->next == NULL) //链表只有一个结点
	{
		*Head = NULL;
	}
	else  //链表中有多个结点
	{
		SListNode* cur = (*Head)->next;
		SListNode* prev = *Head; //prev为前序指针
		while (cur->next)
		{
			cur = cur->next;
			prev = prev->next;
		}
		//在循环退出后,prev指针指向倒数第二个结点
		prev->next = NULL;
		free(cur);
	}
}

4. 单链表头插

void SListPushFront(SListNode** Head, SLTDateType data)
{
	assert(Head);
	SListNode* newNode = BuySListNode(data);  //创建一个值为data的结点
	newNode->next = *Head;
	*Head = newNode;
}

5. 单链表头删

void SListPopFront(SListNode** Head)
{
	if (*Head == NULL) //链表为空
	{
		return;
	}
	else
	{
		SListNode* cur = *Head;
		*Head = (*Head)->next;
		free(cur);
	}
}

6. 单链表查找

此函数旨在查找元素,并不会去修改头指针的指向,所以传一级指针即可。

SListNode* SListFind(SListNode* Head, SLTDateType data)
{
	assert(Head);
	while (Head)
	{
		if (Head->data == data)
		{
			return Head;
		}
		Head = Head->next;
	}
	return NULL; //没有找到就返回NULL
}

7. 单链表在pos位置之后插入data

该操作是在pos位置之后进行插入(该pos位置通过查找函数获取),当然也可以在pos位置之前插入,不同的是只需改变查找函数即可,让查找函数返回pos位置的前一个结点指针。
因为不管任意位置插入还是删除都在pos位置之后,所以并不会涉及到头指针,因此不需要传递二级指针。若是pos位置之前插入删除则需要考虑有可能涉及到头指针指向位置的改变。

void SListInsertAfter(SListNode* pos, SLTDateType data)
{
	SListNode* newNode = BuySListNode(data);
	if (NULL == pos)
		return;
	newNode->next = pos->next;  //这里的两行代码顺序不能乱,因为会导致pos之后的结点找不到
	pos->next = newNode;
}

8. 单链表删除pos位置之后的值

void SListDelAfter(SListNode* pos)
{
	SListNode* newNode = NULL;
	if (NULL == pos || NULL == pos->next)
		return;
	newNode = pos->next; //同样,此处代码不能乱
	pos->next = newNode->next;
	free(newNode);
}

三、源代码及运行结果展示

1. SList.h

结构体创建及函数声明

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

typedef int SLTDateType;

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

SListNode* BuySListNode(SLTDateType data);

void SListPrint(SListNode* Head);

void SListPushBack(SListNode** Head, SLTDateType data);

void SListPushFront(SListNode** Head, SLTDateType data);

void SListPopBack(SListNode** Head);

void SListPopFront(SListNode** Head);

SListNode* SListFind(SListNode* Head, SLTDateType data);

void SListInsertAfter(SListNode* pos, SLTDateType data);

void SListTest();

2. SList.c

方法实现

#include "SList.h"

SListNode* BuySListNode(SLTDateType data)
{
	SListNode* newNode = (SListNode*)malloc(sizeof(SListNode));
	if (newNode == NULL)
	{
		return NULL;
	}
	newNode->data = data;
	newNode->next = NULL;
	return newNode;
}

void SListPushBack(SListNode** Head, SLTDateType data)
{
	assert(Head);
	SListNode* newNode = BuySListNode(data);
	if (*Head == NULL)
	{
		*Head = newNode;
	}
	else
	{
		SListNode* cur = *Head;
		while (cur->next)
		{
			cur = cur->next;
		}
		cur->next = newNode;
	}
}

void SListPushFront(SListNode** Head, SLTDateType data)
{
	assert(Head);
	SListNode* newNode = BuySListNode(data);
	newNode->next = *Head;
	*Head = newNode;

}

void SListPopBack(SListNode** Head)
{
	if (*Head == NULL)
	{
		return;
	}
	else if ((*Head)->next == NULL)
	{
		*Head = NULL;
	}
	else
	{
		SListNode* cur = (*Head)->next;
		SListNode* prev = *Head;
		while (cur->next)
		{
			cur = cur->next;
			prev = prev->next;
		}
		prev->next = NULL;
		free(cur);
	}
}

void SListPopFront(SListNode** Head)
{
	if (*Head == NULL)
	{
		return;
	}
	else
	{
		SListNode* cur = *Head;
		*Head = (*Head)->next;
		free(cur);
	}
}

SListNode* SListFind(SListNode* Head, SLTDateType data)
{
	assert(Head);
	while (Head)
	{
		if (Head->data == data)
		{
			return Head;
		}
		Head = Head->next;
	}
	return NULL;
}

void SListInsertAfter(SListNode* pos, SLTDateType data)
{
	SListNode* newNode = BuySListNode(data);
	if (NULL == pos)
		return;
	newNode->next = pos->next;
	pos->next = newNode;
}

void SListDelAfter(SListNode* pos)
{
	SListNode* newNode = NULL;
	if (NULL == pos || NULL == pos->next)
		return;
	newNode = pos->next;
	pos->next = newNode->next;
	free(newNode);
}

void SListPrint(SListNode* Head)
{
	SListNode* cur = Head;
	while (cur)
	{
		printf("%d---->", cur->data);
		cur = cur->next;
	}
	printf("NULL\n");
}

void SListTest()
{
	SListNode* ListNode = NULL;
	SListPushFront(&ListNode, 0);
	SListPushBack(&ListNode, 1);
	SListPushBack(&ListNode, 2);
	SListPushBack(&ListNode, 3);
	SListPushBack(&ListNode, 4);
	SListPushBack(&ListNode, 5);
	SListPrint(ListNode);

	SListPushFront(&ListNode, -1);
	SListPrint(ListNode);

	SListPopBack(&ListNode);
	SListPopBack(&ListNode);
	SListPrint(ListNode);

	SListPopFront(&ListNode);
	SListPrint(ListNode);

	SListNode* ret = SListFind(ListNode, 0);
	if (ret == NULL)
	{
		printf("没找到\n");
	}
	else
	{
		printf("找到了\n");
	}

	SListInsertAfter(SListFind(ListNode, 2), 0);
	SListPrint(ListNode);

	SListDelAfter(SListFind(ListNode, 2));
	SListPrint(ListNode);
}

3. test.c

主函数

#include "SList.h"

int main()
{
	SListTest();
	return 0;
}

结果展示:

设计一个单链表,该单链表具有头插法创建链表、插入元素、删除元素、显示及取值等,数据结构,数据结构,链表,算法

总结

  对于链表来说有着自己的特点,比如:在内存中不需要连续的地址进行存储,元素之间通过指针相连、逻辑相邻但物理上并不一定相邻,插入与删除操作不需要移动大量的元素,但是无法随机存取,只能从头遍历。而对于不带头结点的单链表来说,尤其要注意对头指针的指向修改时要使用二级指针。

  文章若有不足的地方还请大佬指正!!!

设计一个单链表,该单链表具有头插法创建链表、插入元素、删除元素、显示及取值等,数据结构,数据结构,链表,算法文章来源地址https://www.toymoban.com/news/detail-826295.html

到了这里,关于【数据结构】单链表——单链表的定义及基本操作的实现(头插、尾插、头删、尾删、任意位置的插入与删除)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • 【数据结构】 循环单链表的基本操作 (C语言版)

    目录 一、循环单链表 1、循环单链表的定义: 2、循环单链表的优缺点: 二、循环单链表的基本操作算法(C语言)    1、宏定义  2、创建结构体 3、循环单链表的初始化  4、循环单链表的插入 5、求单链表长度 6、循环单链表的清空 7、循环单链表的销毁 8、循环单链表的取

    2024年01月22日
    浏览(60)
  • 数据结构 2.1 线性表的定义和基本操作

    数据结构三要素——逻辑结构、数据的运算、存储结构(物理结构) 线性表是具有 相同数据类型 的n(n=0)个数据元素的 有限序列 ,其中n为表长,当n=0时,线性表是一个空表。 每个数据元素所占空间一样大,有次序。 几个概念 1.ai是线性表中的第i个 i表示元素线性表中的

    2024年02月07日
    浏览(53)
  • 数据结构 线性表的定义和基本操作(以顺序表为例)

    名人说:一花独放不是春,百花齐放花满园。——《增广贤文》 作者:Code_流苏(CSDN) (一个喜欢古诗词和编程的Coder😊) 以下代码个人分享出来,仅供学习交流,且仅在CSDN平台发布,未经授权禁止二次转发。 〇、线性表是什么? 1、定义 线性表 是具有 相同数据类型 的 n(

    2024年02月12日
    浏览(63)
  • 【数据结构】线性表(一)线性表的定义及其基本操作(顺序表插入、删除、查找、修改)

    目录 一、线性表 1. 线性表的定义 2. 线性表的要素 二、线性表的基本操作 三、线性表的顺序存储结构 1. 定义 2. 顺序表的操作       a. 插入操作 b. 删除操作 c. 查找操作 d. 修改操作 e. 代码实例          一个线性表是由零个或多个 具有相同类型的结点 组成的有序集合。

    2024年02月03日
    浏览(70)
  • 数据结构——单链表基本操作实现 (c++)

    单链表链式存储结构的特点是:用一组任意的存储单元存储线性表的数据元素(这里存储单元可以是连续的,也可以是不连续的),为了表示每个数据元素a与其直接后继数据元素之间的逻辑关系,除了存储信息本身外还要存储一个指示其直接后继的信息(地址). 这两部分信

    2024年02月03日
    浏览(68)
  • 【数据结构】单链表基本操作:查找、插入、删除、创建

     链表由结点组成,结点由数据域和指针域组成。其中,数据域存放的就是数据元素,指针域存放下一个结点的地址。数据元素可以只有一个,也可以有多个不同类型的数据元素,甚至是数组。下图和代码来自《C Primer Plus》,该链表每个节结点同时含char类型和int类型。 ​​

    2024年02月02日
    浏览(63)
  • 数据结构——单链表上基本操作的实现

    1.按位序插入(带头结点) : ==ListInsert(L, i, e): ==在表L 中的第 i 个位置上插入指定元素 e = 找到第 i-1 个结点 ( 前驱结点 ) ,将新结点 插入其后;其中头结点可以看作第 0 个结点,故 i=1 时也适用。 typedef struct LNode{ ElemType data; struct LNode *next; }LNode, *LinkList; // 在第 i 个位置插入

    2024年01月21日
    浏览(57)
  • 数据结构--单链表的定义

    单链表的定义(如何用代码实现) 优点:不要求大片连续空间,改变容量方便 缺点:不可随机存取,要耗费一定空间存放指针 为了方便 我们经常使用 typedef t y p e d e f 数据类型 别名 color{purple}typedef 数据类型 别名 t y p e d e f 数据类型 别名 代码一: 代码二: 代码一与代码二是

    2024年02月11日
    浏览(62)
  • 【数据结构】单链表的定义和操作

    目录 1.单链表的定义 2.单链表的创建和初始化 3.单链表的插入节点操作 4.单链表的删除节点操作 5.单链表的查找节点操作 6.单链表的更新节点操作 7.完整代码 🌈嗨!我是Filotimo__🌈。很高兴与大家相识,希望我的博客能对你有所帮助。 💡本文由Filotimo__✍️原创,首发于CS

    2024年02月02日
    浏览(56)
  • 数据结构---双向链表的基本操作

    头插法 遍历链表 尾插法 头删法 尾删法 按位置插入数据 按位置删除数据 dooublelinklist.c doublelinklist.h doublemain.c

    2024年02月22日
    浏览(53)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包