数据结构顺序表和链表(超详细)

线性表:
顺序表
概念及结构
静态顺序表:使用定长数组存储元素。

动态顺序表:使用动态开辟的数组存储。

如果将一开始动态开辟的内存填满,则会进行扩容,
扩容分两种情况:
原地扩容:在已开辟的动态内存后进行判断,如果后方内存大小足够扩容到新定义的大小,则在之前开辟的内存后面加上一段,以达到新定义内存大小;
异地扩容:如果在已开辟的动态内存后进行判断,后方的大小不足以扩容到新定义的大小,则会在内存中寻找一块新的足够容纳新定义大小的空间,并将之前的数据拷贝到新空间,再释放之前定义的动态内存空间;

接口实现
typedef int SLDateType;
typedef struct SeqList
{
SLDateType* a;//动态开辟的数组
int size; //数据个数
int capacity;//容量空间大小
}SL;
//初始化
void SLInit(SL* ps);//释放或销毁
void SLDestroy(SL* ps);//显示或打印
void SLPrintf(SL* ps);//尾插
void SLPushBack(SL* ps, SLDateType x);
//尾删
void SLPopBack(SL* ps);
//头插
void SLPushFront(SL* ps, SLDateType x);
//头删
void SLPopFront(SL* ps);//扩容
void SLCheckDestroy(SL* ps);//顺序表查找
int SLFind(SL* ps,int n);// 顺序表在pos位置插入x
void SLInsert(SL* ps, int pos, SLDateType x);// 顺序表删除pos位置的值
void SLErase(SL* ps, int pos);//顺序表修改
void SLModify(SL* ps,int pos,SLDateType x);
#define _CRT_SECURE_NO_WARNINGS 1
#include "Sequence.h"
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
//初始化
void SLInit(SL* ps)
{
ps->a = (SLDateType*)malloc(sizeof(SLDateType*)*4);
if (ps->a == NULL)
{
perror("malloc failed");
exit(-1);
}
ps->size = 0;
ps->capacity = 4;
}
//释放或销毁
void SLDestroy(SL* ps)
{
free(ps->a);
ps->a = NULL;
ps->capacity = ps->size = 0;
}
//显示或打印
void SLPrintf(SL* ps)
{
int i = 0;
for (i = 0; i < ps->size; i++)
{
printf("%d ", ps->a[i]);
}
printf("\n");
}//扩容
void SLCheckDestroy(SL* ps)
{
if (ps->size == ps->capacity)
{
SLDateType* tmp = (SLDateType*)realloc(ps->a, sizeof(SLDateType) * 2 * ps->capacity);
if (tmp == NULL)
{
perror("realloc failed");
exit(-1);
}
ps->a = tmp;
ps->capacity *= 2;
}
}
//尾插void SLPushBack(SL* ps, SLDateType x)
{
SLCheckDestroy(ps);//ps->a[ps->size] = x;
//ps->size++;SLInsert(ps,ps->size,x);
}//尾删
void SLPopBack(SL* ps)
{
assert(ps);
//温柔型
//if (ps->size == 0)
//{
// return;
//}
//暴力型
//assert(ps->size > 0);//ps->size--;
SLErase(ps, ps->size - 1);
}
//头插void SLPushFront(SL* ps, SLDateType x)
{
assert(ps);
SLCheckDestroy(ps);
//int i = 0;
//for (i = 0; i < ps->size ; i++)
//{
// ps->a[ps->size-i] = ps->a[ps->size - i -1];
//}
//ps->a[0] = x;
//ps->size++;
SLInsert(ps, 0, x);
}//头删
void SLPopFront(SL* ps)
{
//防止越界
assert(ps->size > 0);//int i = 0;
//for (i = 0; i < ps->size - 1; i++)
//{
// ps->a[i] = ps->a[i+1];
//}
//ps->size--;SLErase(ps, 0);
}//查找
int SLFind(SL* ps, int n)
{
int i = 0;
for (i = 0; i < ps->size; i++)
{
if (ps->a[i] == n)
{
return i;
}
}
return -1;
}
// 顺序表在pos位置插入xvoid SLInsert(SL* ps, int pos, SLDateType x)
{
assert(ps);
//=0相当于头插,=size等于尾插
assert(pos >= 0 && pos <= ps->size);SLCheckDestroy(ps);
int start = ps->size - 1 ;
while (start >= pos)
{
ps->a[start + 1] = ps->a[start];
start--;
}
ps->a[pos] = x;
ps->size++;
}
// 顺序表删除pos位置的值void SLErase(SL* ps, int pos)
{
assert(ps);assert(pos >= 0 && pos < ps->size);
int begin = pos + 1;
while (begin < ps->size)
{
ps->a[begin - 1] = ps->a[begin];
begin++;
}
ps->size--;
}//顺序表修改
void SLModify(SL* ps, int pos, SLDateType x)
{
assert(ps);
assert(pos >= 0 && pos < ps->size);ps->a[pos];
}
值得注意的是:


