[数据结构]——单链表超详细总结

带你走进链表的世界

  • 目录:
  • 一、线性表的概念
  • 二、顺序表
  • 三、链表
    • 3.1 链表的概念
    • 3.2 链表的分类
    • 3.3 无头+单向+非循环链表的实现
    • 3.4 带头+双向+循环链表的实现
  • 四、顺序表和链表的区别和联系

目录:

链表是个优秀的结构,没有容量概念,可以在任意位置增加删除数据,这个博客,我准备花大量篇幅去总结链表(特别是单链表),同时也总结一下顺序表(顺序表和我们以前写的通讯录动态版类似,一般采用数组存储的方法,在数组上完成数据的增删查改)

一、线性表的概念

线性表的定义:由n个数据元素组成具有相同特性的有限序列。
常见的线性表:顺序表、链表、栈、队列、字符串等等。
线性表的概念:线性表在逻辑上是线性结构,也就是说它是一条直线,它的物理结构并不一定是连续的,线性表在物理上存储时,通常以数组和链表的形式存储。

二、顺序表

顺序表的定义:
顺序表是一段物理地址连续存储单元,是一种用来依次存储数据线性结构
在这里插入图片描述
1.静态顺序表:使用定常数组存储元素

#define N 7//方便改变数组大小
typedef int SLDatatype;
typedef struct SLD
{
SLDatatype arr[N];//定长数组
size_t size;//有效数据的个数
}SeqList;//顺序表

2.动态顺序表:使用动态开辟的数组存储元素(空间不够就可以扩容)

typedef int SLDatatype;
typedef struct SLD
{
SLDatatype* p;//指向动态开辟的数组
size_t size;//有效数据的个数
size_t capicity;//表示容量空间的大小
}SeqList;//顺序表

三、链表

3.1 链表的概念

链表是一种物理存储结构上非连续非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。
注意:
链式结构在逻辑上是连续的,但是在物理上不一定连续。
现实中的节点一般都是从堆上申请出来的
从堆上申请的空间是按照一定的策略来分配的,两次申请的空间可能连续,也可能不连续。

3.2 链表的分类

链表的结构非常多,结合起来有8种:
1、单向或者双向
在这里插入图片描述
2、带头或者不带头
在这里插入图片描述
3、循环或者不循环
在这里插入图片描述
实际中常用的两种结构是:

  1. 无头单向非循环链表 :
    结构简单,一般不会单独存数据。其他数据的子结构,如哈希桶、图的领接表等等。
  2. 带头双向循环链表:
    结构最复杂,一般可以单独存数据。实际中使用的链表数据结构,都是带头双向循环链表。

3.3 无头+单向+非循环链表的实现

头文件里的函数声明

// slist.h
typedef int SLTDateType;
typedef struct SListNode
{SLTDateType data;struct SListNode* next;
}SListNode;// 动态申请一个节点
SListNode* BuySListNode(SLTDateType x);
// 单链表打印
void SListPrint(SListNode* plist);
// 单链表尾插
void SListPushBack(SListNode** pplist, SLTDateType x);
// 单链表的头插
void SListPushFront(SListNode** pplist, SLTDateType x);
// 单链表的尾删
void SListPopBack(SListNode** pplist);
// 单链表头删
void SListPopFront(SListNode** pplist);
// 单链表查找
SListNode* SListFind(SListNode* plist, SLTDateType x);
// 单链表在pos位置之后插入x
void SListInsertAfter(SListNode* pos, SLTDateType x);
// 单链表删除pos位置之后的值
void SListEraseAfter(SListNode* pos);
// 单链表的销毁
void SListDestroy(SListNode** pplist);

下面将是各个函数的实现

