LeetCode--2023Q4
打卡10月LeetCode
2023.10.10
128.最长连续序列
解法
基本思路:哈希表
要求时间复杂度为O(n),那就不能排序后再找。并且肯定需要遍历一遍数组,因此判断的时间复杂度必须是O(1)。
那么就想到了哈希表,用哈希表存储数组中的不重复元素,遍历哈希表中的元素,每个元素先检查-1是否存在,不存在说明这是连续序列的第一个元素,进入下一步。之后循环判断+1的元素是否存在,存在则计数累加,对每个数产生的累加结果取最大值即可。
Code
class Solution {
public:
int longestConsecutive(vector<int>& nums) {
unordered_set<int> st;
for(int n : nums)
st.insert(n);
int res = 0;
for(const int& n : st) {
if(!st.count(n-1)) {
int cur = n, cnt = 1;
while(st.count(cur+1)) {
++cur;
++cnt;
}
res = max(res, cnt);
}
}
return res;
}
};
560.和为K的子数组
解法
基本思路:哈希表、前缀和
和为k的子数组[i, j],可以由前缀和pre[j] - pre[i-1]计算得到,并且可以发现每一个前缀和都只和前一个前缀和有关,因此可以直接使用一个变量sum累加。同时可得 pre[i-1] = pre[j] - k。
因此可以维护一个哈希表,存储每一个前缀和出现的次数(初始化需要记录前缀和为0的次数为1,因为可能存在子数组是从第一个元素开始,此时公式需要支持找到key为0对应的值)。之后的遍历只需要寻找键 pre[j] - k,即可得到之前 pre[i-1] 的出现次数(因为元素可能为负数,因此某个前缀和可能出现多次),累加这个次数。
Code
class Solution {
public:
int subarraySum(vector<int>& nums, int k) {
unordered_map<int, int> hash;
hash[0] = 1;
int res = 0, sum = 0;
for(int n : nums) {
sum += n;
if(hash.count(sum - k))
res += hash[sum - k];
hash[sum]++;
}
return res;
}
};
238.除自身以外数组的乘积
解法
基本思路:前缀和
其实是前缀积。除自身以外的乘积,可以分为自身左边的乘积 x 自身右边的乘积,用相同的方式计算。
先算左边的乘积,就是前缀积,累加相乘,注意 res[i] 保存的是[0, i-1]的乘积,所以先保存再相乘,最左边的元素左边没有元素,所以相当于 x 1不动,因此 res[0] = 1。同理之后从后往前遍历,每个元素从1开始,先乘保存,再累乘,得到的就是正确数组。
Code
class Solution {
public:
vector<int> productExceptSelf(vector<int>& nums) {
vector<int> res(nums.size());
int fac = 1;
for(int i = 0; i < nums.size(); ++i) {
res[i] = fac;
fac *= nums[i];
}
fac = 1;
for(int i = nums.size()-1; i >= 0; --i) {
res[i] *= fac;
fac *= nums[i];
}
return res;
}
};
2023.10.21
2316.统计无向图中无法到达点对数
解法
基本思路:并查集
在最基础的并查集基础上,增加数组统计每一个连通分量中的节点个数。可以发现所求结果,对一个节点来说无法到达即为该节点所在连通分量外的节点个数。因此遍历所有节点,求无法到达节点的和,是实际结果的两倍(求的是对数,因此要除以2)。
Code
class Solution {
public:
long long countPairs(int n, vector<vector<int>>& edges) {
vector<int> fa(n);
vector<int> sz(n, 1);
iota(fa.begin(), fa.end(), 0);
function<int(int)> find = [&](int u) {
return u == fa[u] ? u : fa[u] = find(fa[u]);
};
auto join = [&](int u, int v) {
u = find(u);
v = find(v);
if(u == v) return;
if(sz[u] > sz[v]) {
fa[v] = u;
sz[u] += sz[v];
}
else {
fa[u] = v;
sz[v] += sz[u];
}
};
for(auto& vec : edges)
join(vec[0], vec[1]);
long long res = 0;
for(int i = 0; i < n; ++i)
res += n - sz[find(i)];
return res / 2;
}
};
2023.10.22
1402.做菜顺序
解法
基本思路:贪心、前缀和
由示例可知,要想得到最大值,满意度最高的菜就要最后求和,因此可以先对数组进行降序排序。
假设排序后数组前三个值为a、b、c,则如果只取第一个,结果为a,取前两个,结果为 2a + b = a + (a+b),取前三,结果a + (a+b) + (a+b+c)。。。由此可知,每多取一个就是加上当前的前缀和,并且只要前缀和大于0,就可以一直往后取(贪心)。
Code
class Solution {
public:
int maxSatisfaction(vector<int>& satisfaction) {
sort(satisfaction.rbegin(), satisfaction.rend());
int pre = 0, sum = 0;
for(int n : satisfaction) {
pre += n;
if(pre <= 0) break;
sum += pre;
}
return sum;
}
};
2023.10.23
2678.老人的数目
解法
基本思路:字符串、数组
简单题。
Code
class Solution {
public:
int countSeniors(vector<string>& details) {
int res = 0;
for(const string& str : details)
if(str[11] > '6' || (str[11] == '6' && str[12] > '0') )
++res;
return res;
}
};
2023.11.2
2103.环和杆
解法
基本思路:哈希表、位运算
简单题。一眼哈希,但哈希的value要存RGB三种,可以用位数组存储。
Code
class Solution {
public:
int countPoints(string rings) {
unsigned char hash[10] = {0};
for(int i = 0; i < rings.size(); i += 2) {
unsigned char mask;
if(rings[i] == 'R') mask = 1;
else if(rings[i] == 'G') mask = 2;
else mask = 4;
hash[rings[i+1] - '0'] |= mask;
}
int res = 0;
for(int i = 0; i < 10; ++i)
if(hash[i] == 7)
++res;
return res;
}
};