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

数据结构与算法--数组

文章目录

  • 前言
  • 一、什么是数组
    • 1.1 定义
    • 1.2 数组特性
    • 1.3 时间复杂度
    • 1.4 空间复杂度
  • 二、基本操作
    • 2.1 插入数据
    • 2.2 删除数据
    • 2.3 随机访问
  • 三、关于数组的笔试题
    • 3.1 字符串逆序
    • 3.2 组合问题
    • 3.3 合并两个数组
    • 3.4 重排问题


前言

每一种编程语言中,基本都有数组这种数据类型。它不仅仅是一种数据类型,还是一种最基础的数据结构。


一、什么是数组

1.1 定义

数组(Array)是一种线性表数据结构。它用一组连续的内存空间,来存储一组具有相同类型的数据。
在这里插入图片描述

1.2 数组特性

  1. 支持随机访问,根据下标随机访问的时间复杂度为 O(1)。
  2. 连续的内存空间、相同类型的数据:因此可以随机访问,但为了保证内存的连续性,需要做大量的数据搬移工作,使增删等操作变得非常低效。
  3. 线性存储,程序执行效率高。

1.3 时间复杂度

  • 插入元素:O(n),所插入位置后面的元素都要向后移动。
  • 删除元素:O(n),所删除位置后面的元素都要向前移动。
  • 访问元素:O(1),使用索引访问。
  • 查找元素:与使用查找算法有关,如顺序查找、二分查找、插值查找等。

1.4 空间复杂度

数组只用来存储指定类型的数据,所以存储n个数据的空间复杂度为O(n)。

二、基本操作

2.1 插入数据

数组插入数据,特定场景,特殊对待,如下:

  • 数组需要保持有序:将插入位置k后面的数据元素依次后移,空出要插入的位置,再将新元素插入。O(n)
  • 数组不需要保持有序:将插入位置的旧元素移到最后,再在k处插入新元素。或直接将新元素放在最后。O(1)

2.2 删除数据

数组删除数据,特定场景,特殊对待,如下:

  • 考虑内存连续性:

    数组需要保持有序:删除k位置元素后,将后面的元素依次前移一个位置。O(n)

    数组不需要保持有序:用最后一个元素覆盖要删除的元素。O(1)

  • 不考虑内存连续:

    标记删除法,将要删除的元素标记(没有真正的删除),当数组空间不够时,一次性删除。但会造成内存不连续,如下图:
    在这里插入图片描述

数组空间:数组所占内存空间,不随插入或删除元素而改变,除非申请空间
数组长度:数组所存储的数据元素个数,随插入或删除元素而改变

2.3 随机访问

以存储整型数据为例
在这里插入图片描述
对于数组int a[],其地址为a,第一个数组元素的地址和数组的地址相等,第二个数据元素的地址为a+4,依次类推,每往后移动一个数据元素,地址加4,可见只要知道了数组的首地址,就知道了数组中每一个元素的地址,所以利用数组下标可以实现数组元素的随机访问。

随机访问寻址方法:a[i]_address = base_address + i * data_type_size

其中,base_address 为数组首地址,data_type_size 为每个元素的大小,依数据类型确定。

#include <stdio.h>

int main()
{
    int a[] = {0, 1, 2, 3};

    printf("   a.address: %p\n", a);
    for (int i = 0; i < 4; i++)
    { 
	printf("a[%d].address: %p\n", i, &a[i]);
    }
}

打印结果为:

 a.address: 0x7ffd46f51c50
a[0].address: 0x7ffd46f51c50
a[1].address: 0x7ffd46f51c54
a[2].address: 0x7ffd46f51c58
a[3].address: 0x7ffd46f51c5c

三、关于数组的笔试题

3.1 字符串逆序

给定一个含有n个元素的字符数组a,将其原地逆序

// 字符串逆序
void Reverse(char*a, int n)
{
     int left =0; 
     int right = n -1;

     while (left < right)
     {
         char temp = a[left] ;
         a[left++] = a[right] ;
         a[right--] = temp ;
     }
}

3.2 组合问题

给定一个含有n个元素的整型数组a,从中任取m个元素,求所有组合。比如下面的例子
a = 1, 2, 3, 4, 5
m = 3
输出
1 2 3, 1 2 4, 1 2 5, 1 3 4, 1 3 5, 1 4 5
2 3 4, 2 3 5, 2 4 5
3 4 5

// n选m的所有组合
int buffer[100] ;

void PrintArray(int *a, int n)
{
    for (int i = 0; i < n; ++i)
        cout << a[i] << "";
    cout << endl ;
}

bool IsValid(int lastIndex, int value)
{
    for (int i = 0; i < lastIndex; i++)
    {
        if (buffer[i] >= value)
            return false;
    }
    return true;
}

void Select(int t, int n, int m)
{
    if (t == m)
        PrintArray(buffer, m);
    else
    {
        for (int i = 1; i <= n; i++)
        {
            buffer[t] = i;
            if (IsValid(t, i))
                Select(t + 1, n, m);
        }
    }
}

3.3 合并两个数组

