线性表之数组

news2024/11/13 10:42:12

        数组(Array)是 C/C++ 中最基础和重要的数据结构之一,它提供了一种有效存储和访问固定大小元素集合的方式。关于数组的定义和使用相信大家都已经熟练掌握,本文将着重为大家剖析数组的物理结构和逻辑结构。

1. 数组的物理结构

        数组的物理结构是指数组元素在内存中的实际存储方式。在内存中,数组元素是连续存储的,这意味着相邻元素的地址是连续的,且每个元素占用固定大小的内存空间。

        例如,对于一个整型数组 int numbers[5],如果数组的起始地址为 0x1000,每个整型元素占据4个字节,那么数组中的元素在内存中的存储情况可能如下:

0x1000: numbers[0]
0x1004: numbers[1]
0x1008: numbers[2]
0x100C: numbers[3]
0x1010: numbers[4]

        这样的存储方式保证了对数组元素的快速访问和遍历,因为可以通过计算地址偏移来访问数组中的任意元素。

2. 数组的逻辑结构

2.1 线性表

        数组是一种线性数据结构,它由相同类型的元素组成,并以连续的内存地址存储。所谓的线性表就是由一个或多个元素组成的有序序列。在一个线性表中头部节点只有一个后继节点,尾部节点只有一个前驱节点,头部和尾部中间的节点分别有一个前驱节点,一个后继节点,如下图:

        根据从前到后的顺序,我们就可以将A1记作头结点,A5记作尾节点,A2~A4记作中间节点。拿A3举例,它的前驱节点是A2,后继节点是A4。

        在逻辑上,数组是一个有序集合,每个元素可以通过唯一的下标(索引)来访问。

        例如,数组 int numbers[5] 中的元素可以用 numbers[0]、numbers[1]、numbers[2]、numbers[3] 和 numbers[4] 这些下标来访问。这种抽象方式隐藏了数组元素在内存中的实际存储方式,使程序员只需关注元素的逻辑位置而无需担心其物理存储。

2.2 数组的优缺点

关于数组的使用,它的优缺点总结如下:

优点

  • 通过下标访问数组中的任意元素速度非常快,时间复杂度是 O(1)
  • 末尾位置增加、删除元素速度非常快,时间复杂度是O(1)
  • 访问当前元素前、后相邻位置的元素非常方便

缺点

  • 非末尾位置增加、删除元素需要进行大量的数据移动,时间复杂度为 O(n)
  • 数组扩容的时候资源开销比较大

2.3 动态数组

        在C语言中是没有动态数组的概念的,只有静态数组。想要使用动态数组只能由程序员自己实现,其核心思想有以下几点:

  • 记录下数组的容量和数组中存储的数据的个数
  • 当数组的容量 == 存储的数据个数的时候重新申请一块新内存或者扩容

        与此同时,如果要求这个动态数组可以在不同场景下存储不同类型的数据,我们就需要使用泛型编程,因此可以这样定义这个类:

#include <iostream>
using namespace std;

template <typename T>
class Array
{
public:
    Array(int size = 64);
    ~Array();

    // 在尾部添加元素
    void append(T val);
    // 尾部删除元素
    void popBack();
    // 插入元素
    void insert(int pos, T val);
    // 删除元素
    void remove(int pos);
    // 查询元素-> 返回位置
    int find(T val);
    // 得到指定位置的元素的值
    int value(int pos);
    // 获取数组元素数量
    int size();
    // 打印数据
    void show();
private:
    void expand(int size);
private:
    T* m_arry;           // 数组的起始地址
    int m_capacity;      // 数组容量
    int m_count;         // 数组中的元素数量
};

        在编写模板类的时候需要注意一个问题:类的声明和定义要写到同一个文件中,如果分开写到 .h 和 .cpp 中,编译的时候就会出现链接错误。因此在实现上边这个的类的时候可以将代码写到一个 .h 或者 .cpp 文件中。

array.cpp 文件

#include <iostream>
#include <cassert>
using namespace std;

template <typename T>
class Array
{
public:
    Array(int size = 64);
    ~Array();

    // 在尾部添加元素
    void append(T val);
    // 尾部删除元素
    void popBack();
    // 插入元素
    void insert(int pos, T val);
    // 删除元素
    void remove(int pos);
    // 查询元素-> 返回位置
    int find(T val);
    // 得到指定位置的元素的值
    int value(int pos);
    // 获取数组元素数量
    int size();
    // 打印数据
    void show();

private:
    void expand(int size);

private:
    T* m_arry;           // 数组的起始地址
    int m_capacity;      // 数组容量
    int m_count;         // 数组中的元素数量
};

template <typename T>
Array<T>::Array(int size) :
    m_capacity(size),
    m_count(0),
    m_arry(new T[size]()) 
{
}

template <typename T>
Array<T>::~Array()
{
    // 释放资源
    delete[]m_arry;
    m_arry = nullptr;
}

template <typename T>
void Array<T>::append(T val)
{
    // 满了, 扩容
    if (m_count == m_capacity)
    {
        expand(m_capacity * 2);
    }
    m_arry[m_count++] = val;
}

