数据结构链表——单链表

数据结构链表——单链表

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

概念及结构

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

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;//赋值xnewnode->next = NULL;//尾指针为NULLreturn newnode;//返回头指针newnode
}

尾插

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

void SLTPushBack(SLTNode* phead, SLTDataType x)
{SLTNode* newnode = BuySLTNode(x);//创建要插入的节点//找尾SLTNode* cur = phead;//遍历链表while (cur->next != NULL){cur = cur->next;}//找到后跳出循环此时cur->next=NULLcur->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=NULLtail->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的前一个节点prevSLTNode* 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.hqwc.cn/news/60824.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MNIST数据集知识合集

MNIST数据集知识合集 认识MNIST数据集通过本地文件加载MNIST数据集torchvision.datasets加载MNIST数据集可视化&#xff08;即转换成.jpg/.png之类的文件&#xff09;疑惑—datasets.mnist和datasets.MNIST问题—downloadFalse运行报错 搭建CNN用于数字识别 认识MNIST数据集 M…

web前端之CSS

文章目录 一、CSS简介1.1 CSS语法规则 二、CSS的引用方法2.1 定义行内样式表2.2定义内部样式表2.3链入外部样式表2.4导入外部样式表 三、CSS选择符3.1 基本选择符3.1.1 标签选择符3.1.2 class类选择符3.1.3 id选择符 3.2 复合选择符3.2.1 交集选择符&#xff08;合并选择器&…

Linux MQTT智能家居项目(智能家居界面布局)

文章目录 前言一、创建工程项目二、界面布局准备工作三、正式界面布局总结 前言 一、创建工程项目 1.选择工程名称和项目保存路径 2.选择QWidget 3.添加保存图片的资源文件&#xff1a; 在工程目录下添加Icon文件夹保存图片&#xff1a; 将文件放入目录中&#xff1a; …

AIGC 浪潮下,鹅厂新一代前端人的真实工作感受

点击链接了解详情 原创作者&#xff1a;张波 腾小云导读 AIGC 这一时代潮流已然不可阻挡&#xff0c;我们要做的不是慌乱&#xff0c;而是把握住这个时代的机会。本文就和大家一起来探索在 AIGC 下&#xff0c;前端工程师即将面临的挑战和机遇。聊聊从以前到现在&#xff0c;A…

浅谈AI浪潮下的视频大数据发展趋势与应用

视频大数据的发展趋势是多样化和个性化的。随着科技的不断进步&#xff0c;人们对于视频内容的需求也在不断变化。从传统的电视节目到现在的短视频、直播、VR等多种形式&#xff0c;视频内容已经不再是单一的娱乐方式&#xff0c;更是涉及到教育、医疗、商业等各个领域。 为了满…

云原生Kubernetes:阿里云托管k8s集群ACK创建和使用

目录 一、理论 1.容器服务Kubernetes版 2.ACK Pro版集群概述 3.ACK版本说明 二、实验 1.创建专有版Kubernetes集群 三、问题 1.依赖检查未通过 一、理论 1.容器服务Kubernetes版 &#xff08;1&#xff09;概念 阿里云容器服务Kubernetes版&#xff08;Alibaba Cloud…

react-virtualized可视化区域渲染的使用

介绍 github地址&#xff1a;https://github.com/bvaughn/react-virtualized 实例网址&#xff1a;react-virtualized如果体积太大&#xff0c;可以参考用react-window。 使用 安装&#xff1a; yarn add react-virtualized。在项目入口文件index.js中导入样式文件&#xff…

进阶:Docker容器管理工具——Docker-Compose使用

文章目录 前言Compose大杀器编排服务 1、docker-compose安装curl方式安装增加可执行权限查看版本 2、Docker-compose.yaml命令3、 docker-compose实战4、Docker网络路由docker的跨主机网络路由**问题由来**:方案两台机分别配置路由表ip_forward配置 总结 前言 容器的管理工具&…

惊讶,日本用“Excel”作画,中国却用“Excel”造“另类”软件

精益求精 表格是一项伟大的创造&#xff0c;它的出现改变了人类记录、分析和展示数据的方式。 随着科技的日益月异的更新&#xff0c;从最早的纸质表格到现代化的电子表格&#xff0c;人类对表格的改造也在不断的升级和进步。 最初的纸质表格的简单记录&#xff0c;无法进行复…

机器人CPP编程基础-03变量类型Variables Types

机器人CPP编程基础-02变量Variables 全文AI生成。 C #include<iostream>using namespace std;main() {int a10,b35; // 4 bytescout<<"Value of a : "<<a<<" Address of a : "<<&a <<endl;cout<<"Val…

数据结构——双向链表

双向链表实质上是在单向链表的基础上加上了一个指针指向后面地址 单向链表请参考http://t.csdn.cn/3Gxk9 物理结构 首先我们看一下两种链表的物理结构 我们可以看到&#xff1a;双向在单向基础上加入了一个指向上一个地址的指针&#xff0c;如此操作我们便可以向数组一样操作…

六、web应用程序技术——编码

文章目录 一、状态与会话二、编码方案2.1 URL编码2.2 Unicode编码2.3 HTML编码2.4 Base64编码2.5 十六进制编码 一、状态与会话 web应用程序服务器和客户端组件除了以各种方式进行数据交换和处理&#xff0c;应用程序还需要追踪每位用户通过不同的请求与应用程序交互的状态。例…