趣味数据结构之——数组

你们一定都听说过它的故事……

是的没错,就是一个萝卜一个坑。ಥ◡ಥ

想象一下数组就是那个坑,那么定义数组就是在挖坑。

元素就是萝卜。

坑就在那里(地上),整整齐齐地排在那里。

于是数组最重要的一个特性就显现出来了——随机存取。还有一个特性就是它在内存里开辟的是一整块的连续空间,是多少就是多少。一眼望去也可以看出来它是多少。

// 定义一个大小为 10 的静态数组
int arr[10];// 用 memset 函数把数组的值初始化为 0
memset(arr, 0, sizeof(arr));// 使用索引赋值
arr[0] = 1;
arr[1] = 2;// 使用索引取值
int a = arr[0];

数据定义(安放)的事情我们解决了,下面就是操作了——增删查改

按理来说我们希望“增删查改后的数组仍然还是个数组”,就是按顺序去看,一个萝卜一个坑,可以短,但中间不能又空缺的

在尾部增删查改都好说,可在头部或者中间就不一样了。因为会造成空缺!!!

于是就涉及到了「数据搬移」。需要腾位置的腾位置,需要挤一下的挤一下。

插入→

// 大小为 10 的数组已经装了 4 个元素
int arr[10];
for (int i = 0; i < 4; i++) {arr[i] = i;
}// 在索引 2 置插入元素 666
// 需要把索引 2 以及之后的元素都往后移动一位
// 注意要倒着遍历数组中已有元素避免覆盖,不懂的话请看下方可视化面板
for (int i = 4; i > 2; i--) {arr[i] = arr[i - 1];
}// 现在第 3 个位置空出来了,可以插入新元素
arr[2] = 666;

扩容→

// 大小为 10 的数组已经装满了
int arr[10];
for (int i = 0; i < 10; i++) {arr[i] = i;
}// 现在想在数组末尾追加一个元素 10
// 需要先扩容数组
int newArr[20];
// 把原来的 10 个元素复制过去
for (int i = 0; i < 10; i++) {newArr[i] = arr[i];
}// 释放旧数组的内存空间
// ...// 在新的大数组中追加新元素
newArr[10] = 10;

删除→

// 大小为 10 的数组已经装了 5 个元素
int arr[10];
for (int i = 0; i < 5; i++) {arr[i] = i;
}// 删除 arr[1]
// 需要把 arr[1] 之后的元素都往前移动一位
// 注意要正着遍历数组中已有元素避免覆盖,不懂的话请看下方可视化面板
for (int i = 1; i < 4; i++) {arr[i] = arr[i + 1];
}// 最后一个元素置为 -1 代表已删除
arr[4] = -1;

数组的插入、扩容、删除的时间复杂度是O(N),因为涉及到数据搬移,给新元素腾地方

是的这就是静态数组,就是这么简单。

Then,动态数组它要来咯!!!

动态数组其实就是我们找了个兔子搬运工。◮_◮

动态数组底层还是静态数组,只是自动帮我们进行数组空间的扩缩容,并把增删查改操作进行了封装,让我们使用起来更方便而已。

即C++提供给我们的vector类。ಠoಠ

// 创建动态数组
// 不用显式指定数组大小,它会根据实际存储的元素数量自动扩缩容
vector<int> arr;for (int i = 0; i < 10; i++) {// 在末尾追加元素,时间复杂度 O(1)arr.push_back(i);
}// 在中间插入元素,时间复杂度 O(N)
// 在索引 2 的位置插入元素 666
arr.insert(arr.begin() + 2, 666);// 在头部插入元素,时间复杂度 O(N)
arr.insert(arr.begin(), -1);// 删除末尾元素,时间复杂度 O(1)
arr.pop_back();// 删除中间元素,时间复杂度 O(N)
// 删除索引 2 的元素
arr.erase(arr.begin() + 2);// 根据索引查询元素,时间复杂度 O(1)
int a = arr[0];// 根据索引修改元素,时间复杂度 O(1)
arr[0] = 100;// 根据元素值查找索引,时间复杂度 O(N)
int index = find(arr.begin(), arr.end(), 666) - arr.begin();

أ‿أ