template <typename T>
void Array<T>::popBack()
{
    if (m_count == 0)
    {
        return;
    }
    m_count--;  // 把尾部元素变成了无效元素
}

template <typename T>
void Array<T>::insert(int pos, T val)
{
    if (pos < 0 || pos > m_count) 
    {
        cout << "插入数据失败: 无效的pos位置" << endl;
        return;
    }
    if (m_count == m_capacity)
    {
        expand(2 * m_capacity);
    }
    // 移动元素
    for (int i = m_count - 1; i >= pos; --i)
    {
        m_arry[i + 1] = m_arry[i];
    }
    m_arry[pos] = val;
    m_count++;
}

template <typename T>
void Array<T>::remove(int pos)
{
    if (pos < 0 || pos >= m_count)  
    {
        return;
    }
    int value = m_arry[pos];
    for (int i = pos + 1; i < m_count; ++i)
    {
        m_arry[i - 1] = m_arry[i];
    }
    m_count--;
}

template <typename T>
int Array<T>::find(T val)
{
    for (int i = 0; i < m_count; ++i)
    {
        if (m_arry[i] == val)
        {
            return i;
        }
    }
    return -1;
}

template<typename T>
int Array<T>::value(int pos)
{
    assert(pos >= 0 && pos < m_count);
    return m_arry[pos];
}

template<typename T>
int Array<T>::size()
{
    return m_count;
}

template <typename T>
void Array<T>::show()
{
    for (int i = 0; i < m_count; ++i)
    {
        cout << m_arry[i] << " ";
        cout << (int)m_arry[i] << " ";
    }
    cout << endl;
}

template <typename T>
void Array<T>::expand(int size)
{
    // 申请一块新的内存
    T* ptr = new T[size]();
    // 旧数据拷贝到新内存
    memcpy(ptr, m_arry, sizeof(T) * m_capacity);
    delete[]m_arry;
    // 数据更新
    m_arry = ptr;
    m_capacity = size;
}

        上面的代码是通过append或者insert函数往数组中添加数据的时候,判断了数组中已存储的元素数量,如果数组已满便调用expand函数进行扩容。

  • 使用relloc函数扩容比重新分配内存的方式要简单一些,如果它在其它位置开辟了新的存储空间而不是在尾部扩容,会自动释放旧的内存块。
  • 对数组进行动态扩容之后,要对数组的容量进行更新。
  • 对数组进行动态扩容之后,要更新数组指针指向的起始地址。

        另外,通过阅读上述代码也可以证明在数组的中间位置添加、删除数据都会涉及到元素的大量移动(后移或者前移),操作相率相对较低。

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

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

相关文章

视频技术未来展望:EasyCVR如何引领汇聚融合平台新趋势

随着科技的飞速发展&#xff0c;视频技术已成为现代社会不可或缺的一部分&#xff0c;广泛应用于安防监控、娱乐传播、在线教育、电商直播等多个领域。本文将探讨视频技术的未来发展趋势&#xff0c;并深入分析TSINGSEE青犀EasyCVR视频汇聚融合平台的技术优势&#xff0c;展现其…

【SolidWorks2024 详细安装教程【附安装包】】

提示&#xff1a;【SolidWorks2024 详细安装教程【附安装包】】 文章目录 安装包获取一、安装步骤总结 安装包获取 提示&#xff1a;这里可以获得软件安装包&#xff1a; SolidWorks2024详细安装教程&#xff0c;百度网盘 链接&#xff1a;https://pan.baidu.com/s/1UyipwXokK…

rsync搭建全网备份

rsync搭建全网备份 1. 总体概述1.1 目标1.2 简易指导图1.3 涉及工具或命令1.4 环境 2. 实施2.1 配置备份服务器2.2 备份文件准备2.3 整合命令2.4 扩展功能 1. 总体概述 1.1 目标 本次搭建目标&#xff1a; 每天定时把服务器数据备份到备份服务器备份完成后进行校验把过期数据…

【python】turtle的使用

文章目录 1.初始化2.颜色3.画笔4.其他案例&#xff1a;分形树的绘制 1.初始化 import turtle# 创建一支画笔 pen turtle.Turtle()# ...# 暂停屏幕&#xff0c;防止程序关闭 turtle.done()2.颜色 # 设置颜色模式(如果要使用颜色相关设置&#xff0c;必须要使用这个) turtle.c…

基于STM32的RFID高速收费系统(论文+源码+实物)

1系统方案设计 本文基于STM32的RFID高速收费系统&#xff0c;其可以实现小车和货车两种车型收费&#xff0c;当车辆超过了规定的重量后&#xff0c;出现声光报警提示&#xff0c;并且启动杆不会抬起&#xff0c;只有当车辆重量低于设置值时&#xff0c;启动杆才会自动抬起&…

零基础学习Redis(7) -- hash类型命令使用

Redis本身就是通过哈希表的方式组织数据&#xff0c;同时redis中的value也可以是另一个哈希表。 1. 常用命令 1. hset / hsetnx hset key filed1 value1 filed2 value2 ... hset 用于把键值对存入value中&#xff0c;这里的key为redis组织的键&#xff0c; filed1 value1 fil…

