数据结构链表详解(不仅顺序表可以,我链表也可以)

目录

顺序表的缺点:

链表

链表的概念及其结构

链表的分类

链表的实现

链表形式:

节点的创建:

链表的增删:

尾插

头插

尾删

头删

查找 

打印

链表的重点

 1、尾删:则是需要找到尾节点,进行删除

2、头删:找到头节点的下一个节点,在删除头节点

在指定位置之前插入数据

在指定位置之后插入数据

删除pos之后的节点 

链表的摧毁

以上我们完成了链表的实现,可以尝试一下使用顺序表或链表实现通讯录!!!!


我们上节学习了顺序表的实现.

顺序表的缺点:

  • 任意位置插入或者删除元素的效率低:每次插入或者删除需要搬移元素;
  • 考虑空间的大小是否需要开辟空间

以下为顺序表与链表的不同点

链表

链表的概念及其结构

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

链表的分类

1. 单向或者双向

2. 带头或者不带头

3. 循环或者非循环

虽然链表的类型很多,但是我们实际中最常用还是两种结构:

1. 无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。
2. 带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,实现反而简单了,后面我们代码实现了就知道了。 

链表的实现

#pragma once
//定义节点的结构
//数据 + 指向下一个节点的指针
typedef int SLTDataType;typedef struct SListNode {SLTDataType data;struct SListNode* next;
}SLTNode;void SLTPrint(SLTNode* phead);//尾插
void SLTPushBack(SLTNode** pphead, SLTDataType x);
//头插
void SLTPushFront(SLTNode** pphead, SLTDataType x);
//尾删
void SLTPopBack(SLTNode** pphead);
//头删
void SLTPopFront(SLTNode** pphead);
//查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x);//在指定位置之前插入数据
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);
//在指定位置之后插入数据
void SLTInsertAfter(SLTNode* pos, SLTDataType x);//删除pos节点
void SLTErase(SLTNode** pphead, SLTNode* pos);
//删除pos之后的节点
void SLTEraseAfter(SLTNode* pos);

链表形式:

//定义节点的结构
//数据 + 指向下一个节点的指针
typedef int SLTDataType;typedef struct SListNode {SLTDataType data;struct SListNode* next;
}SLTNode;

节点的创建:

链表是一个个节点连接起来的,所以我们的第一步应该动态申请节点

//开辟新结点
SLTNode* SLTBuyNode(SLTDataType x)
{SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));if (newnode == NULL)//判断开辟是否成功{perror("malloc fail!");exit(1);}newnode->data = x;newnode->next = NULL;return newnode;
}

链表的增删:

尾插

我们先要创建一个存放x的新节点,然后找到节点的尾,在将为节点的next指向新节点

//尾插
void SLTPushBack(SLTNode** pphead, SLTDataType x) {assert(pphead);SLTNode* newnode = SLTBuyNode(x);//创建一个新节点if (*pphead==NULL) {*pphead = newnode;}else {//创建一个尾节点,找尾SLTNode* ptail = *pphead;while (ptail->next) {ptail = ptail->next;//找链表下一个为NULL的地址}//下一个为NULL,出循环ptail->next = newnode;}
}

头插

头插对于尾插简单不少,先创建新节点,将新节点的next指向头节点,然后将地址赋给头节点

//头插
void SLTPushFront(SLTNode** pphead, SLTDataType x) {assert(pphead);SLTNode* newnode = SLTBuyNode(x);//创建一个新节点if (*pphead == NULL) {*pphead = newnode;}else {newnode->next = *pphead;*pphead = newnode;//头节点为新的节点}
}

尾删

//尾删
void SLTPopBack(SLTNode** pphead) {assert(pphead && *pphead);if ((*pphead)->next == NULL) {//->的优先级高于();//只存在一个节点free(*pphead);*pphead = NULL;}else {//找尾节点SLTNode* pcur = *pphead;//保存尾节点的上一个节点SLTNode* ptail = *pphead;while (ptail->next) {pcur = ptail;ptail = ptail->next;}free(ptail);ptail = NULL;pcur->next = NULL;}
}

头删

//头删
void SLTPopFront(SLTNode** pphead) {assert(pphead && *pphead);SLTNode* pcur = (*pphead)->next;free(*pphead);*pphead = NULL;*pphead = pcur;
}

查找 

//查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x) {SLTNode* pcur = phead;while (pcur){if (pcur->data == x) {return pcur;}pcur = pcur->next;}//没有查找到return NULL;
}

打印

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

链表的重点

 这是一串链表,在链表中的增删查改,其实是需要关注其节点;

 1、尾删:则是需要找到尾节点,进行删除

如果我们将为释放掉,那尾节点的上一个节点,指向哪里,将会沦为野指针,是非常危险的,所以我们得创建一个指针,用来找到这个节点,这就是尾删的重点

