数据结构链表——单链表

news2024/10/6 20:34:50

数据结构链表——单链表

  • 概念及结构
  • 单链表的实现
    • 结构体类型的定义和头文件
    • 接口函数
    • 打印链表
    • 创建新节点
    • 尾插
    • 头插
    • 尾删
    • 头删
    • 查找
    • 任意插入
      • 指定位置之前插入
      • 指定位置之后插入
    • 指定位置删除
    • 指定位置后删除
    • 单链表空间的销毁

概念及结构

概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。
类似于小火车
在这里插入图片描述
在这里插入图片描述
注意:

1.从上图可看出,链式结构在逻辑上是连续的,但是在物理上不一定连续
2.现实中的结点一般都是从堆上申请出来的
3.从堆上申请的空间,是按照一定的策略来分配的,两次申请的空间可能连续,也可能不连续

单链表的实现

结构体类型的定义和头文件

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

typedef int SLTDataType;//数据类型

typedef struct SListNode
{
	SLTDataType data;//数据
	struct SListNode* next;//指针
}SLTNode;//结构体类型

这里结构体数据类型的命名“SLT”为“Sequence List”的缩写 “Data”代表"数据",“Type”代表类型

接口函数

void SLTPrint(SLTNode* phead);//打印链表

SLTNode* BuySLTtNode(SLTDataType x);//创建新节点

void SLTPushBack(SLTNode** pphead, SLTDataType x);//尾插

void SLTPushFront(SLTNode** pphead, SLTDataType x);//头插

void SLTPopBack(SLTNode** pphead);//尾删

void SLTPopFront(SLTNode** pphead);//头删

SLTNode* SLTtFInd(SLTNode* phead, SLTDataType x);//查找

void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);//任意位置之前插入

void SLTErase(SLTNode** pphead, SLTNode* pos);//任意位置删除

void SLTInsertAfter(SLTNode* pos, SLTDataType x);//在指定位置后插入

void SLTEraseAfter(SLTNode* pos);//在指定位置后删除

打印链表

void SLTPrint(SLTNode* phead)//打印链表
{
	SLTNode* cur = phead;//定义一个结构体指针变量来遍历链表
	while (cur != NULL)
	{
		printf("%d->", cur->data);
		cur = cur->next;
	}
	printf("NULL\n");//打印尾指针NULL
}

创建新节点

因为后面的头插尾插需要创建新节点,所以干脆写个函数,避免代码的冗余

SLTNode* BuySLTNode(SLTDataType x)//创建新节点
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));//开辟空间
	if (NULL == newnode)//检查malloc:取消对NULL的引用
	{
		perror("BuySLTNode malloc");
		return -1;
	}
	newnode->data = x;//赋值x
	newnode->next = NULL;//尾指针为NULL
	return newnode;//返回头指针newnode
}

尾插

在这里插入图片描述
于是就有代码:

void SLTPushBack(SLTNode* phead, SLTDataType x)
{
	SLTNode* newnode = BuySLTNode(x);//创建要插入的节点
	//找尾
	SLTNode* cur = phead;//遍历链表
	while (cur->next != NULL)
	{
		cur = cur->next;
	}
	//找到后跳出循环此时cur->next=NULL
	cur->next = newnode;//链接新节点

}

在这里插入图片描述

测试后发现没有考虑到空链表的情况,这个代码并不能实现空链表的尾插
在这里插入图片描述
分情况讨论:

  • 空链表: 直接将新节点指针赋值到原来的头指,来修改传入的头指针。这时候就需要考虑到形参只是时实参的临时拷贝,要调用函数修改一级指针,那么传参传参就需要传二级指针。
    总结: 修改一级头指针就需要传参二级指针
  • 非空链表: 首先找尾指针,然后将新节点链接到尾指针

那么上正确代码:

