04 |「数组」必刷题

本文先回顾数组基础,介绍其形式、定义、特点及增删改查操作。随后列举LeetCode上的高频面试题,如合并两个有序数组、删除有序数组中的重复项等,详细阐述各题的算法思路、源码剖析及时间复杂度,各题时间复杂度均为O(n)。

前言

前言:刷「数组」高频面试题。

一、基础回顾

详细介绍参考 03 讲: 数组简析 (点击链接直达)

1. 形式

[ 1 , 2 , 3 , 4 , 5 ] [1,2,3,4,5] [1,2,3,4,5],数组类比排队,可以根据下标(排队的编号)查找某人。

2. 定义

int a[100];

3. 特点

支持随机访问,根据索引进行寻址
给一个索引 i ,可以在 O ( 1 ) O(1) O(1) 的时间拿到 a[i] 这个值。

4. 增删改查

查找:根据下标找到对应的元素,随机访问的时间复杂度是 O(1)
插入/删除:插入元素和删除元素的时间复杂度都是 O(n)


二. 高频面试题

1. 例题

例题1:LeetCode 88 合并两个有序数组
1)题目链接

原题链接:合并两个有序数组(点击链接直达)

2) 算法思路
  • 定义:两个指针 ij 分别指向两个数组的末尾元素;
  • 移动:比较 ij 对应元素的大小,哪个元素大就放到 nums1 中;
  • 注意
    • 不采用从前往后遍历比较,将数小的放到 nums1 中 。因为可能 nums2 中前几个数都比 nums1 中的第一个数小,会造成结果覆盖 nums1 中待比较的元素,故采用从后往前遍历;
    • 每次访问数组元素前,要判断下标是否有意义,避免数组越界;
    • 两指针分别移动,终有一个先停止,另一个后停止;
3)源码剖析
class Solution {
public:
    void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
        int i = m - 1, j = n - 1;									//(1)
        int k = m + n - 1;											//(2)

        while (i >= 0 && j >= 0)									//(3)
        {
            if (nums1[i] >= nums2[j]) nums1[k --] = nums1[i --];	//(4)
            else nums1[k --] = nums2[j --];							//(5)
        }
        while (j >= 0) nums1[k --] = nums2[j --];					//(6)
        //存放nums[2]剩余元素
    }
};
  • (1)ij 两个指针用于从后往前遍历两个数组;
  • (2)k 为结果数组的下标;
  • (3)从后往前遍历,防止 ij 越界;
  • (4)nums[i] 对应的元素大,存放到结果数组 nums1 中;
  • (5)nums[j] 对应的元素大,存放到结果数组 nums1 中;
  • (6)如果 nums2 没有存完(此时 nums1 所有元素存放完毕),直接将剩余 nums[2] 中的元素放到 nums1 中即可;因为 nums1 中的第一个元素比当前 j 指向的 nums2 中的元素大,由于初始数组有序,则剩余 nums2 中的元素都比 nums1 数组的第一个元素小;nums1 没有存完不用管,此时元素已经在正确位置;
4)时间复杂度

        数组中每个元素只会遍历一次,时间复杂度为 O ( n ) O(n) O(n)


例题2:LeetCode 26. 删除有序数组中的重复项
1)题目链接

原题链接:删除有序数组中的重复项(点击链接直达)

2) 算法思路
  • 有序数组:意味着相同元素是相邻的;
  • 明确一个数什么时候要:当前数与前一个数不同时(所有第一次出现的数),要当前数;
  • 边界:第一个数必然要,第一个数没有前一个数,需加判断条件,避免数组越界;
  • 定义两个指针 iki 用来遍历整个数组,k 存下所有第一次出现的数;
3)源码剖析
class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        int n = nums.size();					//(1)	
        int k = 0;								//(2)
        for (int i = 0; i < n; i ++)			//(3)
        {
            if (!i || nums[i] != nums[i - 1]) 	//(4)
            	nums[k ++] = nums[i];			//(5)
        }
        return k;								//(6)
    }
};
  • (1)n 为整个数组的长度;
  • (2)k 用来存所有第一次出现的数;
  • (3)i 用来遍历整个数组;
  • (4)当前数与前一个数不同时,将当前数放入 k
  • (5)将当前数存入结果数组;
  • (6)返回删除后数组的新长度 k
4)时间复杂度

         O ( n ) O(n) O(n)


例题3:LeetCode 80. 删除有序数组中的重复项 II
1)题目链接

原题链接:删除有序数组中的重复项 II(点击链接直达)

2) 算法思路
  • 目的:将重复数只保留两个;
  • 做法:从前往后遍历数组,对于当前遍历的数 a[i] 判断是否有效,如果有效存入有效数组中,无效则跳过;
  • 判断有效:如果当前判断数 a[i] 在有效数组中出现次数小于 2 证明 a[i] 有效,插入到有效数组中,否则无效跳过;
  • 判断出现次数:因为初始数组有序,所以有效数组也有序,则当前判断数 a[i] 是目前有效数组中的最大值;
  • 插入有效数组:判断有效数组末尾两个数是否与当前判断数 a[i] 相等,如果两个都相等则跳过当前判断数,否则将当前判断数插入到有效数组中;
3)源码剖析
class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        int n = nums.size();										//(1)	
        int k = 0;													//(2)
      	for (int i = 0; i < n; i ++)								//(3)
      	{
       		if (k < 2 || 
       		(nums[k - 1] != nums[i] || nums[k - 2] != nums[i]))		//(4)
          		nums[k ++] = nums[i];								//(5)
      }
        return k;													//(6)
    }
};
  • (1)n 为当前数组的元素个数;
  • (2)k 为有效数组的元素个数;
  • (3)遍历数组中的每个元素;
  • (4)判断有效数组末尾两个数是否与当前判断数 a[i] 相等,注意边界条件的判断;
  • (5)将当前数存到有效数组中;
  • (6)返回删除后数组的新长度;
4)时间复杂度

         O ( n ) O(n) O(n)


例题4:LeetCode 283. 移动 0
1)题目链接

原题链接:移动 0(点击链接直达)

2) 算法思路
  • 明确一个数什么时候要:遍历整个数组,当前数不为 0 时要,存入结果数组;
  • 最后再将结果数组后面的数添加 0
3)源码剖析
class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        int n = nums.size();
        int k = 0;
        for (int i = 0; i < n; i ++)
        {
            if (nums[i] != 0) nums[k ++] = nums[i];
        }
        for (int i = k; i < n; i ++) nums[i] = 0;
    }
};
4)时间复杂度

         O ( n ) O(n) O(n)

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

一个写代码的修车工

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

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

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

打赏作者

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

抵扣说明:

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

余额充值