今天记录了两道题,难度范围:★~★★。想到解决办法都不困难,想到奇招挺困难的。
一.找到所有数组中消失的数字 ★★☆☆☆
题目
448. 找到所有数组中消失的数字 给你一个含 n 个整数的数组 nums ,其中 nums[i] 在区间 [1, n] 内。请你找出所有在 [1, n] 范围内但没有出现在 nums 中的数字,并以数组的形式返回结果。
思路1
将1~n先存入结果数组,然后遍历nums数组,将nums存在的元素移除res,最后剩下的就是消失的数字。
说明:
auto it=find(res.begin(),res.end(),nums[i]);------查找nums[i]在res中的迭代器,然后从res在擦除对应元素
代码(未通过所有测试用例)
cpp
class Solution {
public:
vector<int> findDisappearedNumbers(vector<int>& nums) {
int n=nums.size();
vector<int> res;
//先将1~n存入res
//然后遍历数组移除在数组中的元素
for(int i=1;i<=n;i++){
res.push_back(i);
}
for(int i=0;i<n;i++){
auto it=find(res.begin(),res.end(),nums[i]);
res.erase(it);
}
return res;
}
};
代码运行时间太长,无法通过所有测试用例
思路2
利用哈希集合记录下数组的元素,然后在哈希集合中寻找1~n的数字,未找到的数字加入结果数组,最后结果数组中保存的就是答案。
代码2
cpp
class Solution {
public:
vector<int> findDisappearedNumbers(vector<int>& nums) {
int n=nums.size();
vector<int> res;
unordered_set<int> s;
//先将1~n存入res
//然后遍历数组移除在数组中的元素
for(int i=0;i<n;i++){
s.insert(nums[i]);
}
for(int i=1;i<=n;i++){
if(s.find(i)==s.end()){
res.push_back(i);
}
}
return res;
}
};
复杂度
时间复杂度:O(n)。遍历数组并插入哈希集合+遍历并查找集合→O(n)+O(n)=O(n)
空间复杂度:O(n)。需要额外的 O (n) 空间存储哈希集合
官方题解------原地修改 ★★★★☆
我标的四星难度,主要是因为个人觉得这个做法很难想到。
一般的思路就是利用哈希表记录数字,也可以创建一个数组代替哈希表,而数组nums的长度刚好是n,所以可以用nums充当哈希表。
数字范围是 1~n,可以利用此范围外的数字表示"是否存在",具体做法:遍历数组nums,每遇到一个数字x,就让nums[x-1]+n。因为nums数组中的所有数都在要求的数字范围内,所以这些数之后一定大于n,最后再次遍历nums数组,如果有nums[i]不大于n,说明没有遇到对应的数字 i+1,此数字就属于消失的数字。
注意点:遍历数组时,存在该数已经增加过n的情况,所以需要对n取模还原出本来的值。
代码
cpp
class Solution {
public:
vector<int> findDisappearedNumbers(vector<int>& nums) {
int n=nums.size();
for(auto & num:nums){
int x=(num-1)%n;
if(nums[x]<=n){
nums[x]+=n;
}
}
vector<int> res;
for(int i=0;i<n;i++){
if(nums[i]<=n){
res.push_back(i+1);
}
}
return res;
}
};
复杂度
时间复杂度:O(n)
空间复杂度:O(1)
二.分发饼干 ★☆☆☆☆
题目
假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。
对每个孩子 i,都有一个胃口值 g[i],这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j,都有一个尺寸 s[j] 。如果 s[j] >= g[i],我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是满足尽可能多的孩子,并输出这个最大数值。
思路
先对胃口值和饼干尺寸进行升序排序,使得用尽量合适的饼干尺寸满足胃口。
类似于双指针思想,创建变量 i 和 j 表示对应索引的胃口值和饼干尺寸。先通过移动 j 找到第一块满足 g[i] 的饼干,res满足的人数+1,然后移动 i 和 j,继续找饼干满足下一个人,重复操作。
代码
cpp
class Solution {
public:
int findContentChildren(vector<int>& g, vector<int>& s) {
//先排序
//在找
int gLen=g.size();
int sLen=s.size();
int res=0;
int i=0,j=0;
sort(g.begin(),g.end());
sort(s.begin(),s.end());
while(i<gLen && j<sLen){
//找到第一块满足的饼干
while(j<sLen && s[j]<g[i]){
j++;
}
if((i<gLen && j<sLen) && s[j]>=g[i]){
res++;
i++;
j++;
}
}
return res;
}
};
复杂度
m、n分别为数组g和s的长度
时间复杂度:O(nlogn+mlogm)。时间开销分为排序操作和循环操作。排序操作所用的时间复杂度O(mlogm+nlogn);循环操作主要是 i 和 j 的移动,总操作次数为 m+n,不存在循环操作,所以整体的时间复杂度为O(nlogn+mlogm+n+m)=O(nlogn+mlogm)。
空间复杂度:O(logn+logm)。辅助空间分为排序操作所需的辅助空间O(logn+logm)和局部变量的空间开销O(1),所以整体的空间复杂度为O(logn+logm)。
代码2
我的代码和官方题解的代码大差不差,但是评论区有一个回答看起来更简单点,来自:dp小亡子
直接将满足了的胃口值的个数作为答案,不断移动 j 的时候,能满足胃口值时,移动 i ,最后的 i 就是最多被满足的人数。
cpp
class Solution {
public:
int findContentChildren(vector<int>& g, vector<int>& s) {
int gLen=g.size();
int sLen=s.size();
int i=0,j=0;
sort(g.begin(),g.end());
sort(s.begin(),s.end());
while(i<gLen && j<sLen){
if(g[i]<=s[j]){
i++;
}
j++;
}
return i;
}
};
复杂度和之前的代码基本一致