给定含有n个元素的两个有序(非降序)整型数组a和b。合并两个数组中的元素到整型数组c,要求去除重复元素并保持c有序(非降序)。例子如下
a = 1, 2, 4, 8
b = 1, 3, 5, 8
c = 1, 2, 3, 4, 5, 8
分析
利用合并排序的思想,两个指针i,j和k分别指向数组a和b,然后比较两个指针对应元素的大小,有以下三种情况

  1. a[i] < b[j],则c[k] = a[i]。

  2. a[i] == b[j],则c[k]等于a[i]或b[j]皆可。

  3. a[i] > b[j],则c[k] = b[j]。

重复以上过程,直到i或者j到达数组末尾,然后将剩下的元素直接copy到数组c中即可

// 合并两个有序数组
void Merge(int *a, int *b, int *c, int n)
{
    int i = 0 ;
    int j = 0 ;
    int k = 0 ;

    while (i < n && j < n)
    {
        if (a[i] < b[j])// 如果a的元素小,则插入a中元素到c
        {
            c[k++] = a[i] ;
            ++i ;
        }
        else if (a[i] == b[j])// 如果a和b元素相等,则插入二者皆可,这里插入a
        {
            c[k++] = a[i] ;
            ++i ;
            ++j ;
        }
        else // a[i] > b[j] // 如果b中元素小,则插入b中元素到c
        {
            c[k++] = b[j] ;
            ++j ;
        }
    }

    if (i == n) // 若a遍历完毕,处理b中剩下的元素
    {
        for (int m = j; m < n; ++m)
            c[k++] = b[m] ;
    }
    else//j == n, 若b遍历完毕,处理a中剩下的元素
    {
        for (int m = i; m < n; ++m)
            c[k++] = a[m] ;
    }
}

3.4 重排问题

给定含有n个元素的整型数组a,其中包括0元素和非0元素,对数组进行排序,要求:

  1. 排序后所有0元素在前,所有非零元素在后,且非零元素排序前后相对位置不变

  2. 不能使用额外存储空间

例子如下

输入 0, 3, 0, 2, 1, 0, 0

输出 0, 0, 0, 0, 3, 2, 1

void Arrange(int* a, int n)
{
    int k = n -1 ;
    for (int i = n -1; i >=0; --i)
    {
        if (a[i] !=0)
        {
            if (a[k] ==0)
            {
                a[k] = a[i] ;
                a[i] =0 ;
            }
            --k ;
        }
    }
}

相关文章:

  • jvm oom内存溢出,导出dump,使用mat进行问题分析
  • 百钱百鸡问题(C++枚举法)
  • 基于SSM实现智慧幼儿园信息管理系统
  • 九月组队学习计划!
  • OJ在线编程输入输出(Java版)
  • Matlab代码批处理中国地面气象日值数据集(2400站点数据集),提取所需省份全部站点数据
  • 链表之头指针、头结点、首元结点、空链表
  • 【Linux】静态库与共享库
  • POI入门
  • 07- 诊断事件diagnostic events的类图关系
  • 【C#】RestSharp踩坑日记
  • 自学5个月软件测试找到一个8k的工作,我的学习方式值得你借鉴
  • 【JavaEE初阶】文件操作 和 IO (下篇)
  • Nebula Studio:部署与连接
  • Redis 学习笔记
  • [译]Python中的类属性与实例属性的区别
  • canvas 绘制双线技巧
  • canvas实际项目操作,包含:线条,圆形,扇形,图片绘制,图片圆角遮罩,矩形,弧形文字...
  • CODING 缺陷管理功能正式开始公测
  • CSS3 变换
  •  D - 粉碎叛乱F - 其他起义
  • Git学习与使用心得(1)—— 初始化
  • java8 Stream Pipelines 浅析
  • Java深入 - 深入理解Java集合
  • JS学习笔记——闭包
  • js正则,这点儿就够用了
  • mongo索引构建
  • SegmentFault 社区上线小程序开发频道,助力小程序开发者生态
  • Spring Cloud Alibaba迁移指南(一):一行代码从 Hystrix 迁移到 Sentinel
  • vue-cli在webpack的配置文件探究
  • 使用Swoole加速Laravel(正式环境中)
  • 双管齐下,VMware的容器新战略
  • 微信公众号开发小记——5.python微信红包
  • 一起参Ember.js讨论、问答社区。
  • 用简单代码看卷积组块发展
  • 远离DoS攻击 Windows Server 2016发布DNS政策
  • 在weex里面使用chart图表
  • 如何正确理解,内页权重高于首页?
  • #前后端分离# 头条发布系统
  • $con= MySQL有关填空题_2015年计算机二级考试《MySQL》提高练习题(10)
  • $Django python中使用redis, django中使用(封装了),redis开启事务(管道)
  • (Java实习生)每日10道面试题打卡——JavaWeb篇
  • (待修改)PyG安装步骤
  • (第61天)多租户架构(CDB/PDB)
  • (机器学习-深度学习快速入门)第三章机器学习-第二节:机器学习模型之线性回归
  • (介绍与使用)物联网NodeMCUESP8266(ESP-12F)连接新版onenet mqtt协议实现上传数据(温湿度)和下发指令(控制LED灯)
  • (篇九)MySQL常用内置函数
  • (译) 函数式 JS #1:简介
  • (转)大道至简,职场上做人做事做管理
  • .NET 编写一个可以异步等待循环中任何一个部分的 Awaiter
  • .NET 解决重复提交问题
  • .Net面试题4
  • .NET中使用Redis (二)
  • .NET中统一的存储过程调用方法(收藏)
  • /bin/bash^M: bad interpreter: No such file ordirectory