【C语言15】单链表,(对于二级指针与一级指针应用的详细讲述)

文章目录

  • 单链表
    • 1.单链表的介绍
    • 2.单链表的实现
        • 2.1.1单链表结点的创建与销毁
        • 2.1.2单链表尾插
        • 2.1.3单链表打印
        • 2.1.4尾删
        • 2.1.5头插
        • 2.1.6头删
        • 2.1.7查找
        • 2.1.8在pos位置之后插入数据
        • 2.1.9删除pos位置

单链表

1.单链表的介绍

链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表
中的指针链接次序实现的 。

在上篇博客中,我们可以很清晰的看到顺序表的结构,但是链表不可以,链表的链接就是由指针指引的,

一个数据,他可能间隔着N个内存空间,但是它们却又是实实在在相连的,为了详细说明链表,我准备这么几张图片:
在这里插入图片描述

我们看到,单链表就像一个火车,由一个指针来确定他们不同节点的位置,从而串接起来,但是我们要了解一件事,图片中的箭头是虚拟的,是想象的,我们能确定下一个节点位置只有指针;

2.单链表的实现

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<malloc.h>
typedef int SeDataType;
//定义结点
typedef struct SeqList
{SeDataType data;struct SeqList* next;
}SeNode;
//开辟节点
SeNode* BuySeqListNode(SeNode* phead, SeDataType x)
//销毁
void SeqListDestroy(SeNode** phead);
//打印
void SeqListPrint(SeNode* phead);
//尾插
void SeqListPushBack(SeNode** phead, SeDataType x);
//尾删
void SeqLIstPopBack(SeNode** phead);
//头插
void SeqListPushFront(SeNode** phead, SeDataType x);
//头删
void SeqListPopFront(SeNode** phead);
//pos位置之后插入
void SeqListInsterAfter(SeNode* pos, SeDataType x);
//删除pos位置
void SeqListEraseAfter(SeNode* pos);

为了实现单链表,我们需要实现以上接口

2.1.1单链表结点的创建与销毁

//初始化
SeNode* BuySeqListNode(SeDataType x)
{SeNode* newnode = (SeNode*)malloc(sizeof(SeNode));//创建结点if (newnode == NULL)//判断结点是否生成成功{perror("BuySeqListNode");exit(-1);}newnode->data = x;//赋值newnode->next = NULL;return newnode;//返回结点地址
}
//销毁
void SeqListDestroy(SeNode** phead)
{assert(phead);//判断是否为空while (*phead)//循环free空间{SeNode* next = (*phead)->next;//记录下一个节点,避免链接丢失free(*phead);//释放*phead = next;}*phead = NULL;//置空
}

当读完这串代码后,我们会发现这两个函数使用了完全不一样的指针类型.开辟节点使用的是一级指针,销毁结点使用的是二级指针.要注意,这是本篇博客难度最高的知识点,考验各位对指针的理解程度

在这里插入图片描述

在调用PushBack函数后,我们成功创建了一个节点node,我们在此时这个结点是孤立的
在这里插入图片描述

那么如何让让plist和node链接起来呢.那就是指针.,在此时,如果我们想要用pilst链接node,必须传递plist的地址,只有传递地址才能让plist的指向改变.如果不传递地址,后续操作只是一份临时拷贝

此时,一个纠结点就出现了.我plist本事就是指针,为什么要传递指针的地址呢.

我们来看一张图片

这张图生动的展示了指针本质上的区别.指针也是一种变量,是变量就会有地址.想要改变变量,就必须通过地址改变;在对plist进行修改的时候,虽然plist就是指针,但想要改变他存贮的值,就必须传递本身的地址,这就是为什么传递二级指针的原因.

2.1.2单链表尾插

如何进行尾插操作呢,我们要想明白一件事情,在我们创建的Sqnode类型中, next指向下一个结点.所以,如果我们想要尾插,必须找到next的前一个结点进行链接