2、头删:找到头节点的下一个节点,在删除头节点

与尾删类似,我们需要考虑如果将phead删除,我们如何找到新的头节点

综上所述:链表的难度常常发生在其释放时,指针的丢失或者成为野指针,如果注意其,我们将事半功倍,减少bug的产生;


//在指定位置之前插入数据
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);
//在指定位置之后插入数据
void SLTInsertAfter(SLTNode* pos, SLTDataType x);//删除pos节点
void SLTErase(SLTNode** pphead, SLTNode* pos);
//删除pos之后的节点
void SLTEraseAfter(SLTNode* pos);//销毁链表
void SListDesTroy(SLTNode** pphead);

在指定位置之前插入数据

//在指定位置之前插入数据
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x) {assert(pphead && *pphead);assert(pos);SLTNode* newnode = SLTBuyNode(x);//创建一个新节点if (pos == *pphead) {//头插SLTPushFront(*pphead, x);}else{SLTNode* prev = *pphead;//找到pos的上个节点while (prev->next != pos) {prev = prev->next;}newnode->next = pos;prev->next = newnode;}
}

在指定位置之后插入数据

void SLTInsertAfter(SLTNode* pos, SLTDataType x) {assert(pos);SLTNode* newnode = SLTBuyNode(x);SLTNode* pcur = pos->next;//保存pos下一个节点,以防节点丢失pos->next = newnode;newnode->next = pcur;
}

删除pos之后的节点 

//删除pos之后的节点
void SLTEraseAfter(SLTNode* pos) {assert(pos && pos->next);SLTNode* del = pos->next;pos->next = del->next;free(del);del = NULL;
}

链表的摧毁

//销毁链表
void SListDesTroy(SLTNode** pphead) {assert(pphead && *pphead);SLTNode* pcur = *pphead;while (pcur){SLTNode* next = pcur->next;free(pcur);pcur = next;}*pphead = NULL;
}

总体展示:

#define _CRT_SECURE_NO_WARNINGS
#include"slist.h"
#include<assert.h>void SLTPrint(SLTNode* phead) {SLTNode* pcur = phead;while (pcur) {//pcur!=NULLprintf("%d->", pcur->data);pcur = pcur->next;}printf("NULL\n");
}//开辟新结点
SLTNode* SLTBuyNode(SLTDataType x)
{SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));if (newnode == NULL){perror("malloc fail!");exit(1);}newnode->data = x;newnode->next = NULL;return newnode;
}//尾插
void SLTPushBack(SLTNode** pphead, SLTDataType x) {assert(pphead);SLTNode* newnode = SLTBuyNode(x);//创建一个新节点if (*pphead==NULL) {*pphead = newnode;}else {//创建一个尾节点,找尾SLTNode* ptail = *pphead;while (ptail->next) {ptail = ptail->next;//找链表下一个为NULL的地址}//下一个为NULL,出循环ptail->next = newnode;}
}//头插
void SLTPushFront(SLTNode** pphead, SLTDataType x) {assert(pphead);SLTNode* newnode = SLTBuyNode(x);//创建一个新节点if (*pphead == NULL) {*pphead = newnode;}else {newnode->next = *pphead;*pphead = newnode;//头节点为新的节点}
}//尾删
void SLTPopBack(SLTNode** pphead) {assert(*pphead && pphead);//只有一个节点,直接释放掉if ((*pphead)->next == NULL) //-> 优先级高于*{free(*pphead);*pphead = NULL;}else {//链表有多个节点SLTNode* prev = *pphead;SLTNode* ptail = *pphead;while (ptail->next) {prev = ptail;ptail = ptail->next;}free(ptail);ptail = NULL;prev->next = NULL;}
}//头删
void SLTPopFront(SLTNode** pphead) {//链表不能为空assert(pphead && *pphead);SLTNode* next = (*pphead)->next; //-> 优先级高于*free(*pphead);*pphead = NULL;*pphead = next;}
//查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{SLTNode* pcur = phead;while (pcur){if (pcur->data == x) {return pcur;}pcur = pcur->next;}//没有查找到return NULL;
}//在指定位置之前插入数据
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{assert(pphead&&*pphead);assert(pos);SLTNode* newnode = SLTBuyNode(x);//创建一个新节点//如果为头插if (pos == *pphead) {SLTPushFront(pphead,x);}else {SLTNode* prev = *pphead;while (prev->next != pos) {prev = prev->next;}//找到pos的上一个节点prev->next = newnode;newnode->next = pos;}
}//在指定位置之后插入数据
void SLTInsertAfter(SLTNode* pos, SLTDataType x)
{assert(pos);SLTNode* newnode = SLTBuyNode(x);SLTNode* pcur = pos->next;pos->next = newnode;newnode->next = pcur;}//删除pos节点
void SLTErase(SLTNode** pphead, SLTNode* pos)
{assert(pphead && *pphead);assert(pos);//pos是头结点/pos不是头结点if (pos == *pphead){//头删SLTPopFront(pphead);}else {SLTNode* prev = *pphead;while (prev->next != pos) {prev = prev->next;}prev->next = pos->next;//减少创建一个节点保存pos->next的节点free(pos);pos = NULL;}
}//删除pos之后的节点
void SLTEraseAfter(SLTNode* pos) {assert(pos && pos->next);SLTNode* del = pos->next;pos->next = del->next;free(del);del = NULL;
}//销毁链表
void SListDesTroy(SLTNode** pphead) {assert(pphead&&*pphead);SLTNode* pcur = *pphead;while (pcur){SLTNode* next = pcur->next;free(pcur);pcur = next;}*pphead = NULL;
}

