关于C++的(动态,静态)二维数组的补充

今日诗词:

羽衣常带烟霞色,不惹人间桃李花。

------《卧云》 宋 白玉蟾


目录

补漏:

正文:

1.二维数组的元素添加:

2.二维数组的元素删除

3.二维数组的元素访问和修改

4.二维数组查找元素下标

下期预告:C++的动态数组vector


补漏:

1.我想问一下大家有没有注意我上一篇提到的二维数组的元素,那么为什么是它呢?C++之二维数组及多维数组

二维数组的元素之所以是一维数组,是因为二维数组本身是由多个一维数组(也称为行)组成的集合。这种结构允许我们以行和列的形式组织数据,从而方便地表示和处理矩阵、表格或其他具有二维特性的数据结构。

++具体来说,二维数组中的每个元素都通过两个索引来访问:第一个索引指定行号,第二个索引指定列号。在这个上下文中,每个"元素"实际上都是一个一维数组,它包含了该行中所有的列元素。换句话说,二维数组可以被视为一个"数组的数组",其中外层的数组(即二维数组本身)包含了多个内层的数组(即一维数组,每个一维数组代表一行)。++

这种结构提供了很大的灵活性,因为每个一维数组(即二维数组中的一行)都可以有不同的长度(尽管在大多数编程语言中,二维数组的所有行通常都被要求具有相同的长度,以保持数组的矩形形状)。然而,在某些高级语言或特殊情况下,可能允许创建不规则形状的二维数组,即其中各行的长度可以不同。

二维数组的这种结构使得它们非常适合于执行矩阵运算、图像处理、数据分析等任务,其中经常需要按行和列来访问和操作数据。通过使用二维数组,我们可以轻松地实现这些操作,而无需编写复杂的代码来手动管理数据的存储和访问。

2.大家有没有想过二维数组为什么是一行一行的写,而不是一列一列的写?

这就牵扯到空间的局部性:

在一个小的时间窗口内,访问的变量地址越接近越好,这样执行的速度越快。

也就是说一般来说,需要将最长的循环放在最内层,最短的循环放在最外层,以减少CPU跨切循环的层数。


现在我们接着上次的那篇来看看二维数组的基本用法(增,删,改,查)。

正文:

1.二维数组的元素添加:

在C++中,二维数组通常是通过数组的数组(array of arrays)或者指针的指针(pointer to pointer)来实现的,但更常见的是使用std::vector<std::vector<T>>,因为std::vector提供了更灵活和动态的内存管理。

使用原生二维数组(固定大小)

原生二维数组的大小在编译时就必须确定,因此你不能直接"添加"元素到这样的数组中,因为它们的大小是固定的。但你可以通过创建一个更大的数组,然后将旧数组的内容复制过去,并在新位置添加新元素来间接实现。

使用std::vector<std::vector<T>>

这是更常见和推荐的方法,因为它提供了动态数组的所有好处。

cpp 复制代码
//向末尾添加一行
#include <iostream>
#include <vector>

int main() {
    std::vector<std::vector<int>> arr = {{1, 2}, {3, 4}};

    // 向末尾添加一行
    arr.push_back({5, 6});

    // 打印结果
    for (const auto& row : arr) 
{
        for (int elem : row) 
{
            std::cout << elem << " ";
        }
        std::cout << std::endl;
    }

    return 0;
}
cpp 复制代码
//向特定行添加元素
#include <iostream>
#include <vector>

int main() {
    std::vector<std::vector<int>> arr = {{1, 2}, {3, 4}};

    // 向第二行末尾添加元素7
    if (arr.size() > 1) 
{ // 确保第二行存在
        arr[1].push_back(7);
    }

    // 打印结果
    for (const auto& row : arr) 
{
        for (int elem : row) {
            std::cout << elem << " ";
        }
        std::cout << std::endl;
    }

    return 0;
}

使用std::vector<std::vector<T>>的好处是你可以很容易地动态地添加或删除行和列,而不需要担心内存管理的问题。这就是二维数组添加元素的两种情况。


2.二维数组的元素删除

在C++中,二维数组(静态分配的)实际上是一个连续的内存块,其大小在编译时就已确定,因此不能像动态数据结构(如std::vector)那样直接删除或插入元素。二维数组不支持动态地改变其大小或删除其中的元素。

  1. 使用std::vector<std::vector<T>>

这是处理动态二维数组(即矩阵)的推荐方式。std::vector是一个模板类,能够存储可变数量的同类型元素,并且支持动态地添加、删除元素。

cpp 复制代码
#include <vector>
#include <iostream>

int main() {
    std::vector<std::vector<int>> matrix = {
        {1, 2, 3},
        {4, 5, 6},
        {7, 8, 9}
    };

    // 删除第二行
    matrix.erase(matrix.begin() + 1);

    // 打印结果
    for (const auto& row : matrix) {
        for (int elem : row) {
            std::cout << elem << " ";
        }
        std::cout << std::endl;
    }

    return 0;
}
  1. 标记删除(对于静态二维数组)

如果你必须使用静态二维数组,并且需要"删除"元素,你可以通过标记这些元素为"已删除"或"无效"来实现。这通常意味着你需要一个额外的数组或数据结构来跟踪哪些元素是有效的。

