C++数组(一)(算法竞赛)


🌊用有趣的言语来阐述苦涩难懂的代码世界,让每一个技术都充满风趣!

🔭个人主页: 散峰而望

🚀学习方向: C/C++等方向

📌专栏系列:

💬人生格言: 冀以尘雾之微,补益山海,荧烛末光,增辉岁月。

🎬博主简介

文章目录

  • 前言
  • [1. 一维数组](#1. 一维数组)
    • [1.1 数组创建](#1.1 数组创建)
    • [1.2 数组的初始化](#1.2 数组的初始化)
    • [1.3 数组元素访问](#1.3 数组元素访问)
    • [1.4 数组元素的打印](#1.4 数组元素的打印)
      • [1.4.1 数组和 sizeof](#1.4.1 数组和 sizeof)
      • [1.4.2 数组的输入输出](#1.4.2 数组的输入输出)
    • [1.5 范围 for](#1.5 范围 for)
      • [1.5.1 范围 for 语法](#1.5.1 范围 for 语法)
      • [1.5.1 auto 关键字](#1.5.1 auto 关键字)
    • [1.6 memset 设置数组内容](#1.6 memset 设置数组内容)
      • [1.6.1 memset](#1.6.1 memset)
      • [1.6.1 设置数组内容](#1.6.1 设置数组内容)
      • [1.6.3 错误使用](#1.6.3 错误使用)
    • [1.7 memcpy 拷贝数组内容](#1.7 memcpy 拷贝数组内容)
    • [1.8 练习](#1.8 练习)
  • 结语

前言

前面我们已经将C++的最基础最重要的一部分给讲完了,接下来我们将继续深入另一个在写代码比较常用的知识点数组 ,从而能够面对多种数据不再感到手忙脚乱,能够依靠编号阵列来组织,从而更好的输入或打印多种数据。

数组是一组相同类型元素的集合。从这个概念中我们就可以发现 2 个有价值的信息:

  • 数组中存放的是 1 个或者多个数据,但是数组元素个数不能为 0。
  • 数组中存放的多个数据,类型是相同的。

数组分为一维数组和多维数组,多维数组一般比较多见的是二维数组。

1. 一维数组

一维数组是最常见的,通常用来存放一组数据,一维数组是一块连续的空间。

1.1 数组创建

一维数组创建的基本语法如下:

c 复制代码
type arr_name[常量值];

存放在数组的值被称为数组的元素,数组在创建的时候需要指定数组的大小和数组的元素类型。

  • type 指定的是数组中存放数据的类型,可以是:char、short、int、float 等,也可以自定义的类型
  • arr_name 是数组的名字,这个名字可以自定义,根据实际情况,起的有意义就行。
  • \]中的常量值是用来指定数组的大小的,数组的大小是根据实际的需求指定就行。在算法竞赛中为了为了保证不越界访问,往往会多开辟一些空间,后期题目中会讲到。

  • 例如:int arr[N] ;

比如:我们现在想存储某个公司的20人的业绩,那我们就可以创建一个数组,如下:

cpp 复制代码
//第一种表现形式
int arr[20];

//第二种表现形式
const int N = 20;
int arr[N];

//第三种表现形式
#define N 4;
int arr[N];

当然我们也可以根据创建其他类型和大小的数组:

cpp 复制代码
char ch[8];
double score[10];

1.2 数组的初始化

有时候,数组在创建的时候,我们需要给定一些初始值,这种就称为初始化的。那数组如何初始化呢?数组的初始化一般使用大括号,将数据放在大括号中。

cpp 复制代码
//完全初始化,数据会依次放入数组
int arr[6] = {1,2,3,4,5,6};

//不完全初始化
int arr[6] = {1};
//第一个元素初始化为1,剩余元素默认初始化为0

//错误的初始化 - 初始化项太多 
int arr[3] = {1,2,3,4};

1.3 数组元素访问

数组是有下标的,下标是从 0 开始的,假设数组有 n 个元素,最后一个元素的下标是 n-1,下标就相当于数组元素的编号,如下:

cpp 复制代码
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };

数组的访问提供了一个操作符[] ,这个操作符叫:下标引用操作符

有了下标访问操作符,我们就可以轻松的访问到数组的元素了,比如我们访问下标为 7 的元素,我们就可以使用 arr[7] ,想要访问下标是 3 的元素,就可以使用 arr[3] ,如下代码:

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

int main()
{
	int arr[10] = {1,2,3,4,5,6,7,8,9,10};
	cout << arr[7] << endl;
	cout << arr[3] << endl;
	return 0;
 } 

演示结果:

1.4 数组元素的打印

接下来,如果想要访问整个数组的内容,那怎么办呢?

只要我们产生数组所有元素的下标就可以了,那我们使用 for 循环产生所有的下标,接下来使用下标访问就行了。

注意:如果产生的下标超出了有效下标的范围,比如,使用负数作为下标,或者超出了下标的最大值,再使用这个下标访问元素就会造成:越界访问。越界访问访问的时候,代码编译时语法没报错,但是运行时一定会出问题的。

1.4.1 数组和 sizeof

  • sizeof(数组名),计算的是数组的总大小,单位是字节
  • sizeof(数组名) / sizeof(第一个元素),计算的是数组的元素个数
cpp 复制代码
#include<iostream>
using namespace std;

int main()
{
	int arr[10] = {1,2,3,4,5,6,7,8,9,10};
	cout << sizeof(arr) << endl;
	cout << sizeof(arr) / sizeof(arr[0]) << endl;
	return 0;
 } 

演示结果:

1.4.2 数组的输入输出

我们已经知道如何打印单个的数组元素,那我们该如何遍历数组中所有的元素哩?运用循环,遍历所有的数组下标就能将数组的所有的元素都打印了。

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

int main()
{
	
	int arr[5] = {0};
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);
	//输入
	for(i = 0; i < sz; i++)
	{
		cin >> arr[i];
	 } 
	//输出
	for(i = 0; i < sz; i++)
	{
		cout << arr[i] << " ";
	 } 
	 cout << endl;
	 return 0;
 } 

演示结果:

1.5 范围 for

打印数组元素除了可以使用之前讲过的三种循环外,还有一个更方便的方式,使用范围 for。范围 for 是在 C++11 这个标准中引入的,如果你使用的编译器默认不支持C++11,可能需要配置才能使用。如果没有配置可能会报出这样的错误:

Error\]range-based 'for' 100ps are not allowed in C++98 mode

以DevC++为例教程:Dev-C++一些问题的处理

1.5.1 范围 for 语法

cpp 复制代码
for ( 类型  变量名 : 数组名 )
   语句 //多条语句需要加大括号

示例:打印数组

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

int main()
{
	int arr[10] = {1,2,3,4,5,6,7,8,9,10};
	for(int x : arr)
	{
		cout << x << " ";
	}
	return 0;
 } 

上面代码中的 for 就是范围 for,代码的意思是将 arr 数组中的元素,依次放在 x 变量中,然后打印 x,直到遍历完整个数组的元素,仍发挥着循环的作用。这里的 x 是一个单独的变量,不是数组的元素,所以对 x 的修改,不会影响数组。

但是对于范围 for 要慎重使用!范围 for 是对数组中所有元素进行遍历的,但是我们实际在做题的过程中,可能只需要遍历数组中指定个数 的元素,这样范围 for 就不合适了

1.5.1 auto 关键字

auto 的主要用途是让编译器自动推导出变量的类型的,比如:

cpp 复制代码
#include <iostream>
using namespace std;
int main()
{
    auto a = 3.14;
    auto b = 100;
    auto c = 'x';
    return 0;
}

可以看到使用 vs 2022 调试监视时,a、b、c都推导出变量的类型。

使用 auto 类型,上面的范围 for 也可以这样写:

cpp 复制代码
#include <iostream>
using namespace std;
int main()
{
    int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
    for (auto e : arr)    //auto能够自动推导数组中每个元素的数据类型,在数组范围内遍历打印元素 
    {
        cout << e << " ";
    }
    return 0;
}

演示结果:

范围 for 中 e 前面的类型可以可以是 auto 关键字,当你不知道数组中放什么类型的时候,可以使用 auto 作为类型,auto 在范围 for 中很常用。如果明确的知道数组元素的数据类型,也可以将 auto 换成对应的数据类型。

1.6 memset 设置数组内容

1.6.1 memset

函数原型如下:

cpp 复制代码
void * memset ( void * ptr, int value, size_t num );
参数解释:
ptr -- 指针:指向了要设置的内存块的起始位置
value -- 要设置的值
num -- 设置的字节个数

memset 是用来设置内存的,将内存中的值以字节为单位设置成想要的内容,需要头文件< cstring >。

1.6.1 设置数组内容

代码演示:

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

int main()
{
	char str[] = "hello world";
    memset(str, 'x', 6);
    cout << str << endl;
    
    int arr[] = {1,2,3,4,5};
    memset(arr, 0, sizeof(arr));//这里数组的大小也可以自己计算 
    for(auto i : arr)
    {
        cout << i << " ";
    }
    cout << endl;
    return 0;
}

演示结果:

传输过程演示:

1.6.3 错误使用

代码展示:

cpp 复制代码
#include<iostream>
#include<cstring> 
using namespace std;
int main()
{
    int arr[] = { 1,2,3,4,5 };
    memset(arr, 1, 4 * sizeof(int));
    for (auto e : arr)
    {
        cout << e << " ";
    }
    cout << endl;
    return 0;
}

演示结果:

Dev-C++ 应该对其有优化,输入的还是正常值,所以用 vs 2022 演示

从上面打印结果可以看出,当 value 设置为 1 或者其他非 0 的数字时,打印结果不符合预期。

主要原因是 memset 函数是给每个字节设置 value 值,而一个整型元素占用 4 个字节,一个整型的每个字节都被设置为 1,二进制就是:00000001000000010000000100000001,转换成十进制就是:16843009,因此结果是不符合预期的。

1.7 memcpy 拷贝数组内容

在使用数组的时候,有时候我们需要将数组 a 的内容给数组 b,比如:

cpp 复制代码
int a[10] = {1,2,3,4,5,6,7,8,9,10};
int b[10] = {0};

怎么做呢?直接赋值可以吗?不行!

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

int main()
{
	int a[10] = {1,2,3,4,5,6,7,8,9,10};
	int b[10] = {0};
	b = a;
	cout << b << endl;
	return 0;
 } 

演示结果:

其实 C++ 中有一个库函数 memcpy 可以做数组内容的拷贝,当然 memcpy 其实是用来做内存块的拷贝的,当然用来做数组内容的拷贝也是没问题的。memcpy 需要的头文件是< cstring >。

函数原型如下:

cpp 复制代码
void * memcpy ( void * destination, const void * source, size_t num );
//destination -- 目标空间的起始地址 
//source      -- 源数据空间的起始地址 
//num         -- 拷⻉的数据的字节个数 

代码举例:

cpp 复制代码
#include <iostream>
#include <cstring>
using namespace std;
int main()
{
    int a[10] = {1,2,3,4,5,6,7,8,9,10};
    int b[10] = {0};
    memcpy(b, a, 10 * sizeof(int));
    for(int e: b)
    {
        cout << e << " ";
    }
    return 0;
}

演示结果:

当然如果拷贝 double 类型的数组时,计算时应该使用 sizeof(double),要灵活变化。

1.8 练习

  1. 查找特定的值

思路:

  1. 输入
  2. 输入数组的元素值,得有数组
  3. 输入xx,要查找的值
  4. 查找-遍历数组,去找xx
  5. 输入下标,或者-1
cpp 复制代码
#include<iostream>
using namespace std;

const int N = 10010;//多开辟空间,防止数组越界
//又因为n<=10000,所以设10010 
int arr[N];
 
int main()
{
	int n = 0;
    cin >> n;
    for (int i = 0; i < n; i++)
    {
        cin >> arr[i];
    }
    int k = 0;
    cin >> k;
    int i = 0;
    for (i = 0; i < n; i++)
    {
        if (k == arr[i])
        {
            cout << i << endl;
            break;
        }
    }
    if (i == n)
        cout << -1 << endl;
    return 0;
 } 

提示:

一般在涉及到需要数组存放数据的时候,我们要注意:

  1. 有的题目要求数据从下标0的位置开始存放,也有些题目要求数据是从下标1的位置开始存放,要仔细阅读题目。让从下标 1 开始存放的时候,数组的开辟必须要有多余的空间使用,如果开辟的刚刚好就会越界。
  2. 数组空间的开辟要足够,以免数据越界,所以经常题目需要存放n个数据,我们就开辟 n+10 个空间,这样空间就非常充足,比较保险。其实在空间足够的情况下,浪费一点空间是不影响的。在后期学习动态规划相关算法的时候,你就会有非常明显的感觉,一般都会预留好空间。
  3. 一般数组较大的时候,建议将数组创建成全局数组,因为局部的数组太大的时候,可能会导致程序无法运行,刷题多了就⻅怪不怪了。全局变量(数组)是在内存的静态区开辟空间,但是局部的变量(数组)是在内存的栈区开辟空间的,每个程序的栈区空间是有限的,不会很大。
  1. 数组逆序重存放

  2. 向量点积计算

  3. 开关灯

  4. 小鱼比可爱

以上这些题可以自己练练手,后面会进行讲解。


结语

希望这篇文章能够让各位对一维数组 有着深入的了解,同时也要多练练题巩固巩固。下一篇将要讲解二维数组

同时愿诸君能一起共渡重重浪,终见缛彩遥分地,繁光远缀天

相关推荐
wjs20242 小时前
C++ 指针
开发语言
自然常数e2 小时前
深入理解指针(1)
c语言·算法·visual studio
WWZZ20252 小时前
快速上手大模型:深度学习13(文本预处理、语言模型、RNN、GRU、LSTM、seq2seq)
人工智能·深度学习·算法·语言模型·自然语言处理·大模型·具身智能
20岁30年经验的码农2 小时前
Java Sentinel流量控制与熔断降级框架详解
java·开发语言·sentinel
Christo33 小时前
AAAI-2024《Multi-Class Support Vector Machine with Maximizing Minimum Margin》
人工智能·算法·机器学习·支持向量机·数据挖掘
二川bro3 小时前
特征工程完全手册:2025 Python实战技巧
开发语言·python
p***h6433 小时前
JavaScript图像处理开发
开发语言·javascript·图像处理
SelectDB3 小时前
压缩率提升 48%,详解 Apache Doris 存储压缩优化之道|Deep Dive
数据库·开源·github
元亓亓亓4 小时前
LeetCode热题100--79. 单词搜索
算法·leetcode·职场和发展