如果没有使用温柔型或者暴力型判断,可能会发生数组越界,但是一般情况下,编译器不会报错,因为编译器只在数组两天的特定位置放置了越界标记,需要触发才会报错,触发一般是在编译结束时到数组越界标记访问,此时越界了,但没有在此处赋值,也不会报错。
顺序表总结:
缺点:
1.头部和中部插入和删除效率不高O(n);
2.空间不足时,扩容有一定的消耗(尤其是异地扩容)如:开辟空间,拷贝,释放旧空间;
3.扩容逻辑,可能存在空间浪费(200个,但是只用110个)。
优点:
1.尾插尾删足够快;
2.下标的随机访问和修改。
链表
链表的概念及结构:
特点:按需申请释放
逻辑上分析链表如图:

值得注意:
1.上图,链式结构在逻辑上是连续的,在物理上不一定是连续的;
2.现实中的结点一般是在堆上申请;
3.堆上申请的空间,可能连续,也可能不连续。
物理上分析链表如下:

1.单向链表的实现:
// 1 、无头 + 单向 + 非循环链表增删查改实现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// 分析思考为什么不在 pos位置之前插入?void SListInsertAfter ( SListNode * pos , SLTDateType x );// 单链表删除 pos 位置之后的值// 分析思考为什么不删除 pos 位置?void SListEraseAfter ( SListNode * pos );//销毁
void SListDestory ( SListNode** phead );
// 动态申请一个结点
SListNode* BuySListNode(SLDataType x)
{SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));if (newnode == NULL){perror("malloc fail");exit(-1);}newnode->data = x;newnode->next = NULL;return newnode;
}
// 单链表打印
void SListPrint(SListNode* phead)
{SListNode* cur = phead;while (cur != NULL){printf("%d->", cur->data);cur = cur->next;}printf("NULL\n");
}
特别需要注意的是



上面两张图中,图一说明改变成员,要用成员的指针,
图二说明改变成员的指针,要用成员的指针的指针(二级指针)

