数组详解:定义、作用、应用场景及与链表的区别(C/C++ 代码讲解)

1. 什么是数组

数组是一种线性数据结构 ,它把相同类型 的多个元素按连续的内存空间 依次存储,并通过**下标(索引)**来访问每个元素。数组是编程语言中最基础、最常用的数据结构之一。

在 C/C++ 中,数组的声明格式为:

cpp 复制代码
// 声明一个包含 5 个 int 元素的数组
int arr[5];

// 声明并初始化
int nums[5] = {10, 20, 30, 40, 50};

// 省略长度,由编译器根据初始化列表推断
int scores[] = {90, 85, 92, 88};

数组的下标从 0 开始 ,例如 nums[0] 表示第一个元素 10,nums[4] 表示第五个元素 50。数组在内存中是连续存放的,因此可以通过下标直接计算出元素的地址,实现 O(1) 的随机访问。

2. 数组的作用

数组的核心作用可以概括为以下几点:

  • 批量存储同类型数据:用一个变量名管理多个数据,避免为每个数据单独声明变量。
  • 高效随机访问:通过下标可以在常数时间内访问任意元素,时间复杂度为 O(1)。
  • 内存连续、缓存友好:由于元素在内存中连续排列,遍历时能充分利用 CPU 缓存,提升访问速度。
  • 作为其他数据结构的基础:栈、队列、哈希表、堆等高级数据结构,底层往往都依赖数组实现。

下面通过一段代码演示数组的基本操作:

cpp 复制代码
#include <iostream>
using namespace std;

int main() {
    int arr[5] = {10, 20, 30, 40, 50};

    // 随机访问:O(1)
    cout << "第三个元素:" << arr[2] << endl;

    // 遍历数组
    for (int i = 0; i < 5; i++) {
        cout << "arr[" << i << "] = " << arr[i] << endl;
    }

    // 修改元素
    arr[0] = 99;
    cout << "修改后第一个元素:" << arr[0] << endl;

    return 0;
}

3. 数组的应用场景

数组在实际开发中应用非常广泛,常见场景包括:

  • 存储固定数量的数据:例如存储一个班级学生的成绩、一周七天的温度等。
  • 实现查找和排序算法:二分查找、冒泡排序、快速排序等算法都直接基于数组操作。
  • 作为动态数据结构的底层存储:例如用数组实现栈、队列、循环队列等。
  • 矩阵和图像处理:二维数组天然适合表示矩阵、图像像素等二维数据。
  • 字符串处理:C 语言中字符串本质上就是字符数组。
  • 哈希表的冲突解决:开放寻址法直接使用数组存储键值对。

下面给出一个用数组实现栈的简单示例:

cpp 复制代码
#include <iostream>
using namespace std;

class ArrayStack {
private:
    int* data;
    int capacity;
    int topIndex;

public:
    ArrayStack(int cap) {
        capacity = cap;
        data = new int[capacity];
        topIndex = -1;
    }

    void push(int value) {
        if (topIndex < capacity - 1) {
            data[++topIndex] = value;
        }
    }

    int pop() {
        if (topIndex >= 0) {
            return data[topIndex--];
        }
        return -1; // 栈空
    }

    ~ArrayStack() {
        delete[] data;
    }
};

int main() {
    ArrayStack stack(10);
    stack.push(1);
    stack.push(2);
    stack.push(3);
    cout << "出栈:" << stack.pop() << endl; // 输出 3
    return 0;
}

4. 数组与链表的区别

数组和链表是两种最基础的线性数据结构,它们在内存布局、访问方式、插入删除效率等方面有显著差异。下面从多个维度进行对比:

对比维度 数组 链表
内存布局 连续内存空间 非连续,节点通过指针连接
随机访问 O(1),通过下标直接访问 O(n),需要从头遍历
插入/删除(已知位置) O(n),需要移动大量元素 O(1),只需修改指针
空间分配 静态分配或动态一次性分配,大小固定 动态分配,按需申请节点
空间利用率 高,无额外指针开销 较低,每个节点需额外存储指针
缓存友好性 高,连续内存利于 CPU 缓存 低,节点分散,缓存命中率低
扩容 需要重新分配更大的内存并拷贝 天然支持动态增长

下面用代码直观展示数组和链表在插入、访问上的差异:

cpp 复制代码
#include <iostream>
using namespace std;

// 链表节点定义
struct Node {
    int data;
    Node* next;
    Node(int val) : data(val), next(nullptr) {}
};

// 在链表头部插入节点:O(1)
Node* insertHead(Node* head, int val) {
    Node* newNode = new Node(val);
    newNode->next = head;
    return newNode;
}

// 遍历链表查找元素:O(n)
int getByIndex(Node* head, int index) {
    Node* cur = head;
    int i = 0;
    while (cur != nullptr && i < index) {
        cur = cur->next;
        i++;
    }
    return (cur != nullptr) ? cur->data : -1;
}

int main() {
    // 数组:随机访问 O(1)
    int arr[5] = {10, 20, 30, 40, 50};
    cout << "数组下标访问 arr[3] = " << arr[3] << endl;

    // 链表:插入 O(1),访问 O(n)
    Node* head = nullptr;
    head = insertHead(head, 50);
    head = insertHead(head, 40);
    head = insertHead(head, 30);
    cout << "链表第 2 个元素 = " << getByIndex(head, 2) << endl;

    return 0;
}

5. 总结

数组和链表各有优劣,选择哪种数据结构取决于具体场景:

  • 如果读多写少 、需要频繁按下标访问元素,优先选择数组
  • 如果写多读少 、需要频繁在中间插入或删除元素,优先选择链表
  • 如果数据规模固定且对性能要求高,数组通常更合适;如果数据规模动态变化较大,链表更灵活。

理解数组和链表的本质区别,是掌握数据结构与算法的基础,也是写出高效代码的关键一步。

相关推荐
hongmai66688829 分钟前
MP2348GTL-Z:这颗24V/4A同步降压芯片,把性能和体积平衡得恰到好处
c语言·开发语言·arm开发·单片机·5g
多弗朗皮卡丘32 分钟前
C语言梦开始的地方19:文件操作
c语言·开发语言
Dovis(誓平步青云)33 分钟前
折叠屏悬停看视频,上半屏和下半屏应该各做什么
android·java·服务器·开发语言·安全·音视频
benchmark_cc44 分钟前
Python量化实战:如何检测并剔除历史数据中的“闪崩/乌龙指”异常价格点?
开发语言·人工智能·python·量化·quantdash·量化数据源
2501_937860941 小时前
Java多线程初阶(下)—— synchronized、volatile、wait/notify与经典并发工具
java·开发语言·jvm
笨笨饿1 小时前
#135_代码中的类型重定义艺术:从基础封装到函数类型设计
开发语言·stm32·单片机·嵌入式硬件·mcu·物联网·嵌入式实时数据库
统计学小王子1 小时前
R语言入门——ggplot2图例增加公式
开发语言·r语言
牛油果子哥q1 小时前
C++内存池与对象池精讲:内存碎片、自定义分配器、对象池实现、STL allocator原理、工程落地与性能对比
java·开发语言·c++
CodeStats1 小时前
《源纹天书》第三百六十三章至第三百六十四章
java·开发语言·源纹天书