《LeetCode零基础指南》(第三讲) 一维数组

本文是《LeetCode零基础指南》的第三讲,主要介绍一维数组的相关概念,包括输入输出、刷题步骤、顺序存储、数组长度、数组索引等,并通过实例解析数组的查找、最小值、斐波那契数列等问题。

零、了解网站

1、输入输出

  对于算法而言,就是给定一些输入,得到一些输出,在一般的在线评测系统中,我们需要自己手写输入输出函数(例如 C语言中的scanfprintf),而在 LeetCode 这个平台,只需要实现它提供的函数即可。函数的传入参数就代表了的算法的输入,而函数的返回值则代表了的算法的输出。

2、刷题步骤

  找到一道题,以 两整数之和 为例,如何把这道题通过呢?如下图所示:


  第 1 步:阅读题目;
  第 2 步:参考示例;
  第 3 步:思考数据范围;
  第 4 步:根据题意,实现函数的功能;
  第 5 步:本地数据测试;
  第 6 步:提交;


3、尝试编码

  这个题目比较简单,就是求两个数的和,我们可以在编码区域尝试敲下这么一段代码。

int getSum(int a, int b){  // (1)
    return a + b;          // (2)
}
  • ( 1 ) (1) (1) 这里int是C/C++中的一种类型,代表整数,即 Integer,传入参数是两个整数;
  • ( 2 ) (2) (2) 题目要求返回两个整数的和,我们用加法运算符实现两个整数的加法;

4、调试提交


  第 1 步:实现加法,将值返回;
  第 2 步:执行代码,进行测试数据的调试;
  第 3 步:代码的实际输出;
  第 4 步:期望的输出,如果两者符合,则代表这一组数据通过(但是不代表所有数据都能通过哦);
  第 5 步:尝试提交代码;
  第 6 步:提交通过,撒花 🌻🌼🌺🌷🥀;


  这样,我们就大致知道了一个刷题的步骤了。上一章我们了解了循环,今天我们就来学习一下数组,从一维数组开始学习。

一、概念定义

1、顺序存储

  顺序存储结构,是指用一段地址连续的存储单元来依次存储数据。如图所示,每个蓝色方块都对应了数组中的一个数据。数据有类型,例如:32位整型int、单精度浮点型float、双精度浮点型double、字符型char,64位整型long long、16位短整型short等等。

2、存储方式

  在编程语言中,用一维数组来实现顺序存储结构,在C语言中,把第一个数据元素存储到下标为 0 的位置中,把第 2 个数据元素存储到下标为 1 的位置中,以此类推。
  C语言中,数组的定义如下:

int a[7];

  我们也可以将数组元素进行初始化,如下:

int a[7] = {5, 2, 0, 1, 3, 1, 4};

  由于编译器能够通过后面的初始化列表知道到底有多少个数据元素,所以中括号中的数字可以省略,如下:

int a[] = {5, 2, 0, 1, 3, 1, 4};

  当然,我们还可以定义一个大数组,但是只初始化其中几个元素:

int a[18] = {5, 2, 0, 1, 3, 1, 4};

3、长度和容量

  数组的长度指的是数组当前有多少个元素,数组的容量指的是数组最大能够存放多少个元素。如果数组元素大于最大能存储的范围,在程序上是不允许的,可能会产生意想不到的问题,实现上是需要规避的。

  如上图所示,数组的长度为 5,即红色部分;容量为 8,即红色 加 蓝色部分。例如下面这个数组:

int a[8] = {9, 8, 7, 6, 5};

4、数组的索引

  数组中的元素索引,我们可以采用[]运算符来完成,例如:

int a[7] = {5, 2, 0, 1, 3, 1, 4};
int b = a[0];    // 5 
int c = a[6];    // 4
int d = a[7];    // error

  这段代码中,a[0]表示的是数组的第一个元素,a[6]表示的数组的最后一个元素。
  而a[7]则是非法的,原因是数组元素一个 7 个,而这个调用,索引到了第 8 个元素,如果程序这样调用,就可能引起位置的错误。这种错误调用,我们一般称它为:数组下标越界。

5、数组的函数传参

  在学习函数的时候,我们知道,如果想将两个整型变量传递给函数,通过函数返回两个整型变量的和,我们可以写出如下代码:

int add(int a, int b) {
    return a + b;
}

  那么,如果我们要求的是一个长度为 10 的数组中所有元素的和,我们如何通过函数来实现呢?我们学过循环 和 函数,并且套用上面的格式,传递参数变成一个数组,如下:

int add(int nums[10]) {
    int i;
    int s = 0;
    for(i = 0; i < 10; ++i) {
    	s += nums[i];
    }
    return s;
}

  我们利用一个循环,把数组中所有的元素取出来,然后累加到变量 s s s 上,最后返回 s s s 的值,那么问题来了,如果没有定义数组的长度,我们如何完成这件事情呢?
  对于计算机来说,它其实并不知道你的数组有多少个元素,因为这是你自己定义的,所以你需要告诉这个函数,所以,我们只需要在传参中再添加一个参数,代表数组的长度即可,代码实现如下:

int add(int nums[10], int numsSize) {
    int i;
    int s = 0;
    for(i = 0; i < numsSize; ++i) {
    	s += nums[i];
    }
    return s;
}

  以上代码中,我们用 numsSize来代表这个数组中有多少个元素,然后通过参数的形式传递给这个函数,那么,函数在计算的时候就可以取变量numsSize的值作为循环上限来进行累加了。
  并且,我们还可以把参数列表中的数字 10 去掉,如下:

int add(int nums[], int numsSize) {
    // ...
}

  在力扣这个平台,传参一般会写成如下形式:

