返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >C语言一篇精通链表的各种操作
  • 590
分享到

C语言一篇精通链表的各种操作

2024-04-02 19:04:59 590人浏览 独家记忆
摘要

目录前言一、链表的介绍1.什么是链表2.链表的分类2.1.根据方向2.2.头结点2.3.循环/非循环 二、链表的实现1.结构体2.开辟结点3.打印4.尾插5.头插6.测试7

前言

关于线性表的一些相关介绍,大家可以看看我们之前写的点我-链表

点我-顺序表,里面有一些相关的知识介绍,都是比较基础的,一些比较常见的操作里面也有具体的介绍与实现到,然后呢,今天我们学习的是链表,相比于之前的操作实现更加具有深度,对于一些比较简单的操作这里就不加说明了。而且涉及到之前未说到的一些知识,对此我们可以强化对其认识,这就是写这篇博客的目的。

编译工具:vs2019,小伙伴们可以一起跟着来敲敲代码。

开始之前:很有必要提醒大家注意二级指针的使用,为什么会用到二级指针,我的博客也有一些相关介绍,简单来说,传值参数并不改变实参,传址参数改变形参。

一、链表的介绍

1.什么是链表

简单来说,就是一条链子连接成的表,上面的链接也有比较正式规矩的介绍。

与顺序表相比,链表的最大特点就是不要求物理空间连续,插入不需要移动大量的数据

 怎么去联系各个结点呢?

从上图其实不难发现,搞个指针连接起来就行了。既要有数据域和指针域,注意一点:最后一个元素的指针域为NULL。上面的箭头实际并不存在,只是为了看起来比较直接,形象化起来。那要怎么去表示出来了?可以用结构体的自引用。

2.链表的分类

之前并没说到链表的类型有哪些,根据类型的不同,我们实际上可以对其进行分类,由于都是基于单链表实现操作,因此需要学好单链表,进行深化学习。

2.1.根据方向

单向/双向链表

2.2.头结点

带头结点/不带头结点

2.3.循环/非循环

 二、链表的实现

链表的实现当然离不开我们自己动手去敲代码了,这首先需要准备好我们的编译环境,vs2019,同时,每次写完一块模板,我们要去测试一下有没有bug,方便我们去找错误,进行调试,这样会大大减少我们的工作量。

1.结构体

链表我们该如何去表示呢?其实,通过上面的例子,我们大致已经知道,需要一个地方来存放数据,另一个地方存放下一个结点的地址。我们可以通过结构体来定义,具体代码如下:

#include <stdio.h>
typedef int SLTDataType;
typedef struct SListnode
{
	SLTDataType data;
	struct SListNode* next;
}SLTNode;

2.开辟结点

后续操作会频繁动态开辟一个头结点,我们不妨把它封装成一个函数,便于后面方便使用。当然,你如果觉得自己每次都可以自己写的话,也不必写成一个函数。

//创建新结点
SLTNode* BuySListNode(SLTDataType x) {
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	newnode->data = x;
	newnode->next = NULL;
	return newnode;
}

注意点:新结点的指针域置为空!

3.打印

先别急,我们先来试试水,先尝试自己动手写一下打印的函数。

这里为了比较更加形象起来,每次打印的时候都会用->来连接,同时,最后用NULL结尾

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

4.尾插

什么是尾插?根据字面意思,就是将新结点插入到到链表的尾部。

为了让大家更好理解,特地上网找了一张图片,一起来看看把

 每一次插入一个数都放在最后一个位置,同时,最后一个结点的指针域置为空,关键的就是,我们如何找到当前链表的尾结点呢?前面已经说了,最后一个结点的指针域为空,我们可以以此为突破点。注意:当链表为空时,你会怎么处理?想想。这里先不说了,直接看看我们的代码。

//尾插
void SListPushBack(SLTNode** pphead, SLTDataType x)
{
	SLTNode* newnode = BuySListNode(x);
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else {
		SLTNode* tail = *pphead;
		while (tail->next != NULL)
		{
			tail = tail->next;
		}
		tail->next = newnode;
	}
}

细心的你应该注意到了,这里我们使用的都是二级指针pphead

因为假设我们使用一级指针,直接传入头指针phead时,头指针本身就是一级指针的了,当我们需要更改该指针指向的地址时,改动只会在函数内部生效,main函数中的phead指针并没有被改变。要想改变的话,就需要二级指针来进行操作了

5.头插

有尾插自然就会有头插,相比较与尾插而言,头插显得更加简单了,直接把新的结点放在头结点前不就ok了?

//头插 
void SListPushFront(SLTNode** pphead, SLTDataType x)
{
	SLTNode* newnode = BuySListNode(x);
	newnode->next = *pphead;
	*pphead = newnode;
}

6.测试

好了,到这里,我们已经有一些函数了,不急,我们先来测试测试效果如何