以上我们完成了链表的实现,可以尝试一下使用顺序表或链表实现通讯录!!!!

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

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

相关文章

智能决策引擎架构设计

智能决策引擎概述 智能决策引擎系统是在大数据支撑下,根据行业专家经验制定规则策略、以及机器学习/深度学习/AI领域建立的模型运算,对当前的业务进行全面的评估,并给出决策结果的一套系统。 一套商业决策引擎系统动辄百万而且需要不断加钱定制,大多数企业最终仍会走上自研…

Springboot整合 Spring Cloud OpenFeign

1.OpenFeign简介 1.相比于Netflix Feign&#xff0c;OpenFeign支持Spring MVC注解&#xff0c;整合了Ribbon(springcloud在Hoxton.M2 RELEASED版本之后舍弃Ribbon。需手动引入spring-cloud-loadbalancer)和Nacos。 2.使得开发人员调用远程接口或者服务之间相互调用就像调用本地…

威纶通触摸屏下载项目文件后,文本都变成了框框的解决办法

威纶通触摸屏下载项目文件后,文本都变成了框框的解决办法 我们在用Easy builder pro编辑某些项目的情况下,编译没问题,为什么下载到触摸屏之后,文本都变成了框框了呢? 分析:: 不能正常显示文本的原因是字体文件缺失。 解决办法: 如下图所示,在Easy builder pro软件中,…

丑萌的黏土滤镜爆火,这款APP冲到了排行榜第一

你最近是否在社交软件上看到过黏土风格图片呢&#xff1f;突出的面部线条&#xff0c;宛如橡皮一样富有弹性的质感&#xff0c;不少人都用自己的照片或者一些出名表情包进行了恶搞。而掀起这股风潮的&#xff0c;就是一款Remini的AI修图软件。 聊起AI作图&#xff0c;相信不少人…

Qt自定义QpushButton分别在c++/python中实现

//.h文件#ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include<QPainter> #include<QMouseEvent> #include<QPropertyAnimation> #include<QResizeEvent>QT_BEGIN_NAMESPACE namespace Ui { class Widget; }class Widget : public QWi…

ue引擎游戏开发笔记(38)——实现敌人接收攻击伤害,并作出反应

1.需求分析&#xff1a; 现在已经显示造成实际伤害&#xff0c;但敌人对实际伤害并未产生反馈&#xff0c;例如还击&#xff0c;或者死亡倒地等等&#xff0c;实现敌人对于受击的反馈。 2.操作实现&#xff1a; 1.思路&#xff1a;在动画蓝图中添加死亡动画&#xff0c;并通过…

ESP8266-01模块继电器制作手机APP远程遥控智能开关

资料下载地址&#xff1a; ESP8266-01模块继电器制作手机APP远程遥控智能开关 这是一款使用ESP8266-01模块继电器制作手机APP远程遥控智能开关&#xff0c;它能实现远程遥控、定时、倒计时控制。电路简单&#xff0c;适合新手入门制作&#xff0c;下图是用这个智能开关制作的小…

OpenAI 推出 GPT-4o:实现多模态 AI 交互

一、前言 OpenAI 推出了其最新的 AI 模型——GPT-4o&#xff0c;此次发布的并非 GPT-4.5 或 GPT-5&#xff0c;而是一款全新的“全模态模型(Omnimodel)”。这是一个将文本、语音和视觉能力集成到单一无缝 AI 体验中的突破性发展。 GPT-4o 于 2024 年 5 月 14 日发布&#xff0…

Ubuntu 配置Samba

Ubuntu 配置&#xff1a; 安装 Samba &#xff1a; sudo apt-get install samba添加用户并设置密码&#xff08;可与ubuntu用户密码相同方便记忆&#xff09; sudo smbpasswd -a root这里我设置的密码为123456 sudo vi /etc/samba/smb.conf注意这个共享的目录一定要存在\ho…