int add(int *nums, int numsSize) {
    // ...
}

  这里的int *numsint nums[]是等价的,其中*表示指针,好了,指针是下一章的内容,今天数组相关的内容就介绍到这里了,你只需要理解两者是等价的,今天的题就可以做了。

二、题目分析

1、数组的查找

  实现一个函数,在一个整型数组中找一个整数,返回它的数组下标,如果不存在则返回 -1

int search(int* nums, int numsSize, int target){  // (1)
    int i;
    for(i = 0; i < numsSize; ++i) {               // (2)
        if(nums[i] == target) {
            return i;                             // (3)
        }
    }
    return -1;                                    // (4)
}
  • ( 1 ) (1) (1) 传入参数int * nums代表一个整型数组,int numsSize代表数组长度,target则表示要查找的数;
  • ( 2 ) (2) (2) 遍历数组的每个元素;
  • ( 3 ) (3) (3) 如果找到某个元素和传入参数target相等,则直接返回下标i
  • ( 4 ) (4) (4) 如果一直没找到,则返回 − 1 -1 1

2、数组的最小值

  实现一个函数,在一个整型数组中寻找最小值并返回,数组元素值都不超过 100000。

int findMin(int* nums, int numsSize){
    int i, min = 100000;              // (1)
    for(i = 0; i < numsSize; ++i) {   // (2)
        if(nums[i] < min) {           // (3)
            min = nums[i];
        }
    }
    return min;
}
  • ( 1 ) (1) (1) 定义一个全局最小值;
  • ( 2 ) (2) (2) 遍历数组所有元素;
  • ( 3 ) (3) (3) 如果数组元素nums[i]比全局最小值小,则更新全局最小值。

3、斐波那契数列

  假设你正在爬楼梯。需要 n n n 阶你才能到达楼顶。每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

int f[1000];                      // (1)
int climbStairs(int n){
    f[0] = f[1] = 1;              // (2)
    for(int i = 2; i <= n; ++i) {
        f[i] = f[i-1] + f[i-2];   // (3)
    }
    return f[n];                  // (4)
}
  • ( 1 ) (1) (1) 定义一个全局的辅助数组;
  • ( 2 ) (2) (2) 初始化方案为 1;
  • ( 3 ) (3) (3) 你可以从 i − 1 i-1 i1 层爬过来,也可以从 i − 2 i-2 i2 层爬过来,所以两个方案数加和即可;
  • ( 4 ) (4) (4) 返回到达第 n n n 层的方案数;

4、绝对值为 k 的数对

  给你一个整数数组nums和一个整数 k k k ,请你返回数对 ( i , j ) (i, j) (i,j) 的数目,满足 i < j i < j i<j|nums[i] - nums[j]| == k

int countKDifference(int* nums, int numsSize, int k){
    int i, j, ans = 0;
    for(i = 0; i < numsSize; ++i) {             // (1)
        for(j = i + 1; j < numsSize; ++j) {     // (2)
            if( abs(nums[i] - nums[j]) == k )   // (3)
                ++ans;                          
        }
    }
    return ans;                                 // (4)
}
  • ( 1 ) (1) (1) 先枚举一个 i i i
  • ( 2 ) (2) (2) 再枚举一个 j j j
  • ( 3 ) (3) (3) 然后计算两个数组元素的差的绝对值,判断是否等于 k k k,如果等于,计数器加一;
  • ( 4 ) (4) (4) 最后返回计数器;

5、猜数字

  小A 和 小B 在玩猜数字。小B 每次从 1, 2, 3 中随机选择一个,小A 每次也从 1, 2, 3 中选择一个猜。他们一共进行三次这个游戏,请返回 小A 猜对了几次?输入的 guess数组为 小A 每次的猜测,answer数组为 小B 每次的选择。guess和answer的长度都等于3。

int game(int* guess, int guessSize, int* answer, int answerSize){
    int i;
    int ans = 0;
    for(i = 0; i < 3; ++i) {
        ans += (guess[i] == answer[i]) ? 1 : 0;   // (1)
    }
    return ans;
}
  • ( 1 ) (1) (1) 分别取两个数组中对应的元素进行判断,如果相等则计数器加一,否则加零(即不加);

6、拿硬币

  桌上有 n n n 堆力扣币,每堆的数量保存在数组coins中。我们每次可以选择任意一堆,拿走其中的一枚或者两枚,求拿完所有力扣币的最少次数。

int minCount(int* coins, int coinsSize){
    int i;
    int ans = 0;
    for(i = 0; i < coinsSize; ++i) {
        ans += (coins[i]+1)/2;   // (1)
    }
    return ans;
}
  • ( 1 ) (1) (1) 这个问题其实就是奇数 和 偶数 的情况分别处理即可,考虑一个数 x x x,如果是奇数,就要返回 x + 1 2 \frac {x+1}{2} 2x+1;如果是偶数,则返回 x 2 \frac {x}{2} 2x。当然也可以合并,利用 C语言整数除法是取下整的性质,可以都用 x + 1 2 \frac {x+1} 2 2x+1

三、推荐学习

🌳《画解数据结构》🌳

(1-1) 顺序表

四、课后习题

1、必做

序号题目链接难度
1爬楼梯★☆☆☆☆
2斐波那契数★☆☆☆☆
3第 N 个泰波那契数★☆☆☆☆
4差的绝对值为 K 的数对数目★☆☆☆☆
5猜数字★☆☆☆☆
6拿硬币★☆☆☆☆

2、选做

序号题目链接难度
1搜索旋转排序数组★☆☆☆☆
2搜索旋转排序数组 II★☆☆☆☆
3寻找旋转排序数组中的最小值★☆☆☆☆
4山峰数组的顶部★★☆☆☆
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

英雄哪里出来

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值