void TestSList1()
{
	SLTNode* plist = NULL;
	SListPushBack(&plist, 1);
	SListPushBack(&plist, 2);
	SListPushBack(&plist, 3);
	SListPushBack(&plist, 4);
	SListPushFront(&plist, 0);
	SListPrint(plist);
}
int main()
{
TestSList1();
}

运行结果如下:

 我们必须养成边写代码边测试的习惯,这有利于我们及时发现自己的错误,不易导致后面出现一大堆bug而自己却不知道错在哪。当然,除此之外,我们还可以通过调试的方法,快速准确发现自己的bug,这也是我们需要养成的。这些都是需要我们去关注的点。

好啦,下面我不会在像上面那么详细的说明了,我们直接来个头删尾删

7.头删/尾删

有头插尾插,自然有头删尾删,其实,不知道你们发现,不管是插还是删,关于头部的操作都是比较简单了,我们先来个开胃菜,头删:可不能直接删哦,我们要记录头结点的下一个位置,如何直接删了头结点的话,那就麻烦,会造成野指针的,自己好好捋捋。

//头删
void SListpopFront(SLTNode**pphead)
{
	SLTNode* next = (*pphead)->next;
	free(*pphead);
	*pphead = next;
}

尾删:说起尾删,就要多注意点了,要看具体情况而言了,直接来看代码把

//尾删
void SListPopBack(SLTNode** pphead)
{
	//链表为空
	if (*pphead == NULL)
	{
		return;
	}
	//只有一个结点
	else if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	//有一个以上的结点
	else {
		SLTNode* prev = NULL;
		SLTNode* tail = *pphead;
		while (tail->next != NULL)
		{
			prev = tail;
			tail = tail->next;
		}
		free(tail);
		prev->next = NULL;
	}
}

尾删的关键点在于找到最后一个结点,最后一个结点的指针域为空。

1.链表为空时,不需要删,

2.链表只有一个结点,那它自己就是最后一个结点了,

3.多个结点就按常规处理就ok了,该说清楚的还是要说清楚的。 

8.查找

查找这个操作其实是比较简单的,在这里说是为了后面的使用,想要找到摸个元素,直接去调用函数即可,不用自己一次次去遍历。