// 单链表尾插
void SListPushBack(SListNode** phead, SLDataType x)
{assert(phead);SListNode* newnode = BuySListNode(x);if (*phead == NULL){// 改变的结构体的指针,所以要用二级指针*phead = newnode;}else{SListNode* tail = *phead;while (tail->next != NULL){tail = tail->next;}// 改变的结构体,用结构体的指针即可tail->next = newnode;}}

// 单链表的头插
void SListPushFront(SListNode** phead, SLDataType x)
{assert(phead);SListNode* newnode = BuySListNode(x);newnode->next = *phead;*phead = newnode;
}
// 单链表的尾删
void SListPopBack(SListNode** phead)
{assert(phead);//0个assert(*phead);//1个或以上if ((*phead)->next == NULL){free(*phead);*phead = NULL;}else{SListNode* tail = *phead;while (tail->next->next){tail = tail->next;}free(tail->next);tail->next = NULL;}
}
// 单链表头删
void SListPopFront(SListNode** phead)
{assert(phead);assert(*phead);SListNode* newnode = (*phead)->next;free(*phead);*phead = newnode;
}
//查找
SListNode* SListFind(SListNode* phead, SLDataType x)
{SListNode* newnode = phead;while (newnode){if (newnode->data == x){return newnode;}newnode = newnode->next;}return NULL;
}
//在pos之后插入x
void SListInsertAfter(SListNode* pos, SLDataType x)
{assert(pos);SListNode* newnode = BuySListNode(x);newnode->next = pos->next;pos->next = newnode;}
//在pos之前插x
void SLTInsert(SListNode** phead, SListNode* pos, SLDataType x)
{assert(pos);//方法一if (pos == *phead){SListPushFront(phead, x);}else{SListNode* tail = *phead;while (tail->next != pos){tail = tail->next;}SListNode* newnode = BuySListNode(x);tail->next = newnode;newnode->next = pos;}//方法二//SListNode* tail = *phead;//while (newnode->next == NULL)//{ // if (*phead == pos)// {// newnode->next = tail;// *phead = newnode;// }// if (tail->next == pos)// {// newnode->next = tail->next;// tail->next = newnode;// }// else// {// tail = tail->next;// }//}
}
// 删除pos位置
void SLTErase(SListNode** phead, SListNode* pos)
{assert(phead);assert(pos);SListNode* tail = *phead;if (*phead == pos){SListPopFront(phead);//*phead = pos->next;//return;}while (tail->next != pos->next){if (tail->next == pos){tail->next = tail->next->next;}else{tail = tail->next;}}free(pos);//pos = NULL;//形参不改变实参,在调用外面置空
}
// 删除pos的后一个位置
void SLTEraseAfter(SListNode* pos)
{assert(pos);assert(pos->next);//如果pos没有下一个,报错SListNode* postNext = pos->next;pos->next = pos->next->next;free(postNext);postNext = NULL;
}
//销毁
void SListDestory(SListNode** phead)
{assert(phead);SListNode* cur = *phead;while (cur){SListNode* node = cur->next;free(cur);cur = node;}*phead = NULL;
}
替换发删除:
将下一个节点的值给pos,然后删除pos->next的节点。

2.双向链表的实现:
// 2 、带头 + 双向 + 循环链表增删查改实现typedef int LTDataType ;typedef struct ListNode{LTDataType _data ;struct ListNode * next ;struct ListNode * prev ;} ListNode ;// 创建返回链表的头结点 .ListNode * ListCreate ();// 双向链表销毁void ListDestory ( ListNode * plist );// 双向链表打印void ListPrint ( ListNode * plist );// 双向链表尾插void ListPushBack ( ListNode * plist , LTDataType x );// 双向链表尾删void ListPopBack ( ListNode * plist );// 双向链表头插void ListPushFront ( ListNode * plist , LTDataType x );// 双向链表头删void ListPopFront ( ListNode * plist );// 双向链表查找ListNode * ListFind ( ListNode * plist , LTDataType x );// 双向链表在 pos 的前面进行插入void ListInsert ( ListNode * pos , LTDataType x );// 双向链表删除 pos 位置的结点void ListErase ( ListNode * pos );
// 创建返回链表的头结点
ListNode* BuyLTNode(LTDataType x)
{ListNode* node = (ListNode*)malloc(sizeof(ListNode));if (node == NULL){perror("malloc fail");exit(-1);}node->data = x;node->next = NULL;node->prev = NULL;return node;
}// 创建返回链表的头结点.
ListNode* ListCreate()
{ListNode* head = BuyLTNode(0);head->next = head;head->prev = head;return head;
}
// 双向链表打印
// 双向链表打印
void ListPrint(ListNode* pHead)
{assert(pHead);ListNode* node = pHead->next;while (node != pHead){printf("%d->",node->data);node = node->next;}printf("NULL\n");
}
// 双向链表在pos的前面进行插入
// 双向链表在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x)
{assert(pos);ListNode* newnode = BuyLTNode(x);ListNode* oldnode = pos->prev;newnode->prev = oldnode;oldnode->next = newnode;newnode->next = pos;pos->prev = newnode;}
// 双向链表删除pos位置的节点
// 双向链表删除pos位置的节点
void ListErase(ListNode* pos)
{assert(pos);ListNode* oldnode = pos->prev;oldnode->next = pos->next;pos->next->prev = oldnode;free(pos);
}
// 双向链表尾插
// 双向链表尾插
void ListPushBack(ListNode* pHead, LTDataType x)
{assert(pHead);ListInsert(pHead,x);
}
// 双向链表头插
// 双向链表头插
void ListPushFront(ListNode* pHead, LTDataType x)
{assert(pHead);ListInsert(pHead->next,x);
}
// 双向链表尾删
// 双向链表尾删
void ListPopBack(ListNode* pHead)
{assert(pHead);//assert(pHead->next != pHead);ListErase(pHead->prev);
}
// 双向链表头删
// 双向链表头删
void ListPopFront(ListNode* pHead)
{assert(pHead);//assert(pHead->next != pHead);ListErase(pHead->next);
}
// 双向链表查找
// 双向链表查找
ListNode* ListFind(ListNode* pHead, LTDataType x)
{assert(pHead);ListNode* node = pHead->next;while(node != pHead){if (node->data == x){return node;}node = node->next;}return NULL;
}
// 双向链表销毁
// 双向链表销毁
void ListDestory(ListNode* pHead)
{assert(pHead);ListNode* oldnode = pHead->next;while (oldnode != pHead){ListNode* oldnode1 = oldnode->next;free(oldnode);oldnode = oldnode1;}free(pHead);printf("销毁完成!\n");
}
链表和顺序表的区别:

缓存利用率:


以上就是个人学习线性表的个人见解和学习的解析,欢迎各位大佬在评论区探讨!
感谢大佬们的一键三连! 感谢大佬们的一键三连! 感谢大佬们的一键三连!

相关文章:
数据结构顺序表和链表(超详细)
线性表: 线性表 ( linear list ) 是 n 个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使 用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串 ... 线性表在逻辑上是线性结构,也就…...
free 查看 buff/cache 很大,处理方法
如果 free 命令输出中的 buff/cache 很大,这意味着系统将一部分内存用于缓存文件系统的数据。这是正常的行为,因为缓存可以提高文件访问的速度。然而,如果需要释放缓存来腾出内存空间,可以尝试以下方法: 清理 PageCach…...
【Quarkus技术系列】「云原生架构体系」在云原生时代下的Java“拯救者”是Quarkus,那云原生是什么呢?
云原生时代下的Java"拯救者" 在云原生时代,其实Java程序是有很大的劣势的,以最流行的spring boot/spring cloud微服务框架为例,启动一个已经优化好,很多bean需要lazy load的application至少需要3-4秒时间,内…...
DHCP的工作原理
DHCP是一种网络管理协议,全称为动态主机配置协议(Dynamic Host Configuration Protocol)。它是一种基于TCP/IP协议的网络服务,允许网络管理员集中管理和分配IP地址和其他网络配置参数,以便客户端设备能够使用这些参数与…...
display:flex;兼容浏览器写法
通过在 display 属性中使用这些不同的值,可以确保在各种浏览器中都能正确显示 flex 布局。 需要注意的是,这只是一个示例,实际使用时可能还需要考虑其他兼容性问题,并根据具体情况进行调整。如果你需要更全面的兼容性解决方案&am…...
三、python Django ORM postgresql[数据定时备份、数据恢复]
一、数据定时备份 解释:备份指定数据库,能有效在发生错误时,预防错误,进行恢复 1.基本备份 #!/bin/bash sudo -u postgres pg_dump -U postgres -d dbname -Fc > /home/postgres/backup/backup.dump # sudo -u postgres&…...
c++字符串函数
在 C 中有大量用于操作 C-style 字符串的函数,它们集成在头文件 <cstring> 中。其常见的函 函数作用strcpy(s1,s2) 复制字符串 s2 到 s1strcat(s1,s2) 将字符串 s2 连接到 s1 末尾strlen(s) 计算字符串 s 长度strcmp(s1,s2) 比较字符串 s1 和 s2 …...
使用OkHttp发送POST请求的几种方式
使用OkHttp发送POST请求的几种方式 介绍pom依赖基本的POST请求带授权的POST请求POST方式发送JSON数据Multipart POST 请求 介绍 本文将介绍 OkHttp 客户端的基本用法。 主要介绍 OkHttp 3.x 版本中发送Post请求的几种方式。 pom依赖 <dependency><groupId>com.sq…...
时序预测 | MATLAB实现EEMD-GRU、GRU集合经验模态分解结合门控循环单元时间序列预测对比
时序预测 | MATLAB实现EEMD-GRU、GRU集合经验模态分解结合门控循环单元时间序列预测对比 目录 时序预测 | MATLAB实现EEMD-GRU、GRU集合经验模态分解结合门控循环单元时间序列预测对比效果一览基本介绍模型搭建程序设计参考资料 效果一览 基本介绍 1.MATLAB实现EEMD-GRU、GRU时…...
学习笔记整理-JS-04-流程控制语句
文章目录 一、条件语句1. if语句的基本使用2. if else if多条件分支3. if语句算法题4. switch语句5. 三元运算符 二、循环语句1. for循环语句2. for循环算法题3. while循环语句4. break和continue5. do while语句 三、初识算法1. 什么是算法2. 累加器和累乘器3. 穷举法4. 综合算…...
stable-diffusion-webui 界面汉化
本教程通过安装 sd-webui-bilingual-localization 插件来达到汉化目的, 项目地址为:https://github.com/journey-ad/sd-webui-bilingual-localization 一、安装插件 先进入插件安装界面 在搜索栏搜索 zh_CN Localization 中文语言包, 项目地址: https://github.com/dtlnor/st…...
问道管理:信创概念走势活跃,恒银科技斩获四连板
信创概念9日盘中走势活泼,截至发稿,新晨科技、竞业达、恒银科技等涨停,宇信科技涨近10%,中孚信息涨近9%,华是科技、神州数码涨超7%。 新晨科技今天“20cm”涨停,公司昨日晚间公告,近来收到投标代…...
centos 7镜像(iso)下载图文教程(超详细)
声明:本教程为本人学习笔记,仅供参考 文章目录 前言一、阿里云镜像站下载centos 7 二、清华源下载centos 7小结 前言 声明:本教程为本人学习笔记,仅供参考 本教程将提供两种方式下载centos 7 系统镜像 1、阿里巴巴开源镜像站 2、…...
使用Druid,以jdbc方式配置多数据源
文章目录 背景示例代码(结合实际进行配置)总结 背景 当使用Spring Boot项目并需要多数据源时,你可以使用Druid连接池来配置和管理多个数据源。以下是一个示例的配置和代码,以说明如何实现多数据源: 示例代码…...
RabbitMQ基础(2)——发布订阅/fanout模式 topic模式 rabbitmq回调确认 延迟队列(死信)设计
目录 引出点对点(simple)Work queues 一对多发布订阅/fanout模式以登陆验证码为例pom文件导包application.yml文件rabbitmq的配置生产者生成验证码,发送给交换机消费者消费验证码 topic模式配置类增加配置生产者发送信息进行发送控制台查看 rabbitmq回调确认配置类验…...
2. VisionOS平台概述
Unity 对VisionOS的支持将 Unity 编辑器和运行时引擎的全部功能与RealityKit提供的渲染功能结合起来。Unity 的核心功能(包括脚本、物理、动画混合、AI、场景管理等)无需修改即可支持。这允许游戏和应用程序逻辑像任何其他 Unity 支持的平台一样在Vision…...
MySql存储过程详解
文章目录 存储过程1 介绍 基本语法创建:调用查看删除演示: 变量相关系统变量演示: 用户自定义变量局部变量 if语法参数介绍casewhilerepeatloop游标条件处理程序存储函数 存储过程 1 介绍 存储过程是事先经过编译并存储在数据库中的一段 SQL 语句的集合,调用存储过…...
CRM 系统实施风险分析
企业实施 CRM 系统将引起各个方面的巨大变化, CRM 系统实施项目中,有不 少成功的案例,也存在相当的风险,企业只有增强风险意识并积极防范,才有可能提高 CRM 实施成功的概率。 1.企业内部环境带来的风险 (…...
保持城市天际线(力扣)贪心 JAVA
给你一座由 n x n 个街区组成的城市,每个街区都包含一座立方体建筑。给你一个下标从 0 开始的 n x n 整数矩阵 grid ,其中 grid[r][c] 表示坐落于 r 行 c 列的建筑物的 高度 。 城市的 天际线 是从远处观察城市时,所有建筑物形成的外部轮廓。…...
电路综合原理与实践---T衰减与PI衰减的详细计算理论与设计仿真
电路综合原理与实践—T衰减与PI衰减的详细计算理论与设计仿真 最近要找工作在刷笔试题目,会刷到关于T衰减的理论计算问题,一直搞不明白怎么算的,搞明白之后给大家伙来分享一下。 基础理论可以参考:电阻衰减网络计算(P…...
wordpress后台更新后 前端没变化的解决方法
使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…...
Linux应用开发之网络套接字编程(实例篇)
服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …...
vscode里如何用git
打开vs终端执行如下: 1 初始化 Git 仓库(如果尚未初始化) git init 2 添加文件到 Git 仓库 git add . 3 使用 git commit 命令来提交你的更改。确保在提交时加上一个有用的消息。 git commit -m "备注信息" 4 …...
【JavaEE】-- HTTP
1. HTTP是什么? HTTP(全称为"超文本传输协议")是一种应用非常广泛的应用层协议,HTTP是基于TCP协议的一种应用层协议。 应用层协议:是计算机网络协议栈中最高层的协议,它定义了运行在不同主机上…...
线程同步:确保多线程程序的安全与高效!
全文目录: 开篇语前序前言第一部分:线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分:synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分ÿ…...
基础测试工具使用经验
背景 vtune,perf, nsight system等基础测试工具,都是用过的,但是没有记录,都逐渐忘了。所以写这篇博客总结记录一下,只要以后发现新的用法,就记得来编辑补充一下 perf 比较基础的用法: 先改这…...
Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
dify打造数据可视化图表
一、概述 在日常工作和学习中,我们经常需要和数据打交道。无论是分析报告、项目展示,还是简单的数据洞察,一个清晰直观的图表,往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server,由蚂蚁集团 AntV 团队…...
C++使用 new 来创建动态数组
问题: 不能使用变量定义数组大小 原因: 这是因为数组在内存中是连续存储的,编译器需要在编译阶段就确定数组的大小,以便正确地分配内存空间。如果允许使用变量来定义数组的大小,那么编译器就无法在编译时确定数组的大…...
算法岗面试经验分享-大模型篇
文章目录 A 基础语言模型A.1 TransformerA.2 Bert B 大语言模型结构B.1 GPTB.2 LLamaB.3 ChatGLMB.4 Qwen C 大语言模型微调C.1 Fine-tuningC.2 Adapter-tuningC.3 Prefix-tuningC.4 P-tuningC.5 LoRA A 基础语言模型 A.1 Transformer (1)资源 论文&a…...