cpp 复制代码
#include <iostream>

const int ROWS = 3;
const int COLS = 3;
int matrix[ROWS][COLS] = {
    {1, 2, 3},
    {4, 5, 6},
    {7, 8, 9}
};
bool isValid[ROWS][COLS] = {true}; // 标记数组

// 假设我们要"删除"第二行的第二个元素
isValid[1][1] = false;

// 遍历并打印(忽略已删除的元素)
for (int i = 0; i < ROWS; ++i) {
    for (int j = 0; j < COLS; ++j) {
        if (isValid[i][j]) {
            std::cout << matrix[i][j] << " ";
        }
    }
    std::cout << std::endl;
}
  1. 重新分配内存(对于静态二维数组)

如果你确实需要改变静态二维数组的大小或彻底删除某些元素,你可能需要重新分配一个新的二维数组(或std::vector<std::vector<T>>),并将需要保留的元素复制过去。这种方法比较复杂,且效率较低,因为它涉及到内存分配和复制操作。

总结

对于需要动态改变大小的二维数组,推荐使用std::vector<std::vector<T>>。如果你必须使用静态二维数组,并且需要删除元素,你可能需要采用标记删除的方法,或者考虑重新分配内存。


3.二维数组的元素访问和修改

这个案例较为简单,在上一章有说到过,这里不做过多演示。C++之二维数组及多维数组

4.二维数组查找元素下标

在C++中,对于静态分配的二维数组,要查找一个元素的下标(即行索引和列索引),通常需要遍历整个数组。这是因为二维数组在内存中是以行优先的方式连续存储的,但C++标准库并没有直接提供查找二维数组中元素下标的函数。

以下是一个示例函数,用于在二维数组中查找一个特定元素的下标。如果找到该元素,函数返回其行索引和列索引;如果未找到,则返回某种表示未找到的标记(比如-1或std::pair<int, int>的特定值)。

cpp 复制代码
#include <iostream>
#include <utility> // For std::pair

// 假设我们要在int类型的二维数组中查找
template<size_t ROWS, size_t COLS>
std::pair<int, int> findElementIndex(const int (&arr)[ROWS][COLS], int value) {
    for (size_t i = 0; i < ROWS; ++i) {
        for (size_t j = 0; j < COLS; ++j) {
            if (arr[i][j] == value) {
                return {static_cast<int>(i), static_cast<int>(j)}; // 返回行索引和列索引
            }
        }
    }
    // 如果没有找到,返回{-1, -1}作为未找到的标记
    return {-1, -1};
}

int main() {
    int matrix[3][3] = {
        {1, 2, 3},
        {4, 5, 6},
        {7, 8, 9}
    };

    int valueToFind = 5;
    auto [rowIndex, colIndex] = findElementIndex(matrix, valueToFind);

    if (rowIndex != -1 && colIndex != -1) {
        std::cout << "Found " << valueToFind << " at (" << rowIndex << ", " << colIndex << ")" << std::endl;
    } else {
        std::cout << valueToFind << " not found in the array." << std::endl;
    }

    return 0;
}

注意:

这个函数使用了模板参数ROWS和COLS来指定二维数组的行数和列数,这是为了能够在编译时确定数组的大小,以便函数能够正确地遍历整个数组。然而,这也意味着这个函数对于不同大小的二维数组需要不同的实例化。

如果你的二维数组大小在运行时确定,你可能需要使用std::vector<std::vector<int>>或其他动态数据结构,并相应地调整查找函数。

以上就是二维数组的简单应用。至于多维数组的简单应用相信你学完下一章就会举一反三了。所以我们下一章要说的是动态数组vector,一个十分好用的工具。

🆗到这里,这篇关于C++二维数组的补充就说完了,求一个免费的赞,感谢阅读,我们下期见。

下期预告:C++的动态数组vector
相关推荐
charlie1145141914 分钟前
C++ STL CookBook
开发语言·c++·stl·c++20
袁袁袁袁满5 分钟前
100天精通Python(爬虫篇)——第113天:‌爬虫基础模块之urllib详细教程大全
开发语言·爬虫·python·网络爬虫·爬虫实战·urllib·urllib模块教程
ELI_He99911 分钟前
PHP中替换某个包或某个类
开发语言·php
Lenyiin12 分钟前
01.02、判定是否互为字符重排
算法·leetcode
小林熬夜学编程15 分钟前
【Linux网络编程】第十四弹---构建功能丰富的HTTP服务器:从状态码处理到服务函数扩展
linux·运维·服务器·c语言·网络·c++·http
m0_7482361118 分钟前
Calcite Web 项目常见问题解决方案
开发语言·前端·rust
倔强的石头10626 分钟前
【C++指南】类和对象(九):内部类
开发语言·c++
鸽鸽程序猿27 分钟前
【算法】【优选算法】宽搜(BFS)中队列的使用
算法·宽度优先·队列
Jackey_Song_Odd27 分钟前
C语言 单向链表反转问题
c语言·数据结构·算法·链表
Watermelo61731 分钟前
详解js柯里化原理及用法,探究柯里化在Redux Selector 的场景模拟、构建复杂的数据流管道、优化深度嵌套函数中的精妙应用
开发语言·前端·javascript·算法·数据挖掘·数据分析·ecmascript