SLTNode* SListFind(SLTNode* phead, SLTDataType x)
{
	SLTNode* cur = phead;
	while (cur)
	{
		if (cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}

9.在pos的前面插入x

除了基本的头尾增加,我们还可能还需要在某一个特定节点前后进行插入,这就需要我们玩转起来了,变得更加灵活。

两个核心点:

1.pos 的位置

2.插入的操作(这里可能有的同学会有一些疑惑,其实只要知道一点,插入的位置是已经知道的了)

//在pos的前面插入x
void SListInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
	if (pos == *pphead) {
		SListPushFront(pphead,x);
	}
	else {
		SLTNode* newnode = BuySListNode(x);
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		prev->next = newnode;
		newnode->next = pos;
	}
}

10.删除pos位置的值

关键的一点,如何找到pos,找到之后链表跳过它,然后删除即可。

//删除pos位置的值
void SListErase(SLTNode** pphead, SLTNode* pos)
{
	if (pos == *pphead)
	{
		SListpopFront(pphead);
	}
	else
	{
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		prev->next = pos->next;
		free(pos);
	}
}

三、主函数Test

这个没啥好说的,自己可以去试试

这只是单纯的试试函数能不能调用起来,自己可以动气手来试一试

结束语

好啦,这次想说的主要都讲完了,其实学数据结构除了实现之外,我们还需要及时去刷一些OJ题,提高我们的能力,使自己的知识融会贯通起来?

到此这篇关于C语言一篇精通链表的各种操作的文章就介绍到这了,更多相关C语言链表内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: C语言一篇精通链表的各种操作

本文链接: https://lsjlt.com/news/146928.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • C语言一篇精通链表的各种操作
    目录前言一、链表的介绍1.什么是链表2.链表的分类2.1.根据方向2.2.头结点2.3.循环/非循环 二、链表的实现1.结构体2.开辟结点3.打印4.尾插5.头插6.测试7...
    99+
    2024-04-02
  • C语言各种操作符透彻理解上篇
    前言:在我们程序编写领域,操作符给我们提供了很多的运算便利,但操作符琳琅满目,我们要怎样用好它们呢,下面就带你来熟悉熟悉这些多样的操作符。 操作符分类: 算术操作符 、移位操作符 、...
    99+
    2024-04-02
  • C语言各种操作符透彻理解下篇
    1.单目操作符 之前有了解到的三目操作符(?:),指的是有三个操作数 例如:3+5 其中,+是一个操作符 3是左操作数 5是有操作数 +则是一个双目操作符 那么什么是单目操作符呢,也...
    99+
    2024-04-02
  • C语言详解无头单向非循环链表各种操作方法
    目录链表引入链表介绍创建链表打印链表创建结点单链表尾插单链表头插单链表尾删单链表头删在pos位置之前插入数据在pos位置之后插入数据删除pos位置结点删除pos位置之后的结点销毁链表...
    99+
    2024-04-02
  • C语言数组的各种操作梳理
    目录一、一维数组1.创建2.初始化3.使用4.数组在内存中的存储5.数组大小的计算二、二维数组1.创建2.初始化3.使用4.二维数组在内存中的存储三、数组作为函数参数1.关于数组名是...
    99+
    2024-04-02
  • C语言超详细讲解顺序表的各种操作
    目录顺序表是什么顺序表的结构体顺序表的接口函数顺序表相关操作的菜单顺序表的初始化添加元素陈列元素往最后加元素往前面加元素任意位置加元素删除最后元素删除前面元素 删除任意元素...
    99+
    2024-04-02
  • C语言链表的操作有哪些
    这篇“C语言链表的操作有哪些”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“C语言链表的操作有哪些”文章吧。前言编译工具:vs...
    99+
    2023-06-30
  • C语言中双链表的基本操作
    目录带头结点的双向循环链表基本操作创建销毁打印尾插法尾删头插头删查找元素位置任意位置插入任意位置删除完整代码及测试总结带头结点的双向循环链表 链表结构如下: 每个节点都有一个数据域和...
    99+
    2023-02-05
    C语言双链表 双链表的基本操作 C语言双链表操作
  • C语言如何实现单链表操作
    本篇内容介绍了“C语言如何实现单链表操作”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!1 链表的概念及结构概念:链表是一种物理存储结构上非连...
    99+
    2023-06-29
  • C语言各种符号的使用介绍上篇
    目录1、注释符号1.1 注释的基本注意事项1.2 如何写出好的注释2、接续符和转移符2.1 续行功能2.2 转义字符3、单引号和双引号3.1 基本概念3.2 特殊情况4、逻辑操作符4...
    99+
    2022-11-13
    C语言符号 C语言符号的使用
  • C语言各种符号的使用介绍下篇
    目录1、按位运算符1.1 按位或( | )和按位与( & )1.2 按位异或( ^ )1.3 一个关于整型提升的问题2、移位操作符2.1 左移<< 右移>&...
    99+
    2022-11-13
    C语言符号 C语言符号的使用
  • 关于数据结构单向链表的各种操作
    目录关于节点数据添加:尾添加头添加一次性添加n个x数据节点:关于查找:根据指定数据:根据下标查找:删除头节点:删除尾节点:删除中间节点:删除全部节点:关于节点数据添加: 尾添加 最核...
    99+
    2023-05-15
    数据结构 数据结构单向链表
  • C语言中各种操作符的详细介绍(纯干货!)
    目录算术操作符移位操作符位操作符赋值操作符 单目操作符(类型)   强制类型转换       ...
    99+
    2024-04-02
  • C语言循环链表的原理与使用操作
    目录一.引入二.循环链表的定义三.单链表与循环链表对比3.1图示对比3.2代码对比四.循环链表的操作4.1循环链表的初始化4.2循环链表的建立4.2.1头插法建立循环链表4.2.2尾...
    99+
    2024-04-02
  • C语言双向链表的原理与使用操作
    目录一.引入二.双向链表的定义三.双向链表与单链表对比3.1图示对比3.2代码对比四.双向链表的操作4.1双向链表的创建4.2双向链表的插入4.3双向链表的删除4.4双向链表的销毁五...
    99+
    2024-04-02
  • C语言实现单链表的基本操作分享
    目录导语单链表单链表的特点定义初始化操作头插法尾插法删除第i个元素在第i个位置插入导语 无论是顺序存储结构还是链式存储结构,在内存中进行存放元素的时候,不仅需要存放该元素的相关信息,...
    99+
    2022-11-13
    C语言单链表基本操作 C语言单链表
  • ​​​​​​​C语言实现单链表基本操作方法
    目录存储结构基本功能头插法创建单链表尾插法创建单链表获取指定位置的元素在指定位置插入元素删除指定位置的元素获取单链表的长度合并两个非递减的单链表晴链表遍历打印单链表附上完整代码存储结...
    99+
    2024-04-02
  • 一篇文章带你入门C语言:操作符
    目录操作符分类算术操作符移位操作符整数存储规则左右移位规则赋值操作符单目操作符取地址操作符& 解引用操作符*类型长度操作符sizeof按位取反操作符~++ -- 操作符强制类...
    99+
    2024-04-02
  • 一篇带你了解C语言--位操作详情
    目录二进制数、位和字节二进制整数有符号整数二进制浮点数介绍八进制和十六进制C按位运算符按位逻辑运算符二进制反码或按位取反:~按位与:&按位或:|按位异或:^用法:掩码用法:打...
    99+
    2024-04-02
  • 一篇文章带你了解C语言操作符
    目录一、操作符分类 二、算术操作符三、移位操作符1、左移操作符 2、右移操作符2.1算术移位 2.2逻辑移位 四、位操作符 1、按位...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作