前言
前言:刷「数组」高频面试题。
文章目录
一、基础回顾
详细介绍参考 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) 算法思路
- 定义:两个指针
i和j分别指向两个数组的末尾元素; - 移动:比较
i和j对应元素的大小,哪个元素大就放到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)
i和j两个指针用于从后往前遍历两个数组; - (2)
k为结果数组的下标; - (3)从后往前遍历,防止
i和j越界; - (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) 算法思路
- 有序数组:意味着相同元素是相邻的;
- 明确一个数什么时候要:当前数与前一个数不同时(所有第一次出现的数),要当前数;
- 边界:第一个数必然要,第一个数没有前一个数,需加判断条件,避免数组越界;
- 定义两个指针
i和k,i用来遍历整个数组,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)。
本文先回顾数组基础,介绍其形式、定义、特点及增删改查操作。随后列举LeetCode上的高频面试题,如合并两个有序数组、删除有序数组中的重复项等,详细阐述各题的算法思路、源码剖析及时间复杂度,各题时间复杂度均为O(n)。
1万+

被折叠的 条评论
为什么被折叠?



