【数据结构初阶】--- 顺序表

顺序表,好像学C语言时从来没听过,实际上就是给数组穿了层衣服,本质是一模一样的。
这里的顺序表实际是定义了一个结构体,设计各种函数来实现它的功能,比如说数组中的增删改查插入,这些基本操作其实平时就会在使用数组的时候用到。
那么,今天就带你深度剖析线性表(数组)的底层原理
这节知识只是个开胃菜,但也属于要必备的知识,所以各位同学不要松懈哦

引入

顺序表有静态的也有动态的

  • 静态顺序表,静,无非就是初始化这个静态表后,这个静态表的空间就不能变化了,例如:
 struct SeqList
* {
* 		int arr[20];
* 	    int size;
* };
  • 当初始化线性表后,这个数组存的数据就不能更改了
  • 那有人会想,我用个宏常量替换数组的大小,到时后想扩大或缩小,更改宏常量就行了
    #define N 20 int arr[N];
  • 这个想法不错,但也只能在线性表初始化之前更改,如果你初始化后,你正在使用线性表的功能时,内存不够用了怎么办

那么,接下来我所编写的代码就是动态顺序表,当你不够用内存的时候,会自动给你开辟,当看到这里,如果前面【C语言进阶】— 动态内存管理,你看过,那接下来的知识易如反掌,甚至不用看我对代码的注释就可以清楚,没看过的朋友,强烈推荐,很重要!!!数据结构会经常用到这篇的知识


1. 顺序表在内存中的存储方式

是一块连续的内存

在这里插入图片描述

2. 顺序表的定义

这里是用结构体定义了一个顺序表类型

typedef int SeqListType;//因为顺序表中存储的数据不见得都是int型,也可能是别的类型数据,所以,想存放别的数据时只需把int换成你想存的数据的类型//动态顺序表
typedef struct SeqList
{SeqListType* data;//指针指向的是SeqListType这个类型的数据int size;//记录当前有效储存数据的个数int capacity;//data开辟数组空间的容量
}SeqList;

初始化、销毁、打印功能

//初始化顺序表(开辟空间,赋初值)
void SLInit(SeqList* ps)
{//开辟一块大小为sizeof(SeqListType) * 4的内存,并把首地址传给指针ps->dataps->data = (SeqListType*)malloc(sizeof(SeqListType) * 4);if (ps->data == NULL){perror("malloc");return;}ps->size = 0;ps->capacity = 4;
}
//销毁空间,因为是在堆区开辟的数据,用完要用free函数释放
void SLDestory(SeqList* ps)
{free(ps->data);ps->data = NULL;ps->size = 0;ps->capacity = 0;
}void SLPrint(SeqList* ps)
{for (int i = 0; i < ps->size; i++){printf("%d ", ps->data[i]);}printf("\n");
}

3. 顺序表的功能实现

扩容功能,先跳过看头插法