// slist.c
#include "SList.h"
//动态申请一个结点
SListNode* BuySListNode(SLTDateType x)
{
//在堆上开辟一个存放指针的变量,并给它初始化SListNode* node = (SListNode*)malloc(sizeof(SListNode));node->data = x;node->next = NULL;return node;//返回指针
}
//单链表打印
void SListPrint(SListNode* plist)
{
//定义一个指针,指针指向头指针SListNode* cur = plist;//遍历指针,不是空就循环while (cur){printf("%d->", cur->data);cur = cur->next;}//cur指向空printf("NULL\n");
}
//单链表尾插一个数据
void SListPushBack(SListNode** pplist, SLTDateType x)
{
//开辟一个新结点SListNode* newnode = BuySListNode(x);//如果头指针指向的是空就让它指向这个新结点if (*pplist == NULL){*pplist = newnode;}else//如果头指针指向的不是空{//创建一个尾指针SListNode* tail = *pplist;//尾指针不在尾部,遍历单链表,让尾指针指向链表的最后结点while (tail->next != NULL){tail = tail->next;}
//把开辟的新结点尾插到尾指针结点的下一个结点tail->next = newnode;}
}
//单链表尾删
void SListPopBack(SListNode** pplist)
{
//定义两个指针SListNode* prev = NULL;//当前位置的指针SListNode* tail = *pplist;//尾结点的指针// 1.空、只有一个节点// 2.两个及以上的节点if (tail == NULL || tail->next == NULL){
//给空间释放free(tail);*pplist = NULL;}else{//遍历链表,找到尾指针while (tail->next){prev = tail;tail = tail->next;}free(tail);tail = NULL;
//让最后一个结点指向空prev->next = NULL;}
}//单链表头插法
void SListPushFront(SListNode** pplist, SLTDateType x)
{
//这个指针是空就报错assert(pplist);// 1.空// 2.非空SListNode* newnode = BuySListNode(x);if (*pplist == NULL)//pplist指针指向的是空{*pplist = newnode;}else{//在*pplist指针指向的那个结点前面头插一个结点newnode->next = *pplist;//*pplist指针重新指向头结点*pplist = newnode;}
}
//单链表头删
void SListPopFront(SListNode** pplist)
{// 1.空// 2.一个// 3.两个及以上SListNode* first = *pplist;//定义一个头指针,它为空就返回,if (first == NULL){return;}else if (first->next == NULL)//只有一个结点,就释放空间,然后置空{free(first);*pplist = NULL;}else{//两个以上结点SListNode* next = first->next;//把头指针的下一个结点位置存起来free(first);//释放头指针*pplist = next;//让首指针重新指向头指针}
}
 单链表查找
SListNode* SListFind(SListNode* plist, SLTDateType x)
{SListNode* cur = plist;while (cur){if (cur->data == x)return cur;cur = cur->next;}return NULL;
}
// 单链表在pos位置之后插入x
void SListInsertAfter(SListNode* pos, SLTDateType x)//用单链表查找,找到值x的位置,返回的指针给pos
{assert(pos);SListNode* next = pos->next;//next指针存放pos之后的节点SListNode* newnode = BuySListNode(x);//开辟一个新结点pos->next = newnode;//在pos后面插入新开辟的结点newnode->next = next;//让新结点连接上next指向的那个结点
}
// 单链表删除pos位置之后的值
void SListEraseAfter(SListNode* pos)//用单链表查找,找到值x的位置,返回的指针给pos
{assert(pos);// pos next nextnextSListNode* next = pos->next;if (next != NULL){SListNode* nextnext = next->next;free(next);pos->next = nextnext;}
}
// 单链表的销毁
void SListDestroy(SListNode** pplist)
{SListNode* cur = *pplist;while (cur){*pplist = cur->next;free(cur);cur = *pplist;}
}

3.4 带头+双向+循环链表的实现

头文件里的函数声明