أ‿أ

أ‿أ

(枯燥的)动态数组代码实现

#include <iostream>
#include <stdexcept>
#include <vector>template<typename E>
class MyArrayList {
private:// 真正存储数据的底层数组E* data;// 记录当前元素个数int size;// 最大元素容量int cap;// 默认初始容量static const int INIT_CAP = 1;public:MyArrayList() {this->data = new E[INIT_CAP];this->size = 0;this->cap = INIT_CAP;}MyArrayList(int initCapacity) {this->data = new E[initCapacity];this->size = 0;this->cap = initCapacity;}// 增void addLast(E e) {// 看 data 数组容量够不够if (size == cap) {resize(2 * cap);}// 在尾部插入元素data[size] = e;size++;}void add(int index, E e) {// 检查索引越界checkPositionIndex(index);// 看 data 数组容量够不够if (size == cap) {resize(2 * cap);}// 搬移数据 data[index..] -> data[index+1..]// 给新元素腾出位置for (int i = size - 1; i >= index; i--) {data[i + 1] = data[i];}// 插入新元素data[index] = e;size++;}void addFirst(E e) {add(0, e);}// 删E removeLast() {if (size == 0) {throw std::out_of_range("NoSuchElementException");}// 可以缩容,节约空间if (size == cap / 4) {resize(cap / 2);}E deletedVal = data[size - 1];// 删除最后一个元素// 必须给最后一个元素置为 null,否则会内存泄漏data[size - 1] = E();size--;return deletedVal;}E remove(int index) {// 检查索引越界checkElementIndex(index);// 可以缩容,节约空间if (size == cap / 4) {resize(cap / 2);}E deletedVal = data[index];// 搬移数据 data[index+1..] -> data[index..]for (int i = index + 1; i < size; i++) {data[i - 1] = data[i];}data[size - 1] = E();size--;return deletedVal;}E removeFirst() {return remove(0);}// 查E get(int index) {// 检查索引越界checkElementIndex(index);return data[index];}// 改E set(int index, E element) {// 检查索引越界checkElementIndex(index);// 修改数据E oldVal = data[index];data[index] = element;return oldVal;}// 工具方法int getSize() {return size;}bool isEmpty() {return size == 0;}// 将 data 的容量改为 newCapvoid resize(int newCap) {E* temp = new E[newCap];for (int i = 0; i < size; i++) {temp[i] = data[i];}// 释放原数组内存delete[] data;data = temp;cap = newCap;}bool isElementIndex(int index) {return index >= 0 && index < size;}bool isPositionIndex(int index) {return index >= 0 && index <= size;}// 检查 index 索引位置是否可以存在元素void checkElementIndex(int index) {if (!isElementIndex(index)) {throw std::out_of_range("Index out of bounds");}}// 检查 index 索引位置是否可以添加元素void checkPositionIndex(int index) {if (!isPositionIndex(index)) {throw std::out_of_range("Index out of bounds");}}void display() {std::cout << "size = " << size << " cap = " << cap << std::endl;for (int i = 0; i < size; i++) {std::cout << data[i] << " ";}std::cout << std::endl;}~MyArrayList() {delete[] data;}
};int main() {// 初始容量设置为 3MyArrayList<int> arr(3);// 添加 5 个元素for (int i = 1; i <= 5; i++) {arr.addLast(i);}arr.remove(3);arr.add(1, 9);arr.addFirst(100);int val = arr.removeLast();// 100 1 9 2 3for (int i = 0; i < arr.getSize(); i++) {std::cout << arr.get(i) << std::endl;}return 0;
}

有两个检查越界的方法,分别是 checkElementIndex 和 checkPositionIndex,你可以看到它俩的区别仅仅在于 index < size 和 index <= size

C++动态数组vector的底层是连续的内存空间,普通的数组,并不使用环形数组

扩展

环形数组

通过取模运算实现。

会方便在头部增删元素,避免了数据搬移

环形数组的关键在于,它维护了两个指针 start 和 endstart 指向第一个有效元素的索引,end 指向最后一个有效元素的下一个位置索引。

这样,当我们在数组头部添加或删除元素时,只需要移动 start 索引,而在数组尾部添加或删除元素时,只需要移动 end 索引。

