当前位置: 首页 > news >正文

趣味数据结构之——数组

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

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

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

元素就是萝卜。

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

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

// 定义一个大小为 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

相关文章:

  • spring07-JdbcTemplate操作数据库
  • JSON简介及其应用
  • Geollama 辅助笔记:raw_to_prompt_strings_geo.py
  • 编程江湖-左右互博术(多线程,多进程)
  • [Linux]信号入门
  • 【企业管理】利益分配
  • 《高等数学》(同济大学·第7版)第十章 重积分第三节三重积分
  • 科大讯飞2025AI开发者大赛-用户新增赛道时间规则解析
  • ARFoundation系列讲解 - 100 VisionPro 环境搭建
  • Swift Moya自定义插件打印日志
  • 磁悬浮轴承气隙设计深度解析:微米间的生死时速
  • 蚂蚁百宝箱体验:如何快速创建“旅游小助手”AI智能体
  • Eplan2022导入edz文件并插入使用
  • Java 使用 Easy Excel 进行 Excel 数据导入导出
  • Linux基本指令篇 —— less指令
  • GeoTools 结合 OpenLayers 实现属性查询
  • 阶段二开始-第一章—8天Python从入门到精通【itheima】-118节(继承)
  • 《红黑树实现》
  • 基于esp32s3的自定义唤醒词识别-单元测试
  • 基于CNN卷积神经网络图像识别小程序9部合集