void SLTPushBack(SLTNode** pphead, SLTDataType x)
{
	SLTNode* newnode = BuySLTNode(x);//创建要插入的节点
	if (*pphead == NULL)//如果为空链表
	{
		*pphead = newnode;//修改头指针
	}
	else//非空链表
	{
		//找尾
		SLTNode* tail = *pphead;//遍历链表
		while (tail->next != NULL)
		{
			tail = tail->next;
		}
		//找到后跳出循环此时tail->next=NULL
		tail->next = newnode;//链接新节点
	}
}

头插

首先先分析一下:

  • 空链表: 同尾插一样,直接将新节点赋值到传入的头节点即可
  • 非空链表
    在这里插入图片描述
    细节直接看代码:
void SLTPushFront(SLTNode** pphead, SLTDataType x)//头插
{
	assert(pphead);//防止传入空指针,对NULL的解引用
	SLTNode* newnode = BuySLTNode(x);
	if (*pphead == NULL)
	{
		*pphead = newnode;//将新节点赋值到头节点
	}
	else 
	{
		SLTNode* first = *pphead;//初始化第一个节点的指针
		newnode->next = first;//将新节点指向第一个节点
		*pphead = newnode;//将新节点赋值到头节点
	}
}

其实也可以这样写:
阅读性比较差

void SLTPushFront(SLTNode** pphead, SLTDataType x)//头插
{
	assert(pphead);//防止传入空指针,对NULL的解引用
	SLTNode* newnode = BuySLTNode(x);
	newnode->next = *pphead;//将原链表的头节点(第一个节点)赋值给新节点的尾指针
	*pphead = newnode; //再将原来链表的头节点改变,将新节点的头指针赋值给原链表的头节点
}

尾删

首先分析一下:
在这里插入图片描述

  • 如果链表为空 那肯定不能删,直接断言就好
  • 如果链表有一个节点 ,这时候找不到尾节点的前一个节点,所以单拿出来讨论,这时候直接释放头节点空间,然后将头节点置空,因为修改头节点所以要形参用二级指针
  • 链表节点个数大于1 这时候只需要找prev节点,然后释放prev->next,然后置空

细节直接看代码:

void SLTPopBack(SLTNode** pphead)
{
	assert(pphead);//防止传入空指针,对NULL的解引用
	assert(*pphead);//空链表不可以删除
	//只有一个节点的链表
	if ((*pphead)->next == NULL)
	{
		free(*pphead);//释放空间
		*pphead = NULL;
	}
	else//链表大于一个节点
	{
		//找尾节点和尾节点的前一个节点

		SLTNode* prev = *pphead;
		while (prev->next->next != NULL)
		{
			prev = prev->next;
		}
		//此时pren->next->next=NULL,prev->next为尾节点

		free(prev->next);//释放尾节点的空间		
		prev->next = NULL;//尾指针置为空
	}
}

头删

分析:
在这里插入图片描述
如果链表只有一个节点也需要修改头指针,所以传二级指针,细节直接看代码

void SLTPopFront(SLTNode** pphead)//头删
{
	assert(pphead);//防止传入空指针,对NULL的解引用
	assert(*pphead);//空链表不可以删除
	SLTNode* first = *pphead;//初始化第一个节点的数据
	*pphead= first->next;//头节点指向第一个节点的下一个节点
	free(first);//释放空间
	first = NULL;//野指针置为空
}

查找

查找就是对链表的遍历,没有对头指针的修改,所以只需要传一级指针就可以。
找到后返回对应的指针
找不到返回NULL

比较简单直接上代码:

SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{
	SLTNode* cur = phead;//初始化一个结构体指针遍历链表,意为当前指针,当然用头指针也可以哦
	while (cur != NULL)
	{
		if (cur->data == x)
		{
			return cur;
		}
		else
		{
			cur = cur->next;
		}
	}
	return NULL;
}

任意插入

实现单链表的随意位置插入可以在指定位置的前一个插入或者在指定位置后一个插入
任意插入函数声明

void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);
void SLTInsert(SLTNode** pphead, SLTDataType pos, SLTDataType x);