当 start, end 移动超出数组边界(< 0 或 >= arr.length)时,我们可以通过求模运算 % 让它们转一圈到数组头部或尾部继续工作,这样就实现了环形数组的效果。

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

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

相关文章

PR-2025《Scaled Robust Linear Embedding with Adaptive Neighbors Preserving》

核心思想分析 这篇论文的核心思想在于解决线性嵌入&#xff08;linear embedding&#xff09;与非线性流形结构之间的不匹配问题。传统方法通过保留样本点间的亲和关系来提取数据的本质结构&#xff0c;但这种方法在某些情况下无法有效捕捉到数据的全局或局部特性。此外&#…

Redis-渐进式遍历

之前使用的keys查找key,一次获取到了所有的key,当key较多时,这个操作就有可能造成Redis服务器阻塞.特别是keys *操作. 于是可以通过渐进式遍历,每次获取部分key,通过多次遍历,既查询到了所有的key,又不会卡死服务器. 渐进式遍历不是通过一个命令获取到所有元素的,而是由一组命…

ISP Pipeline(3):Lens Shading Correction 镜头阴影校正

上一篇文章讲的是&#xff1a;ISP Pipeline&#xff08;2&#xff09;&#xff1a; Black Level Compensation:ISP Pipeline&#xff08;2&#xff09;&#xff1a;Black Level Compensation 黑电平补偿-CSDN博客 视频&#xff1a;(4) Lens Shading Correction | Image Signal…

什么是WebAssembly(WASM)

WebAssembly&#xff08;WASM&#xff09; 是一种高性能的低级编程语言字节码格式&#xff0c;可在网页和非网页环境中运行&#xff0c;支持多语言编译&#xff0c;运行速度接近原生代码。它在区块链中的作用是&#xff1a;作为智能合约的执行引擎&#xff0c;被多条非以太坊链…

【C++】inline的作用

一、inline的作用 1.1函数内联 作用​&#xff1a;建议编译器将函数调用替换为函数体代码&#xff0c;减少函数调用的开销&#xff08;压栈/跳转&#xff09;。​注意​&#xff1a;这只是对编译器的建议&#xff0c;编译器可能忽略&#xff08;如函数体过大或递归&#xff0…

代码随想录|图论|04广度优先搜索理论基础

广搜的使用场景 广搜的搜索方式就适合于解决两个点之间的最短路径问题。 因为广搜是从起点出发&#xff0c;以起始点为中心一圈一圈进行搜索&#xff0c;一旦遇到终点&#xff0c;记录之前走过的节点就是一条最短路。 当然&#xff0c;也有一些问题是广搜 和 深搜都可以解决…

Xposed框架深度解析:Android系统级Hook实战指南

引言:Android系统定制化的革命性突破 在移动安全研究和系统优化领域,传统的APP修改方案面临​​三重技术瓶颈​​: ​​逆向工程壁垒​​:APK重打包方案需处理签名校验、代码混淆等防护,平均耗时增加200%​​兼容性挑战​​:Android碎片化导致设备适配率不足65%​​功能…

大模型在通讯网络中的系统性应用架构

一、网络架构智能化重构​​ ​​1.1 空天地一体化组网优化​​ 智能拓扑动态调整​​&#xff1a;大模型通过分析卫星轨道数据、地面基站负载及用户分布&#xff0c;实时优化天地一体化网络拓扑。例如&#xff0c;在用户密集区域&#xff08;如城市中心&#xff09;自动增强低…

软件测试进阶:Python 高级特性与数据库优化(第二阶段 Day6)

在掌握 SQL 复杂查询和 Python 数据库基础操作后&#xff0c;第六天将深入探索Python 高级编程特性与数据库性能优化。通过掌握 Python 的模块与包管理、装饰器等高级语法&#xff0c;结合数据库索引优化、慢查询分析等技术&#xff0c;提升测试工具开发与数据处理效率。 一、…

【NLP】自然语言项目设计04

目录 04模型验证 代码架构核心设计说明 05运行推理 代码架构核心设计说明 项目展望 项目简介 训练一个模型&#xff0c;实现歌词仿写生成 任务类型&#xff1a;文本生成&#xff1b; 数据集是一份歌词语料&#xff0c;训练一个模型仿写歌词。 要求 1.清洗数据。歌词语料…