SpringData-ElasticSearch入门

文章目录 1、创建demo工程2、application.properties3、Goods 实体类4、EsDemoApplicationTests 测试类5、pom.xml6、查看索引库7、查看单个索引&#xff08;数据库&#xff09;8、从goods索引中检索出符合特定搜索条件的文档&#xff08;或记录&#xff09; 1、创建demo工程 2…

Elasticsearch:使用 LTR 进行个性化搜索

作者&#xff1a;来自 Elastic Max Jakob 如今&#xff0c;用户已经开始期待根据个人兴趣定制搜索结果。如果我们听的所有歌曲都是摇滚歌曲&#xff0c;那么在搜索 “Crazy” 时&#xff0c;我们会期望 Aerosmith 的歌曲排在搜索结果的首位&#xff0c;而不是 Gnarls Barkley 的…

使用安信可Ai-WB2-12F开启wifi与手机通信TCP-IP(AT指令)

当时在做两个单片机之间无线通信&#xff0c;或者单片机与手机无线通信&#xff0c;就像找一个蓝牙和wifi双模的无线模块&#xff0c;一开始看ESP8684&#xff08;ESP32-C2&#xff09;这个芯片模组是有wifi和蓝牙的&#xff0c;买回来后才发现他不可以在程序运行中更换蓝牙或者…

《黑神话·悟空》这款游戏到底是用什么编程语言开发的?

你也有被这段游戏试玩视频刷屏吗&#xff1f; 13分钟、国产团队出品、B站上线不到24小时&#xff0c;播放量已经破千万&#xff0c;迅速火爆全网。 这就是来自国内游戏团队游戏科学&#xff08;Game Science&#xff09;开发的3A大作《黑神话&#xff1a;悟空》。 《黑神话悟…

vscode开发小程序

1 安装 "微信小程序开发工具" 2 安装 "WXML - Language Service" 3 安装 "wxmp-api-plugin" 或 "wechat-snippet" 4 安装"WXSS"

顶级的python入门教程!小白到大师,从这篇教程开始!

1. 为什么要学习Python&#xff1f; 学习Python的原因有很多&#xff0c;以下是几个主要的原因&#xff1a; 广泛应用&#xff1a;Python被广泛应用于Web开发、数据科学、人工智能、机器学习、自动化运维、网络爬虫、科学计算、游戏开发等多个领域。掌握Python意味着你可以在这…

嵌入式全栈开发学习笔记---Linux系统编程(进程间通信)

目录 进程间通信概述 进程通信目的 进程间通信的发展 进程间通信分类 管道通信 无名管道 有名管道mkfifo() 信号 发送信号kill & raise 忽略信号signal() 发送信号alarm() 消息队列 消息队列使用的步骤 创建消息队列msgget() 读写消息队列msgrcv()/msgsnd()…

ip地址一天变化好几次

‌IP地址每天变化的原因主要取决于其分配方式&#xff1a;静态或动态。静态IP地址是长期固定分配给一台设备的&#xff0c;除非进行手动更改或网络配置发生变化&#xff0c;否则该设备的IP地址将保持不变。而动态IP地址则是根据网络环境和需求动态分配给设备的&#xff0c;可能…

一些评估模型的总结(1)

最近学习了评估模型&#xff08;如下所示&#xff09;&#xff0c;对这四种方法进行小总结。 目录 1. 层次分析法。&#xff08;主观赋权方法&#xff0c;主观确定成对比较矩阵&#xff09; 2. 熵权法&#xff08;基于数据的客观赋权的方法&#xff09; 3. topsis方法&…

【图论入门】图的存储

1.邻接矩阵 邻接矩阵是图论中用于表示图&#xff08;Graph&#xff09;结构的一种重要数据结构&#xff0c;特别适用于表示顶点之间连接关系的图形。在计算机科学和数学领域&#xff0c;它被广泛应用来编码无向图和有向图的信息。 特点&#xff1a; 1、无向图的邻接矩阵是对称…

Java:时区的用法

文章目录 ZoneId常见用法 ZonedDateTime常见方法 代码 黑马学习笔记 ZoneId 常见用法 ZonedDateTime 常见方法 代码 package NewTime;import java.time.Clock; import java.time.ZoneId; import java.time.ZonedDateTime;/*** Author: ggdpzhk* CreateTime: 2024-08-31*/ pu…

09:Logic软件原理图信号连通

原理图信号连通 快捷键&#xff1a;F2 2.添加网络名称

【React】为什么Hooks不能出现在判断中

前言 在 React 中&#xff0c;Hooks 不能写在条件语句中&#xff0c;如下面这段代码点击button后则会报错。 import { useEffect, useState } from "react"export default () > {const [count, setCount] useState(0)if (count > 0) {useEffect(() > {co…

4-4 初始化引导程序

基本原理的讲解 在loader所需要做的事情&#xff0c; 1 他这个检测内存的容量&#xff0c;我想知道是怎么做的。 2 然后就是模式的切换。 3 然后就是加载操作系统&#xff0c;并跳转到操作系统执行。 这是 他的总体的逻辑。 首先是加载 512 字节。 所以这512 字节的主要任务…