WSL2-Ubuntu(深度学习环境搭建)

1.在Windows的WSL2上安装Ubuntu 流程可参考&#xff1a;https://www.bilibili.com/video/BV1mX4y177dJ 注意&#xff1a;中间可能需要使用命令wsl --update更新一下wsl。 2.WSL数据迁移 按照下面流程&#xff1a;开始菜单->设置->应用->安装的应用->搜索“ubun…

Folder Icons for Mac v1.9激活版:自定义文件夹图标

在追求个性和品味的今天&#xff0c;Folder Icons for Mac 让您的Mac桌面焕然一新。支持多种格式的图片和图标文件&#xff0c;满足您不同的审美需求。同时&#xff0c;软件提供丰富的图标库和模板&#xff0c;让您在定制文件夹图标时更加得心应手。Folder Icons for Mac 不仅能…

Spring AI项目Open AI对话接口开发指导

文章目录 创建Spring AI项目配置项目pom、application文件controller接口开发接口测试 创建Spring AI项目 打开IDEA创建一个新的spring boot项目&#xff0c;填写项目名称和位置&#xff0c;类型选择maven&#xff0c;组、工件、软件包名称可以自定义&#xff0c;JDK选择17即可…

AI预测体彩排3采取878定位大底=23策略+杀断组+杀组选+杀和尾+杀和值012缩水测试5月15日预测第1弹

昨天与一位玩排3的彩友通过视频直播的形式聊了下&#xff0c;受益匪浅&#xff0c;给我提供了一些比较有价值的建议&#xff0c;比如&#xff0c;对于878的定位策略&#xff0c;方向是没问题的&#xff0c;但是8783的话&#xff0c;还是缺乏一定的命中率&#xff0c;如果87823&…

photoshop(PS)有什么快速提升工作效率的功能?或者实用功能?这里告诉你5条!

1:文件太多&#xff0c;不方便马上找到需要插入元素&#xff08;元素放入PS会发现&#xff0c;位置不知道在哪里&#xff09;&#xff0c;点击需要选中或者搭配的元素&#xff0c;ctrlV就可以快速插入目标元素的位置了&#xff01; 点击当前元素&#xff0c;选中&#xff0c;c…

U盘中毒文件变乱码?揭秘原因与高效恢复方法!

在日常使用U盘的过程中&#xff0c;有时我们会遭遇到一个非常棘手的问题——文件突然出现乱码。当你满怀期待地插入U盘&#xff0c;准备打开某个重要文件时&#xff0c;却发现文件名或内容变成了一堆无法识别的字符&#xff0c;这种心情无异于晴天霹雳。乱码文件不仅影响了我们…

微分阻尼作用的理解

先说阻尼的作用,阻尼能够缩短系统整定时间,减小系统响应的振动幅度。 1、CODESYS位置式PID(完整ST源代码) CODESYS位置式PID(完整ST源代码)_codesys pid功能块-CSDN博客文章浏览阅读1.2k次,点赞2次,收藏2次。CODESYS增量式PID完整源代码请参看下面文章链接:CODESYS增量式…

[图解]SysML和EA建模住宅安全系统-04

1 00:00:01,200 --> 00:00:04,710 我们首先来看一下需求图的一些要点 2 00:00:05,810 --> 00:00:07,080 需求图用来干什么 3 00:00:07,210 --> 00:00:12,080 用来记录文本形式的一些需求 4 00:00:12,090 --> 00:00:13,480 和需求的素材 5 00:00:14,540 --> …

南网上行通信规约报文解析软件

本文分享一个南网上行通信规约20140617 报文解析软件 下载链接: https://pan.baidu.com/s/1ngbBG-yL8ucRWLDflqzEnQ 提取码: y1de 主界面如下图所示&#xff1a; 本软件同时支持南网上行通信规约20140617-Fn查询功能 软件同时支持多种规约类型&#xff0c;如&#xff1a;国网…

基于springboot实现酒店管理系统项目【项目源码+论文说明】

基于springboot实现酒店管理系统演示 摘要 时代的发展带来了巨大的生活改变&#xff0c;很多事务从传统手工管理转变为自动管理。自动管理是利用科技的发展开发的新型管理系统&#xff0c;这类管理系统可以帮助人完成基本的繁琐的反复工作。酒店是出门的必需品&#xff0c;无论…

Kotlin扩展函数和运算符重载

扩展函数 fun String.lettersCount():Int{var count 0for(i in this){if(i.isLetter())count}return count } fun main(){val str:String "12we"println(str.lettersCount()) } 相当于直接将方法写在类里面。函数体内可以直接使用this而不用传参。 运算符重载 …