数据结构1 ——数据结构的基本概念+一点点算法

数据结构算法程序设计 什么是数据结构 数据&#xff08;data&#xff09;&#xff1a;符号集合&#xff0c;处理对象。 数据元素&#xff08;data element&#xff09;&#xff0c;由数据项&#xff08;data item&#xff09; 组成。 关键字&#xff08;key&#xff09;识别…

每日八股文7.1

每日八股-7.1 网络1.能说说 TCP 报文头部都包含哪些关键字段吗&#xff1f;2.TCP 是如何确保数据传输的可靠性的&#xff1f;你能详细谈谈吗&#xff1f;3.你能解释一下 TCP 滑动窗口是如何设计的&#xff1f;它主要解决了什么问题&#xff1f;4.TCP 协议的拥塞控制是如何实现的…

高性能 List 转 Map 解决方案(10,000 元素)

文章目录 前言一、问题背景&#xff1a;为什么List转Map如此重要&#xff1f;二、基础方法对比&#xff1a;Stream vs For循环三、性能优化关键点四、面试回答技巧 前言 遇到一个有意思的面试题&#xff0c;如标题所说&#xff0c;当10,000条数据的List需要转Map&#xff0c;如…

今日行情明日机会——20250701

上证指数缩量收阳线&#xff0c;形成日线上涨中继&#xff0c;个股上涨和下跌总体持平。 深证指数量能持续放大&#xff0c;即将回补缺口位&#xff0c;短线注意周三或周四的调整。 2025年7月1日涨停股主要行业方向分析 1. 芯片&#xff08;17家涨停&#xff0c;国产替代&…

P1312 [NOIP 2011 提高组] Mayan 游戏

题目描述 Mayan puzzle 是最近流行起来的一个游戏。游戏界面是一个 7 7 7 行 5 \times5 5 列的棋盘&#xff0c;上面堆放着一些方块&#xff0c;方块不能悬空堆放&#xff0c;即方块必须放在最下面一行&#xff0c;或者放在其他方块之上。游戏通关是指在规定的步数内消除所有…

Spring Boot 2 多模块项目中配置文件的加载顺序

Spring Boot 2 多模块项目中配置文件的加载顺序 在 Spring Boot 2 多模块项目中&#xff0c;配置文件的加载遵循特定的顺序规则。了解这些规则对于正确管理多模块应用的配置至关重要。 一、默认配置文件加载顺序 Spring Boot 会按照以下顺序加载 application.properties 或 …

边界的艺术:支持向量机与统计学习时代的王者

当扬勒丘恩的卷积神经网络LeNet在90年代初于手写数字识别领域绽放光芒&#xff0c;却因计算与数据的桎梏未能点燃更广泛的燎原之火时&#xff0c;人工智能&#xff0c;特别是其子领域机器学习&#xff0c;正步入一个理论深化与方法论多元化的关键时期。经历了符号主义通用智能探…

js filter()

listType(queryParams.value).then(response > {filterTable.value response.rows.slice(1); // 只显示前3条数据;filterTable.value filterTable.value.filter(item > {return wnSensorsList.value.some(sensorsgroup > {return sensorsgroup.sensorType item.cod…

Python 库 包 nltk (Natural Language Toolkit)

文章目录 &#x1f9f0; 一、nltk 的主要功能✅ 文本处理功能✅ 内置语料库&#xff08;Corpora&#xff09; &#x1f4e6; 二、安装与使用1. 安装 nltk2. 下载语料库&#xff08;第一次使用时需要下载&#xff09; &#x1f50d; 三、常用功能示例示例 1&#xff1a;分词示例…

设计模式之房产中介——代理模式

手撕设计模式之房产中介——代理模式 1.业务需求 ​ 大家好&#xff0c;我是菠菜啊&#xff0c;好久不见&#xff0c;今天给大家带来的是——代理模式。老规矩&#xff0c;在介绍这期内容前&#xff0c;我们先来看看这样的需求&#xff1a;我们有一套房产需要出售&#xff0c…