void SeqListPushBack(SeNode** phead, SeDataType x)
{assert(phead);//判断指针是否为空SeNode* node = BuySeqListNode(x);//开辟节点if (*phead == NULL)//如果*phead为空,代表链表为空,之间赋值替换即可{*phead = node;}else{SeNode* tail = *phead;while (tail->next != NULL)//如果不为空,则遍历链表,在最后一个结点进行链接{tail = tail->next;}tail->next = node;}
}

此时,我们完成了尾插接口的实现,如何判断程序是否正确呢,我们可以通过调试直观地观察,也可以提前将打印接口实现观察,我们使用实现打印接口.

2.1.3单链表打印

应为我们只是要打印而无需任何改变指针的操作,所以只需要传递一级指针遍历整个数组即可

void SeqListPrint(SeNode* phead)
{SeNode* cur = phead;//记录位置while (cur){printf("%d ", cur->data);//打印cur = cur->next;//赋值遍历}
}

在这里插入图片描述

程序正确,成功运行

2.1.4尾删

尾删的操作与尾插类似,不同的是我们需要遍历到倒数第二个结点,通过倒数第二个节点的next来释放最后一个节点达成尾删操作.因为需要改变内容,所以依旧传递二级指针
在这里插入图片描述

void SeqListPopBack(SeNode** phead)
{assert(phead);//判断指针是否为空assert(*phead);//判断指针指向的内容是否为空if ((*phead)->next == NULL)//如果只有一个节点,直接释放{free(*phead);*phead = NULL;}else{SeNode* tail = *phead;while (tail->next->next != NULL)//如果多个,遍历到末尾的前一个释放{tail = tail->next;}free(tail->next);tail->next = NULL;                       }
}

在这里插入图片描述

2.1.5头插

头插需要注意的是对于指针的操作,在有多个节点时,需要考虑如何在头插的同时链接原头结点

而在一个节点是直接替换赋值即可.

在这里插入图片描述

void SeqListPushFront(SeNode** phead, SeDataType x)
{assert(phead);//判断指针是否为空SeNode* newnode = BuySeqListNode(x);//创建新节点if (*phead == NULL)//当只有一个节点的时候,直接赋值{*phead = newnode;}else//多个节点是先链接在赋值{newnode->next = *phead;*phead = newnode;}
}

在这里插入图片描述

运行成功

2.1.6头删

头删要注意的与头插类似,直接上图
在这里插入图片描述

//头删
void SeqListPopFront(SeNode** phead)
{assert(phead);//判断指针是否为空assert(*phead);//判断指针指向的内容是否为空if ((*phead)->next == NULL)//只有一个节点时,直接释放{free(*phead);*phead = NULL;}else//多个节点时,记录新头结点,释放原头结点{	SeNode* pphead = (*phead)->next;free(*phead);*phead = pphead;}
}

在这里插入图片描述

运行成功

2.1.7查找

该接口原理与打印接口相同,同是遍历链表,不同点为该函数会返回一个地址

SeNode* SeqListFind(SeNode* phead,SeDataType x)
{assert(phead);//判断指针是否为空SeNode* cur = phead;//赋值遍历while (cur->next != NULL){if (cur->data == x)//判断相等{return cur;//相等返回地址}cur = cur->next;}return NULL;	//不等返回NULL;
}

在这里插入图片描述

2.1.8在pos位置之后插入数据

在这里插入图片描述

void SeqListInsterAfter(SeNode**phead,SeNode* pos, SeDataType x)
{assert(pos);//判断指针是否为空assert(*phead);if (pos == *phead)//如果相等,就是头插{SeqListPushFront(phead,x);}else{SeNode* node = BuySeqListNode(x);SeNode* pphead = pos->next;//记录原结点链接的数据node->next = pphead;//链接pos->next = node;}
}

在这里插入图片描述

修改成功

2.1.9删除pos位置

在这里插入图片描述