#define _CRT_SECURE_NO_WARNINGS 1
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
// 带头+双向+循环链表增删查改实现
typedef int LTDataType;
typedef struct ListNode
{LTDataType data;struct ListNode* next;struct ListNode* prev;
}ListNode;
//创建新结点ListNode* ListNodes(LTDataType x);
//双向链表初始化ListNode* ListInit();
// 双向链表销毁
void ListDestory(ListNode* pHead);
// 双向链表打印
void ListPrint(ListNode* phead);
// 双向链表尾插
void ListPushBack(ListNode* phead, LTDataType x);
// 双向链表尾删
void ListPopBack(ListNode* phead);
// 双向链表头插
void ListPushFront(ListNode* phead, LTDataType x);
// 双向链表头删
void ListPopFront(ListNode* phead);
//求链表有多少数据
int listsize(ListNode* phead);
// 双向链表查找
ListNode* ListFind(ListNode* phead, LTDataType x);
// 双向链表在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x);
// 双向链表删除pos位置的节点
void ListErase(ListNode* pos);

函数的定义实现

// 创建新节点
ListNode* ListNodes(LTDataType x)
{ListNode* head = (ListNode*)malloc(sizeof(ListNode));if (head == NULL){perror("malloc fail");exit(-1);}head->data = x;head->next = NULL;head->prev = NULL;return head;
}
//链表初始化
ListNode* ListInit()
{ListNode* phead = ListNodes(-1);phead->next = phead;phead->prev = phead;return phead;
}
// 双向链表尾插
void ListPushBack(ListNode* phead, LTDataType x)
{assert(phead);ListNode* newnode = ListNodes(x);//创建一个新节点ListNode* tail = phead->prev;newnode->data = x;//双向链表尾插tail->next = newnode;newnode->next = phead;newnode->prev = tail;phead->prev = newnode;
}
// 双向链表尾删
void ListPopBack(ListNode* phead)
{assert(phead);assert(phead->next != NULL);ListNode* tail = phead->prev;ListNode* tailPrev = tail->prev;free(tail);tailPrev->next = phead;phead->prev = tailPrev;
}
// 双向链表头插
void ListPushFront(ListNode* phead, LTDataType x)
{ListNode* next = phead->next;ListNode* newnode = ListNodes(x);phead->next = newnode;newnode->prev = phead;newnode->next = next;next->prev = newnode;
}
// 双向链表头删
void ListPopFront(ListNode* phead)
{assert(phead);assert(phead->next != NULL);ListNode* node = phead->next;phead->next = node->next;node->next->prev = phead;free(node);
}
//求链表有多少数据
int listsize(ListNode* phead)
{int  size = 0;assert(phead);ListNode* cur = phead->next;while (cur != phead){size++;cur = cur->next;}return size;
}
// 双向链表查找
ListNode* ListFind(ListNode* phead, LTDataType x)
{ListNode* cur = phead->next;while (cur != phead){if (cur->data == x)return cur;cur = cur->next;}return NULL;
}
// 双向链表在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x)
{ListNode* newnode = ListNodes(x);ListNode* prevnode = pos->prev;prevnode->next = newnode;newnode->prev = prevnode;newnode->next = pos;pos->prev = newnode;
}
// 双向链表删除pos位置的节点
void ListErase(ListNode* pos)
{assert(pos);ListNode* nodeprev = pos->prev;ListNode* nodenext = pos->next;free(pos);nodeprev->next = nodenext;nodenext->prev = nodeprev;
}
// 双向链表打印
void ListPrint(ListNode* phead)
{assert(phead);ListNode* cur = phead->next;while (cur != phead){printf("%d<=>", cur->data);cur = cur->next;}
}
// 双向链表销毁
void ListDestory(ListNode* phead)
{//断言指针指针不为NULLassert(phead);ListNode* cur = phead;//定义一个指针//断开循环链表phead->prev->next = NULL;while (cur){ListNode* Next = cur->next;free(cur);cur = Next;}
}

四、顺序表和链表的区别和联系

在这里插入图片描述
补充: 高速缓存利用率
先要对存储器的层次结构有一定了解
如图:
在这里插入图片描述
数据是存在内存中的,CPU要访问数据,它不会去内存直接访问数据。看数据在不在缓存,在缓存,数据就命中(cpu访问数据时,数据恰好在缓存里),不在缓存,访问不命中(cpu访问数据,不会把4个字节加载到缓存,它会把一长段的数据加载到缓存)

注意:CPU访问数据第一次不命中,第二次一定命中

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

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

相关文章