这两种其实大体思路一样,只是使用方式不同。
第一种就在主函数调用查找函数;第二种就是在插入函数中调用查找函数。

指定位置之前插入

分析:

  • 如果指定位置为第一个节点,那么即为头插,头插需要修改头节点,所以传二级指针
  • 链表大于一个节点
    在这里插入图片描述
    细节直接看代码:
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)//指定位置之前插入
{
	assert(pphead);
	assert(pos);
	if (pos == *pphead)
	{
		SLTPushFront(pphead, x);//头插
	}
	else
	{
		SLTNode* newnode = BuySLTNode(x);//创建新节点
		SLTNode* prev = *pphead;//初始化指定位置的前一个节点的指针
		//找到pos的前一个节点
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		//跳出循环时prev->next=pos 
		prev->next = newnode;//pos前一个节点链接新节点
		newnode->next = pos;//新节点链接pos节点
	}
}

指定位置之后插入

分析:

  • 在指定位置之后插入,就不需要传参头指针
  • 空链表也不能插入
    在这里插入图片描述
    比较简单直接上代码:
void SLTInsertAfter(SLTNode* pos, SLTDataType x)//在指定位置后插入
{
	assert(pos);//空链表不可以进行指定位置后插入
	SLTNode* newnode = BuySLTNode(x);//创建新节点
	newnode->next = pos->next;
	pos->next = newnode;//这里这两个的顺序必须为这样的
}

指定位置删除

分析:

  • 空链表不能删除,要删除的位置不为NULL(断言处理即可)
  • 删除位置为头指针,就是头删,需要改变头指针,所以穿二级指针
  • 正常位置分析如下:
    在这里插入图片描述
    细节直接看代码:
void SLTErase(SLTNode** pphead, SLTNode* pos)//指定位置位置删除
{

	assert(pphead);
	assert(pos);
	//sassert(*pphead);//pos不为空那么就间接证明不是空链表
	if (*pphead == pos)//要删除的位置为头指针
	{
		SLTPopFront(pphead);//头删
	}
	else//删除位置在第一个节点之后
	{
		//找pos的前一个节点prev
		SLTNode* prev = *pphead;//初始化为头指针,然后遍历找pos的前一个节点
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		prev->next = pos->next;//pos前一个节点链接pos后一个节点
		free(pos);//释放要删除位置的空间
		//pos = NULL;//野指针置空   这一步有没有无所谓,因为传入的pos为一级指针
		//函数内部改变并不会影响实参的值,所以调用完该函数后要将传入的pos置空,防止野指针的生成

	}
}

指定位置后删除

分析:

  • 在指定位置后删除,不需要传头指针
  • 空链表不可删除,pos不为空
  • 正常节点
    在这里插入图片描述
    比较简单直接上代码:
void SLTEraseAfter(SLTNode* pos)//在指定位置后删除
{
	assert(pos);
	SLTNode* del = pos->next;
	pos->next = del->next;
	free(del);
	del = NULL;
}

单链表空间的销毁

因为单链表的空间时动态分配的,内存放在堆区,没有在栈区,堆区的内存不会因为函数的调用结束而销毁,需要我们自行销毁
上代码:


void SLTDestroy(SLTNode** pphead)
{
	SLTNode* cur = *pphead;
	while (cur)//粗人指向尾节点NULL结束
	{
		SLTNode* temp = cur->next;//临时指针变量将cur->next的值存储起来
		free(cur);//释放空间
		cur = temp;//将cur指向下一个节点
	}
	*pphead = NULL;//将野指针置空
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/859736.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

PHP 使用ThinkPHP实现电子邮件发送示例

文章目录 首先我们需要设置我们的邮箱客户端授权&#xff0c;获取到授权码找到我们的邮箱设置去账号中找到这一堆服务&#xff0c;找到后开启smtp服务开启服务后管理服务 接下来需要去下载相应的第三方类库(我这里使用的是PHPMailer)在thinkPHP中封装一下邮件服务类实际调用效果…

Java 常用编辑器 IntelliJ IDEA

文章目录 IDEA 概述IDEA 下载和安装IDEA 中的第一个代码IDEA 的项目和模块操作&#xff08;一&#xff09;类的操作&#xff08;二&#xff09;模块的操作&#xff08;三&#xff09;项目的操作 IDEA 概述 IntelliJ IDEA是一款由JetBrains开发的集成开发环境&#xff08;IDE&am…

Android T 窗口层级其二 —— 层级结构树的构建(更新中)

如何通过dump中的内容找到对应的代码&#xff1f; 我们dump窗口层级发现会有很多信息&#xff0c;adb shell dumpsys activity containers 这里我们以其中的DefaultTaskDisplayArea为例 在源码的framework目录下查找该字符串&#xff0c;找到对应的代码就可以通过打印堆栈或者…

计算机的构造和原理

本资料转载于B站up主芯片超人-花 仅用于学习和讨论&#xff0c;如有侵权请联系 计算机工作原理之3D动画揭秘&#xff1a;计算机内部如何工作_哔哩哔哩_bilibili 1.CPU的部分 1.1 CPU放大看 1.2 一个芯片中&#xff0c;有80亿至100亿晶体管 1.3 放大磁道 1.4 共享3级缓存 1.5 …

kafka是有序的吗?如何保证有序?

首先&#xff0c;Kafka无法保证消息的全局有序性&#xff0c;这是因为Kafka的设计中允许多个生产者并行地向同一个主题写入消息。而且&#xff0c;一个主题可能会被划分为多个分区&#xff0c;每个分区都可以在独立的生产者和消费者之间进行并行处理。因此&#xff0c;生产者将…

C字符串与C++ string 类:用法万字详解(下)

目录 引言 一、string类对象的修改操作 1.1 push_back() 1.2 append() 1.3 operator() 1.4 c_str() 1.5 substr() 1.6 find() 1.7 rfind() 二、string类非成员函数 2.1 operator() 2.2 operator<<() 2.3 operator>>() 2.4 getline() 2.5 relational …

TFRecords详解

内容目录 TFRecords 是什么序列化(Serialization)tf.data 图像序列化&#xff08;Serializing Images)tf.Example函数封装 小结 TFRecords 是什么 TPU拥有八个核心&#xff0c;充当八个独立的工作单元。我们可以通过将数据集分成多个文件或分片&#xff08;shards&#xff09;…

phpstorm添加vue 标签属性绑定提示和提示vue的方法提示

v-text v-html v-once v-if v-show v-else v-for v-on v-bind v-model v-ref v-el v-pre v-cloak v-on:click v-on:keyup.enter v-on:keyup click change input number debounce transition :is :class把上面这些文字粘贴到点击右下角放大按钮 后的文本框里&#xff0c;然后保存…

混合云环境中 Kubernetes 可观测性的 6 个有效策略...

2023 年&#xff0c;原生云应用程序和平台将快速增长。组织不断努力最大限度地发挥其应用程序的潜力&#xff0c;确保无缝的用户体验并推动业务增长。 混合云环境的兴起以及 Kubernetes 等容器化技术的采用彻底改变了现代应用程序的开发、部署和扩展方式。 在这个数字领域&am…

Redis 搭建分片集群

文章目录 0.10.2 散列插槽0.3 集群伸缩0.3.1 需求分析0.3.1 创建新的 Redis 实例0.3.3 添加新节点到 Redis0.3.4 转移插槽 0.4 故障转移0.4.1 自动故障转移0.4.2 生动故障转移 0.5 RedisTemplate访问分片集群 1. 集群架构2. 准备实例和配置3. 启动4. 创建集群5. 测试 0.1 主从…

刷新缓冲区(标准IO)

标准IO是带缓冲的&#xff0c;输入和输出函数属于行缓冲&#xff0c;stdin、stdin、printf、scanf 1.换行符刷新 2.缓冲区满刷新 3.fflush函数强制刷新 4.程序正常结束

绩效考核,职场人的痛!

绩效&#xff0c;已经成为职场人不能跳过的话题。 绩效作为提高员工和企业效率的有效手段&#xff0c;已经被越来越公司采用&#xff0c;现在&#xff0c;公司里几乎任何一个岗位都会被考核&#xff0c;特别是互联网公司。今天我们以产品经理为例&#xff0c;看看这个岗位的绩效…

苍穹外卖day11笔记

今日首先介绍前端技术Apache ECharts&#xff0c;说明后端需要准备的数据&#xff0c;然后讲解具体统计功能的实现&#xff0c;包括营业额统计、用户统计、订单统计、销量排名。 一、ECharts 是什么 ECharts是一款基于 Javascript 的数据可视化图表库。我们用它来展示图表数…

leetcode每日一练-第121题-买卖股票的最佳时机

一、思路 动态规划 二、解题方法 维护两个变量&#xff1a;一个表示当前最低的股票价格 minPrice&#xff0c;另一个表示当前最大的利润 maxProfit。 遍历数组中的每个价格&#xff0c;对于每个价格&#xff0c;更新 minPrice 和 maxProfit。具体做法是&#xff0c;如果当前…

【Linux】TCP协议——传输层

目录 TCP协议 谈谈可靠性 TCP协议格式 序号与确认序号 窗口大小 六个标志位 确认应答机制&#xff08;ACK&#xff09; 超时重传机制 连接管理机制 三次握手 四次挥手 流量控制 滑动窗口 拥塞控制 延迟应答 捎带应答 面向字节流 粘包问题 TCP异常情况 TC…

【RTT驱动框架分析06】-pwn驱动框架分析+pwm驱动实现

pwm pwm应用程序开发 访问 PWM 设备API 应用程序通过 RT-Thread 提供的 PWM 设备管理接口来访问 PWM 设备硬件&#xff0c;相关接口如下所示&#xff1a; 函数描述rt_device_find()根据 PWM 设备名称查找设备获取设备句柄rt_pwm_set()设置 PWM 周期和脉冲宽度rt_pwm_enable…

橙河网络:2023年,我看谁还在做实体行业?

大家好&#xff0c;我是橙河老师&#xff0c;今天讲一讲实体行业。 现在实体行业还好干吗&#xff1f; 肯定是不好干了。 别的不扯&#xff0c;这几年很多大佬&#xff0c;能把老百姓干的事儿都干了。 一天收入上百万的演员&#xff0c;在直播间卖着九块九的东西&#xff0…

学习笔记-JVM-工具包(JVM分析工具)

常用工具 JDK工具 ① jps: JVM Process status tool&#xff1a;JVM进程状态工具&#xff0c;查看进程基本信息 ② jstat: JVM statistics monitoring tool &#xff1a; JVM统计监控工具&#xff0c;查看堆&#xff0c;GC详细信息 ③ jinfo&#xff1a;Java Configuration I…

MATLAB实现两组数据的延时对齐效果

博主在某次实验中&#xff0c;相同的实验条件下分别采集了两组数据&#xff0c;发现两组数据存在一个延时&#xff0c;如下图所示&#xff1a; 本文记录消除这个延时&#xff0c;实现相同数据状态的对齐效果&#xff0c;采用MATLAB自带的xcorr函数实现&#xff0c;具体步骤如下…

vteam透明屏,在场景化应用中,有哪些特点表现?

vteam透明屏是一种新型的显示技术&#xff0c;它采用透明材料制成&#xff0c;可以在显示内容的同时保持背景的透明度。 这种屏幕可以应用于各种领域&#xff0c;如广告、零售、展览等&#xff0c;具有很大的潜力和市场前景。 vteam透明屏的特点之一是其高透明度。与传统的显…