void SeqListEraseAfter(SeNode** phead, SeNode* pos)
{assert(pos);//判断空指针if (pos==*phead)//如果相等就是头删{SeqListPopFront(phead);}else{SeNode* cur = *phead;while (cur->next != pos)//记录pos的前一个位置{cur = cur->next;}cur->next = pos->next;//链接free(pos);//释放pos = NULL;}
}

完整代码

//SeqList.h
#define _CRT_SECURE_NO_WARNINGS 1
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<malloc.h>
typedef int SeDataType;
//定义结点
typedef struct SeqList
{SeDataType data;struct SeqList* next;
}SeNode;
//初始化
void SeqListInit(SeNode** phead);
//销毁
void SeqListDestroy(SeNode** phead);
//打印
void SeqListPrint(SeNode* phead);
//尾插
void SeqListPushBack(SeNode** phead, SeDataType x);
//尾删
void SeqListPopBack(SeNode** phead);
//头插
void SeqListPushFront(SeNode** phead, SeDataType x);
//头删
void SeqListPopFront(SeNode** phead);
//pos位置之后插入
void SeqListInsterAfter(SeNode** phead,SeNode* pos, SeDataType x);
//删除pos位置
void SeqListEraseAfter(SeNode** phead, SeNode* pos);
//SeqList.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"SeqList.h"
//初始化
SeNode* BuySeqListNode(SeDataType x)
{SeNode* newnode = (SeNode*)malloc(sizeof(SeNode));//创建结点if (newnode == NULL)//判断结点是否生成成功{perror("BuySeqListNode");exit(-1);}newnode->data = x;//赋值newnode->next = NULL;return newnode;//返回结点地址
}
//销毁
void SeqListDestroy(SeNode** phead)
{assert(phead);//判断是否为空while (*phead)//循环free空间{SeNode* next = (*phead)->next;//记录下一个节点,避免链接丢失free(*phead);//释放*phead = next;}*phead = NULL;//置空
}
//打印
void SeqListPrint(SeNode* phead)
{SeNode* cur = phead;//记录位置while (cur){printf("%d->", cur->data);//打印cur = cur->next;//赋值遍历}printf("NULL\n");
}
//尾插
void SeqListPushBack(SeNode** phead, SeDataType x)
{assert(phead);//判断指针是否为空SeNode* node = BuySeqListNode(x);//开辟节点if (*phead == NULL)//如果*phead为空,代表链表为空,之间赋值替换即可{*phead = node;}else{SeNode* tail = *phead;while (tail->next != NULL)//如果不为空,则遍历链表,在最后一个结点进行链接{tail = tail->next;}tail->next = node;}
}
//尾删
void SeqListPopBack(SeNode** phead)
{assert(phead);//判断指针是否为空assert(*phead);//判断指针指向的内容是否为空if ((*phead)->next == NULL)//如果只有一个节点,直接释放{free(*phead);*phead = NULL;}else{SeNode* tail = *phead;while (tail->next->next != NULL)//如果多个,遍历到末尾的前一个释放{tail = tail->next;}free(tail->next);tail->next = NULL;                       }
}
//头插
void SeqListPushFront(SeNode** phead, SeDataType x)
{assert(phead);//判断指针是否为空SeNode* newnode = BuySeqListNode(x);//创建新节点if (*phead == NULL)//当只有一个节点的时候,直接赋值{*phead = newnode;}else//多个节点是先链接在赋值{newnode->next = *phead;*phead = newnode;}
}
//头删
void SeqListPopFront(SeNode** phead)
{assert(phead);//判断指针是否为空assert(*phead);//判断指针指向的内容是否为空if ((*phead)->next == NULL)//只有一个节点时,直接释放{free(*phead);*phead = NULL;}else//多个节点时,记录新头结点,释放原头结点{	SeNode* pphead = (*phead)->next;free(*phead);*phead = pphead;}
}
SeNode* SeqListFind(SeNode* phead,SeDataType x)
{assert(phead);//判断指针是否为空SeNode* cur = phead;//赋值遍历while (cur->next != NULL){if (cur->data == x)//判断相等{return cur;//相等返回地址}cur = cur->next;}return NULL;	//不等返回NULL;
}
//pos位置之后插入
void SeqListInsterAfter(SeNode**phead,SeNode* pos, SeDataType x)
{assert(pos);//判断指针是否为空assert(*phead);if (pos == *phead)//如果相等,就是头插{SeqListPushFront(phead,x);}else{SeNode* node = BuySeqListNode(x);SeNode* pphead = pos->next;//记录原结点链接的数据node->next = pphead;//链接pos->next = node;}
}
//删除pos位置
void SeqListEraseAfter(SeNode** phead, SeNode* pos)
{assert(pos);//判断空指针if (pos==*phead)//如果相等就是头删{SeqListPopFront(phead);}else{SeNode* cur = *phead;while (cur->next != pos)//记录pos的前一个位置{cur = cur->next;}cur->next = pos->next;//链接free(pos);//释放pos = NULL;}
}
//SeqListTest.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"SeqList.h"
void SeqListTest()
{SeNode* plist = NULL;SeqListPushFront(&plist, 5);SeqListPushFront(&plist, 4);SeqListPushFront(&plist, 3);SeqListPushFront(&plist, 2);SeqListPushFront(&plist, 1);SeqListPushFront(&plist, 0);SeqListPrint(plist);SeNode* ptr = SeqListFind(plist, 3);if (ptr != NULL){SeqListEraseAfter(&plist, ptr);}else{printf("数据有误\n");}SeqListPrint(plist);/*SeNode* ptr = SeqListFind(plist, 3);if (ptr != NULL){printf("%p %d", ptr,ptr->data);}else{printf("数据有误\n");}*///SeqListPopFront(&plist);//SeqListPopFront(&plist);//SeqListPopFront(&plist);//SeqListPopFront(&plist);//SeqListPopFront(&plist);//SeqListPrint(plist);//SeqListPopFront(&plist);//SeqListPrint(plist);/*SeqListPushBack(&plist, 1);SeqListPushBack(&plist, 2);SeqListPushBack(&plist, 3);SeqListPushBack(&plist, 4);SeqListPushBack(&plist, 5);SeqListPrint(plist);SeqListPopBack(&plist);SeqListPrint(plist);SeqListPopBack(&plist);SeqListPrint(plist);SeqListPopBack(&plist);SeqListPrint(plist);SeqListPopBack(&plist);SeqListPrint(plist);SeqListPopBack(&plist);SeqListPrint(plist);SeqListDestroy(&plist);*/}
int main()
{SeqListTest();return 0;
}

ont(&plist);

//SeqListPrint(plist);
/SeqListPushBack(&plist, 1);
SeqListPushBack(&plist, 2);
SeqListPushBack(&plist, 3);
SeqListPushBack(&plist, 4);
SeqListPushBack(&plist, 5);
SeqListPrint(plist);
SeqListPopBack(&plist);
SeqListPrint(plist);
SeqListPopBack(&plist);
SeqListPrint(plist);
SeqListPopBack(&plist);
SeqListPrint(plist);
SeqListPopBack(&plist);
SeqListPrint(plist);
SeqListPopBack(&plist);
SeqListPrint(plist);
SeqListDestroy(&plist);
/

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

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

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

相关文章

Vue 本地应用 图片切换 v-show v-bind实践

点击切换图片的本质&#xff0c;其实修改的是img标签的src属性。 图片的地址有很多个&#xff0c;在js当中通过数组来保存多个数据&#xff0c;数组的取值结合索引&#xff0c;根据索引可以来判断是否是第一张还是最后一张。 图片的变化本质是src属性被修改了&#xff0c;属性…

国标GB28181视频监控平台EasyGBS视频无法播放,抓包返回ICMP是什么原因?

国标GB28181视频平台EasyGBS是基于国标GB/T28181协议的行业内安防视频流媒体能力平台&#xff0c;可实现的视频功能包括&#xff1a;实时监控直播、录像、检索与回看、语音对讲、云存储、告警、平台级联等功能。国标GB28181视频监控平台部署简单、可拓展性强&#xff0c;支持将…

微服务——统一网关Getway

为什么需要网关&#xff1f; 网关的两种实现: 网关Getway——快速入门 步骤一 网关背身也是一个微服务&#xff0c;需要注册到nacos中去 步骤二 成功运行后 可以通过网关进行请求转发到对应服务。 流程如下&#xff1a; 路由断言工厂 网关路由可以配置的东西有如下。 spri…

【深度学习】yolov 图片训练的时候的遇到的warning: corrupt JPEG restored and saved

报错原因 是图片在dataset.py 走验证时报的错误。 if im.format.lower() in (jpg, jpeg):with open(im_file, rb) as f:f.seek(-2, 2)if f.read() ! b\xff\xd9: # corrupt JPEGImageOps.exif_transpose(Image.open(im_file)).save(im_file, JPEG, subsampling0, quality100)m…

Redis 九种数据类型的基本操作

一、redis9种数据类型的基本操作 ①key操作 #查找所有的key 127.0.0.1:6379> keys * 1) "pop" 2) "mylist" 3) "lpl" 4) "myset" #设置key的过期时间 返回1表示执行成功&#xff0c;0表示失败&#xff0c;出现问题 127.0.0.1:6379…

Qt Creator mainwindow.obj:-1: error: LNK2019

构建的时候报错&#xff1a; mainwindow.obj:-1: error: LNK2019: 无法解析的外部符号 "public: __thiscall mynotedig::mynotedig(class QWidget *)" (??0mynotedigQAEPAVQWidgetZ)&#xff0c;该符号在函数 "public: void __thiscall MainWindow::mynoteab…

VMPWN的入门级别题目详解(一)

实验一 VMPWN1 题目简介 这是一道基础的VM相关题目&#xff0c;VMPWN的入门级别题目。前面提到VMPWN一般都是接收字节码然后对字节码进行解析&#xff0c;但是这道题目不接受字节码&#xff0c;它接收字节码的更高一级语言&#xff1a;汇编。程序直接接收类似”mov”、”add”…

Dockerfile 创建镜像,构建LNMP+wordpress架构

目录 一、Dockerfile 构建镜像 1.Dockerfile 构建 nginx镜像 1.1创建 nginx Dockerfile 目录 1.2编写 Dockerfile 文件 1.3构建nginx镜像 2.Dockerfile 构建 mysql 镜像 2.1创建 mysql Dockerfile 目录 2.2修改mysql配置文件 2.3编写 Dockerfile 文件 2.4构建mysql镜…

Cesium态势标绘专题-圆角矩形(标绘+编辑)

标绘专题介绍:态势标绘专题介绍_总要学点什么的博客-CSDN博客 入口文件:Cesium态势标绘专题-入口_总要学点什么的博客-CSDN博客 辅助文件:Cesium态势标绘专题-辅助文件_总要学点什么的博客-CSDN博客 本专题没有废话,只有代码,代码中涉及到的引入文件方法,从上面三个链…

[RabbitMQ] RabbitMQ简单概述,用法和交换机模型

MQ概述&#xff1a; Message Queue(消息队列)&#xff0c;实在消息的传输过程中保存消息的容器&#xff0c;都用于分布式系统之间进行通信 分布式系统通信的两种方式&#xff1a;直接远程调用 和 借助第三昂 完成间接通信 发送方称谓生产者&#xff0c;接收方称为消费者 MQ优…

JMeter基础入门教程之CSV数据文件设置CSV Data Set Config

最近在做压力测试&#xff0c;登录功能用到了配置元件&#xff1a;CSV 数据文件设置&#xff0c;可以将登录用户名和密码放在一个csv文件中&#xff0c;然后通过CSV数据文件设置元件读取出来&#xff0c;用来做压测。 一、CSV文件 CSV文件小知识分享&#xff1a;是指"逗号…

BP神经网络数据分类——语音特征信号分类(Matlab代码实现)

目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果 &#x1f389;3 参考文献 &#x1f468;‍&#x1f4bb;4 Matlab代码 &#x1f4a5;1 概述 BP神经网络是一种常见的人工神经网络&#xff0c;用于数据分类和回归等任务。在语音特征信号分类中&#xff0c;BP神经网络可…

Android11 相机拍照权限,以及解决resolveActivity返回null

一、配置拍照和读写权限 <uses-permission android:name"android.permission.CAMERA"/> <uses-feature android:name"android.hardware.camera" /><uses-permission android:name"android.permission.WRITE_EXTERNAL_STORAGE"/&…

通过RPM方式安装,升级,卸载,以及配置使用MySQL

通过RPM方式安装&#xff0c;升级&#xff0c;卸载&#xff0c;以及配置使用MySQL 一、下载 MySQL是一种开源的关系数据库管理系统&#xff0c;被广泛应用于各种业务应用中。本文将讲解如何下载和安装MySQL的rpm安装包。下载rmp安装包有多种方式&#xff1a; 1、可以到MySQL的…

【C++初阶】 priority_queue(优先级队列)

⭐博客主页&#xff1a;️CS semi主页 ⭐欢迎关注&#xff1a;点赞收藏留言 ⭐系列专栏&#xff1a;C初阶 ⭐代码仓库&#xff1a;C初阶 家人们更新不易&#xff0c;你们的点赞和关注对我而言十分重要&#xff0c;友友们麻烦多多点赞&#xff0b;关注&#xff0c;你们的支持是我…

python简单入门

python简单入门 文章目录 python简单入门0. 地址链接1. 官网2.2. 下载地址3. 官方文档 1. 第一章1.1 python解释器1.2 基础语法1.2.1 常见数据类型1.2.2 强制类型转换1.2.3 注释1.2.4 运算符1.2.5 字符串1.2.5.1 字符串的定义1.2.5.2 字符串拼接1.2.5.3 格式化字符串1.2.5.3 精…

【防火墙】iptables防火墙(二)

1.写在命令行的备份和还原 2.把我们的规则配置在服务的文件当中&#xff0c;形成永久生效 iptables-save > /opt/ky30.bak iptables-restore < /opt/ky30.bak cat /etc/sysconfig/iptables 永久生效的配置文件 自定义链&#xff1a; 1.创建自定义链&#xff1a; i…

第二天 kali代理配置

文章目录 环境一、虚拟机网络模式&#xff08;1&#xff09;NAT&#xff08;2&#xff09;NAT模式&#xff08;3&#xff09;桥接模式&#xff08;4&#xff09;仅主机模式&#xff08;5&#xff09;总结 二、配置代理&#xff08;桥接模式&#xff09;1、基础设置2、虚拟机浏览…

记录安装stable diffusion webui时,出现的gfpgan安装卡住的问题

参考链接&#xff1a;(145条消息) 使用stable diffusion webui时&#xff0c;安装gfpgan失败的解决方案&#xff08;windows下的操作&#xff09;_新时代原始人的博客-CSDN博客

【el-tree查询并高亮】vue使用el-tree组件,搜索展开并选中对应节点,高亮搜索的关键字,过滤后高亮关键字,两种方法

第一种&#xff08;直接展开并高亮关键字&#xff09; 效果图这样的&#xff0c;会把所有的有这些关键字的节点都展开 代码&#xff1a; 这里的逻辑就是通过递归循环把所有和关键字匹配的节点筛选出来 然后通过setCheckedKeys方法把他展开选中 然后通过filterReal把关键字高亮…