基于PHP的芒果销售交易平台

有需要请加文章底部Q哦 可远程调试 基于PHP的芒果销售交易平台 一 介绍 芒果销售交易平台基于原生PHP开发&#xff0c;数据库mysql&#xff0c;前端bootstrap。用户可注册登录&#xff0c;购物下单&#xff0c;评论等。管理员登录后台可对芒果&#xff0c;用户&#xff0c;订…

解决win10因为WSL问题无法正常启动docker

解决win10无法成功启动dockerdesktop因为WSL问题无法启动 问题起因解决方法 问题起因 因为需要在windows复现一个CVE漏洞&#xff0c;就需要安装在WIN10上装docker&#xff0c;但是在启动的时候出现下面报错。 然后查了一下是因为WSL的版本太低了。更新以后发现打开docker仍然…

【PyTorch2 之027】在 PyTorch 中的R-CNN、Fast R-CNN和 Faster R-CNN

一、说明 亮点&#xff1a;对象检测是计算机视觉中最重要的任务之一。在这篇文章中&#xff0c;我们将概述最有影响力的对象检测算法家族之一&#xff1a;R-CNN、Fast R-CNN 和 Faster R-CNN。我们将重点介绍它们中的每一个的主要新颖性和改进。 最后&#xff0c;我们将专注于 …

【SOA-KELM分类】基于海鸥算法优化核极限学习机分类研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

【C/C++数据结构 - 2】:稳定性与优化揭秘,揭开插入排序、希尔排序和快速排序的神秘面纱!

文章目录 排序的稳定性插入排序插入排序的优化 希尔排序快速排序 排序的稳定性 稳定排序&#xff1a;排序前2个相等的数在序列中的前后位置顺序和排序后它们2个的前后位置顺序相同。&#xff08;比如&#xff1a;冒泡、插入、基数、归并&#xff09; 非稳定排序&#xff1a;排…

商品API接口优秀案例 │ 国家电网办公物资电商化采购项目API解决方案

苏宁易购集团股份有限公司&#xff08;以下简称“苏宁”&#xff09;作为中国领先的O2O智慧零售商&#xff0c;在互联网、物联网、大数据盛行的时代&#xff0c;持续推进智慧零售和线上线下融合战略&#xff0c;全品类经营&#xff0c;全渠道运营&#xff0c;开放苏宁物流云、数…

video_topic

使用qt5,ffmpeg6.0,opencv&#xff0c;os2来实现。qt并非必要&#xff0c;只是用惯了。 步骤是&#xff1a; 1.读取rtsp码流&#xff0c;转换成mat图像 2.发送ros::mat图像 项目结构如下&#xff1a; videoplayer.h #ifndef VIDEOPLAYER_H #define VIDEOPLAYER_H#include …

机器学习笔记 - 车道检测的几种深度学习方法

一、简述 人们在打造自动驾驶汽车时首先想到的就是实现车道检测。这是 Tesla 和 mobileye 所说的“强制性”任务,也是 Sebastian Thrun(自动驾驶汽车教父)在接受采访时所说的首要任务。 这个方向有很多传统的 OpenCV 算法,这些算法由不再使用的非常旧的函数组成。目前全部都…

LabVIEW玩转魔方

LabVIEW玩转魔方 使用LabVIEW创建一个3D魔方&#xff0c;并找出解谜题的秘密&#xff0c;给朋友留下深刻深刻的印象。游戏中内置的机制使每张脸都能独立转动&#xff0c;从而混合颜色。要解决难题&#xff0c;每个面必须是相同的纯色 魔方的奥秘在于它的简单性和不可解性。这是…

基于springboot实现音乐网站与分享平台项目【项目源码+论文说明】

摘要 本论文主要论述了如何使用JAVA语言开发一个音乐网站与分享平台 &#xff0c;本系统将严格按照软件开发流程进行各个阶段的工作&#xff0c;采用B/S架构&#xff0c;面向对象编程思想进行项目开发。在引言中&#xff0c;作者将论述音乐网站与分享平台的当前背景以及系统开…