//检查容量,不足顺便扩容
void CheckCapacity(SeqList* ps)
{if (ps->capacity == ps->size){//用realloc给ps->data开辟一个原来2倍的空间SeqListType* new_ps = (SeqListType*)realloc(ps->data,sizeof(SeqListType)*ps->capacity * 2);if (new_ps != NULL)//判断返是否开辟成功,若为NULL则开辟失败{ps->data = new_ps;new_ps = NULL;ps->capacity *= 2;}else{perror("realloc");return;}}
}

3.1 头插法、尾插法

//头插法
void SLPushFront(SeqList* ps, SeqListType x)
{//检查目前容量,不足就扩容CheckCapacity(ps);for (int i = ps->size - 1; i >= 0; i--){ps->data[i + 1] = ps->data[i];}ps->data[0] = x;ps->size++;
}//尾插法
void SLPushBack(SeqList* ps, SeqListType x)
{CheckCapacity(ps);ps->data[ps->size] = x;ps->size++;
}

3.2 头删法、尾删法

//头删法
void SLPopFront(SeqList* ps)
{//这里断言的原因是,如果ps是空指针,for循环条件判断就会访问空指针assert(ps != NULL);
//*****************当size==0时,也会执行ps->size--,后续可能会造成越界访问***************assert(ps->size > 0);for (int i = 1; i < ps->size; i++){ps->data[i - 1] = ps->data[i];}ps->size--;
}
//尾删法
void SLPopBack(SeqList* ps)
{assert(ps != NULL);assert(ps->size > 0);ps->size--;
}

3.3 给指定位置插入数据

void SLInsert(SeqList* ps, int position, SeqListType x)
{assert(ps != NULL);//判断给的位置是否越界,若越界,程序执行完会告诉你这里出的问题assert(position >= 1 && position <= ps->size);CheckCapacity(ps);for (int i = ps->size - 1; i >= position - 1; i--){ps->data[i + 1] = ps->data[i];}ps->data[position - 1] = x;ps->size++;
}

3.4 指定位置删除数据

void SLErase(SeqList* ps, int position)
{assert(ps != NULL);assert(position >= 1 && position <= ps->size);assert(ps->size > 0);for (int i = position; i < ps->size; i++){ps->data[i - 1] = ps->data[i];}ps->size--;
}

3.5 查找指定数据

int SLFind(SeqList* ps, SeqListType x)
{assert(ps != NULL);for (int i = 0; i < ps->size; i++){if (ps->data[i] == x)return i;}return -1;
}

4.顺序表的优缺点

4.1 优点

因为它跟数组一样可以进行下表访问,所以当你要查询数据时时间复杂度为O(1),是常数级,访问速度很快很方便,这与它的内存是一片连续的内存密切相关,因为当你访问数组下标为i的数据时,arr[i]本质是*(arr+i),首元素地址+i解引用

4.3 缺点

也正因它的内存是连续的,所以当你要删除、插入数据时必须要移动后面的数据,时间复杂度是O(N)级别的。

5 完整代码

5.1 头文件 SeqList.h 中的代码

#pragma once#include<stdio.h>
#include<stdlib.h>
#include<assert.h>typedef int SeqListType;//动态顺序表
typedef struct SeqList
{SeqListType* data;int size;int capacity;
}SeqList;//初始化顺序表
void SLInit(SeqList* ps);
//销毁顺序表
void SLDestory(SeqList* ps);
//打印顺序表
void SLPrint(SeqList* ps);
//头插法插入数据
void SLPushFront(SeqList* ps, SeqListType x);
//尾插法
void SLPushBack(SeqList* ps, SeqListType x);
//头删法
void SLPopFront(SeqList* ps);
//尾删法
void SLPopBack(SeqList* ps);
//指定位置插入数据
void SLInsert(SeqList* ps, int position, SeqListType x);
//指定位置删除
void SLErase(SeqList* ps, int position);
//查找数据
int SLFind(SeqList* ps, SeqListType x);

5.2 函数实现的文件 SeqList.c

#define	_CRT_SECURE_NO_WARNINGS#include"SeqList.h"void SLInit(SeqList* ps)
{ps->data = (SeqListType*)malloc(sizeof(SeqListType) * 4);if (ps->data == NULL){perror("malloc");return;}ps->size = 0;ps->capacity = 4;
}void SLDestory(SeqList* ps)
{free(ps->data);ps->data = NULL;ps->size = 0;ps->capacity = 0;
}void SLPrint(SeqList* ps)
{for (int i = 0; i < ps->size; i++){printf("%d ", ps->data[i]);}printf("\n");
}//检查容量,不足顺便扩容
void CheckCapacity(SeqList* ps)
{if (ps->capacity == ps->size){//用realloc给ps->data开辟一个原来2倍的空间SeqListType* new_ps = (SeqListType*)realloc(ps->data,sizeof(SeqListType)*ps->capacity * 2);if (new_ps != NULL){ps->data = new_ps;new_ps = NULL;ps->capacity *= 2;}else{perror("realloc");return;}}
}void SLPushFront(SeqList* ps, SeqListType x)
{//检查目前容量,不足就扩容CheckCapacity(ps);for (int i = ps->size - 1; i >= 0; i--){ps->data[i + 1] = ps->data[i];}ps->data[0] = x;ps->size++;}
//尾插法
void SLPushBack(SeqList* ps, SeqListType x)
{CheckCapacity(ps);ps->data[ps->size] = x;ps->size++;
}void SLPopFront(SeqList* ps)
{//这里断言的原因是,如果ps是空指针,for循环条件判断就会访问空指针assert(ps != NULL);
//*****************当size==0时,也会执行ps->size--,后续可能会造成越界访问***************assert(ps->size > 0);for (int i = 1; i < ps->size; i++){ps->data[i - 1] = ps->data[i];}ps->size--;
}
//尾删法
void SLPopBack(SeqList* ps)
{assert(ps != NULL);assert(ps->size > 0);ps->size--;
}void SLInsert(SeqList* ps, int position, SeqListType x)
{assert(ps != NULL);assert(position >= 1 && position <= ps->size);CheckCapacity(ps);for (int i = ps->size - 1; i >= position - 1; i--){ps->data[i + 1] = ps->data[i];}ps->data[position - 1] = x;ps->size++;
}void SLErase(SeqList* ps, int position)
{assert(ps != NULL);assert(position >= 1 && position <= ps->size);assert(ps->size > 0);for (int i = position; i < ps->size; i++){ps->data[i - 1] = ps->data[i];}ps->size--;
}int SLFind(SeqList* ps, SeqListType x)
{assert(ps != NULL);for (int i = 0; i < ps->size; i++){if (ps->data[i] == x)return i;}return -1;
}

这两套代码不能直接运行,主函数int main(){}中的内容自己写,就剩调用函数了,让自己动动手敲几行代码吧,好的程序员一定是需要实践的,尽管顺序表这一节相对不难,但里面有些边界值的判断,只有你亲手敲代码的时候才能真正进入思考,加油各位!

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

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

相关文章

go语言内置预编译 //go:embed xxx 使用详解

在go语言里面&#xff0c;我们可以使用一个“类注释”的语法来来让编译器帮助我们在编译的时候将一些文件或者目录读取到指定的变量中来供我们使用。 go:embed语法&#xff1a; //go:embed 文件或者目录路径 var 变量名 变量类型 说明&#xff1a; 文件或者目录路径 可以…

【操作系统】进程与线程的区别及总结(非常非常重要,面试必考题,其它文章可以不看,但这篇文章最后的总结你必须要看,满满的全是干货......)

目录 一、 进程1.1 PID(进程标识符)1.2 内存指针1.3 文件描述符表1.4 状态1.5 优先级1.6 记账信息1.7 上下文 二、线程三、总结&#xff1a;进程和线程之间的区别&#xff08;非常非常非常重要&#xff0c;面试必考题&#xff09; 一、 进程 简单来介绍一下什么是进程&#xf…

C++学习/复习13--list概述

一、list概念 1.带头双向链表 2.构造函数 3.迭代器&#xff08;其迭代器需尤其注意&#xff09; 4、size 5.front/back 6.插入删除 删除时的迭代器失效 由于list的节点特殊&#xff0c;既有数据又有指针&#xff0c;其实现需要节点/迭代器/list各成一类再组合

Windows系统电脑本地部署AI音乐创作工具并实现无公网IP远程使用

文章目录 前言1. 本地部署2. 使用方法介绍3. 内网穿透工具下载安装4. 配置公网地址5. 配置固定公网地址 前言 本文主要介绍如何在Windows系统电脑上快速本地部署一个文字生成音乐的AI创作工具MusicGPT&#xff0c;并结合cpolar内网穿透工具实现随时随地远程访问使用。 MusicG…

Spring AI 第二讲 之 Chat Model API 第八节Anthropic 3 Chat

Anthropic Claude 是一系列基础人工智能模型&#xff0c;可用于各种应用。对于开发人员和企业来说&#xff0c;您可以利用 API 访问&#xff0c;直接在 Anthropic 的人工智能基础架构之上进行构建。 Spring AI 支持用于同步和流式文本生成的 Anthropic 消息 API。 Anthropic …

29网课交单平台 epay.php SQL注入漏洞复现

0x01 产品简介 29网课交单平台是一个专注于在线教育和知识付费领域的交单平台。该平台基于PHP开发,通过全开源修复和优化,为用户提供了高效、稳定、安全的在线学习和交易环境。作为知识付费系统的重要组成部分,充分利用了互联网的优势,为用户提供了便捷的支付方式、高效的…

2.Rust自动生成文件解析

目录 一、生成目录解析二、生成文件解析2.1 Cargo.toml2.2 main函数解析 一、生成目录解析 先使用cargo clean命令删除所有生成的文件&#xff0c;下图显示了目录结构和 main.rs文件 使用cargo new testrust时自动创建出名为testrust的Rust项目。内部主要包含一个src的源码文…

[网鼎杯 2020 青龙组]jocker

运行程序,发现是要我们自己输入 那么肯定是拿到enc慢慢还原 32位,无壳 进来就红一下报错 这里可以看见长度为24 动调一下看看 这里进行了大量的异或 这里是对地址开始的硬编码进行异或,从而达到smc的效果 所以你也可以发现在进行这一步操作之前 encry函数全是报错 你点开…

Python 知识图谱补全,Knowledge Graph Completion,基于大模型的知识图谱补全,基于LLMs的KGC任务

今天讲一篇文章《Exploring Large Language Models for Knowledge Graph Completion》 &#xff0c;这篇文章主题&#xff1a;基于大模型做知识图谱补全 1.文章主要思想&#xff1a; 本章描述知识图谱补全中的三个任务&#xff1a;三元组分类、关系预测和实体(链接)预测&…

[office] 如何才能用EXCEL打开dat文件- #微信#学习方法

如何才能用EXCEL打开dat文件? 方法&#xff1a; 1、打开EXCEL软件&#xff1b; 2、文件&#xff0c;打开&#xff0c;选择要转化的DAT文件&#xff1b; 3、在弹出的向导文件&#xff08;步骤1&#xff09;中&#xff0c;选择合适的文件类型&#xff08;按预览选择&#xf…

秒杀优化+秒杀安全

1.Redis预减库存 1.OrderServiceImpl.java 问题分析 2.具体实现 SeckillController.java 1.实现InitializingBean接口的afterPropertiesSet方法&#xff0c;在bean初始化之后将库存信息加载到Redis /*** 系统初始化&#xff0c;将秒杀商品库存加载到redis中** throws Excepti…

QT treeWidget如何添加虚线

1、添加以下代码即可&#xff1a; ui.treeWidget->setStyle(QStyleFactory::create("windows"));2、效果如下&#xff1a;

CorelDRAW2024发布更新啦!设计师们的得力助手

在数字化的今天&#xff0c;视觉设计已经成为我们生活中不可或缺的一部分。从手机界面到广告海报&#xff0c;从网页布局到包装设计&#xff0c;每一个细节都离不开设计师们的专业与创意。然而&#xff0c;面对日益增长的设计需求和不断提升的审美标准&#xff0c;许多设计师开…

华为鸿蒙开发-鸿蒙基于ARKTS开发之启动模式

前言 鸿蒙生态取得爆发式增长&#xff01; 截至3月底&#xff0c;已有超4000个应用加入鸿蒙生态。 而在今年1月中旬&#xff0c;华为刚宣布HarmonyOS NEXT鸿蒙星河版面向开发者开放申请&#xff0c;这一版本鸿蒙系统也被称为“纯血鸿蒙”。 当时&#xff0c;华为宣布首批200…

网络编程TCP

White graces&#xff1a;个人主页 &#x1f649;专栏推荐:Java入门知识&#x1f649; &#x1f649; 内容推荐:Java网络编程(下)&#x1f649; &#x1f439;今日诗词: 壮士当唱大风哥, 宵小之徒能几何&#xff1f;&#x1f439; ⛳️点赞 ☀️收藏⭐️关注&#x1f4ac;卑微…

音视频开发17 FFmpeg 音频解码- 将 aac 解码成 pcm

这一节&#xff0c;接 音视频开发12 FFmpeg 解复用详情分析&#xff0c;前面我们已经对一个 MP4文件&#xff0c;或者 FLV文件&#xff0c;或者TS文件进行了 解复用&#xff0c;解出来的 视频是H264,音频是AAC&#xff0c;那么接下来就要对H264和AAC进行处理&#xff0c;这一节…

ChatGPT交卷2024年高考新课标I卷语文关于AI方面的作文试题

2024年新课标I卷作文试题&#xff1a; 阅读下面的材料&#xff0c;根据要求写作。&#xff08;60分&#xff09; 随着互联网的普及、人工智能的应用&#xff0c;越来越多的问题能很快得到答案。那么&#xff0c;我们的问题是否会越来越少&#xff1f; 以上材料引发了你怎样的…

Nginx03-动态资源和LNMP介绍与实验、自动索引模块、基础认证模块、状态模块

目录 写在前面Nginx03案例1 模拟视频下载网站自动索引autoindex基础认证auth_basic模块状态stub_status模块模块小结 案例2 动态网站&#xff08;部署php代码&#xff09;概述常见的动态网站的架构LNMP架构流程数据库Mariadb安装安全配置基本操作 PHP安装php修改配置文件 Nginx…

os和os.path模块

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 目录也称文件夹&#xff0c;用于分层保存文件。通过目录可以分门别类地存放文件。我们也可以通过目录快速找到想要的文件。在Python中&#xff0c;并…

写入文件内容

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 在实例01中&#xff0c;虽然创建并打开一个文件&#xff0c;但是该文件中并没有任何内容&#xff0c;它的大小是0KB。Python的文件对象提供了write()…