Python 人工智能 Machine Learning 机器学习基础知识点详细教程(更新中)

Artificial Intelligence 人工智能基本介绍 人工智能&#xff08;Artificial Intelligence&#xff0c;AI&#xff09;是研究、开发用于模拟、延伸和扩展人的智能的理论、方法、技术及应用系统的一门新的技术科学。它试图了解智能的实质&#xff0c;并生产出一种新的能以人类智…

VMware使用ubuntu安装增强功能实现自动缩放

VMware使用ubuntu安装增强功能实现自动缩放 1.下载 VMware Tools2.安装tool 1.下载 VMware Tools 1.需要先弹出DVD 2.虚拟机-安装VMware Tools 进入终端 3.把media下的VMware压缩包拷贝到home/下 4.去home下解压 2.安装tool 进入vmware-tools-distrib sudo ./vmware-ins…

微信小程序备案内容常见问题汇总

一、备案时间点 自2023年09月01日起,新的微信小程序,必须备案后才能上架; 在2024年03月31日前,所有小程序都必须完成备案; 于2024年04月01日起,对未备案小程序进行清退处理。 微信小程序备案系统已于9月4日上线。 二、备案流程 [找备案入口]–[填主体信息]–[填小程…

QE01/QA11/QA02屏幕增强

1、业务需求 需要对来料检验增加“合格数量”和“不合格数量”字段&#xff0c;涉及三个增强开发 2、QE01\QE02\QE03\QE51N屏幕增强 增强表 增强点BADI&#xff1a;QEEM_SUBSCREEN_5000 创建程序&#xff0c;包含子屏幕&#xff0c;在增强点中调用 在程序屏幕中绘制字段 在输…

asp.net售后维修管理系统VS开发sqlserver数据库web结构c#编程Microsoft Visual Studio

一、源码特点 asp.net 售后维修管理系统 是一套完善的web设计管理系统&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为vs2010&#xff0c;数据库为sqlserver2008&#xff0c;使用c#语 言开发 asp.net售后维修管理系统1 二、…

python openai playground使用教程

文章目录 playground介绍Playground特点模型设置和参数选择四种语言模型介绍 playground应用构建自己的playground应用playground python使用 playground介绍 OpenAI Playground是一个基于Web的工具&#xff0c;旨在帮助开发人员测试和尝试OpenAI的语言模型&#xff0c;如GPT-…

【大数据】Apache Hive数仓(学习笔记)

一、数据仓库基础概念 1、数仓概述 数据仓库&#xff08;数仓、DW&#xff09;&#xff1a;一个用于存储、分析、报告的数据系统。 OLAP&#xff08;联机分析处理&#xff09;系统&#xff1a;面向分析、支持分析的系统。 数据仓库的目的&#xff1a;构建面向分析的集成化数据…

9、Docker 安装 Redis

1、下载镜像 docker pull redis:3.2.10 2、本机创建redis目录并修改配置文件 1&#xff09;创建目录 mkdir /usr/local/redis 2&#xff09;进入redis目录 cd /usr/local/redis 3&#xff09;创建data目录 mkdir data 4&#xff09;创建redis.conf文件 vi redis.conf 5&a…

网站的搭建与应用|企业APP软件定制开发|小程序

网站的搭建与应用|企业APP软件定制开发|小程序 网站是一种数字化媒体&#xff0c;它可以将我们的信息传递给全球的用户&#xff0c;让更多的人了解我们、了解我们的产品和服务。那么&#xff0c;如何搭建一个网站呢&#xff1f;下面&#xff0c;我将为大家介绍一下网站的建设步…

NIO基础-ByteBuffer,Channel

文章目录 1. 三大组件1.1 Channel1.2 Buffer1.2 Selector 2.ByteBuffer2.1 ByteBuffer 正确使用姿势2.2 ByteBuffer 结构2.3 ByteBuffer 常见方法分配空间向 buffer 写入数据从 buffer 读取数据mark 和 reset字符串与 ByteBuffer 互转分散度集中写byteBuffer黏包半包 3. 文件编…