leetcode第 317 场周赛
2455. 可被三整除的偶数的平均值
难度简单3收藏分享切换为英文接收动态反馈
给你一个由正整数组成的整数数组 nums
,返回其中可被 3
整除的所有偶数的平均值。
注意:n
个元素的平均值等于 n
个元素 求和 再除以 n
,结果 向下取整 到最接近的整数。
示例 1:
输入:nums = [1,3,6,10,12,15]
输出:9
解释:6 和 12 是可以被 3 整除的偶数。(6 + 12) / 2 = 9 。
示例 2:
输入:nums = [1,2,4,7,10]
输出:0
解释:不存在满足题目要求的整数,所以返回 0 。
提示:
-
1 <= nums.length <= 1000
-
1 <= nums[i] <= 1000
可被3整除的所有偶数其实就是可被6整除
class Solution {
public:
int averageValue(vector<int>& nums) {
int sum=0,n=0;
for(int i=0;i<nums.size();i++)
if(nums[i]%6==0)
{
sum+=nums[i];
n++;
}
return sum==0?0:sum/n;
}
};
2456. 最流行的视频创作者
难度中等15收藏分享切换为英文接收动态反馈
给你两个字符串数组 creators
和 ids
,和一个整数数组 views
,所有数组的长度都是 n
。平台上第 i
个视频者是 creator[i]
,视频分配的 id 是 ids[i]
,且播放量为 views[i]
。
视频创作者的 流行度 是该创作者的 所有 视频的播放量的 总和 。请找出流行度 最高 创作者以及该创作者播放量 最大 的视频的 id 。
- 如果存在多个创作者流行度都最高,则需要找出所有符合条件的创作者。
- 如果某个创作者存在多个播放量最高的视频,则只需要找出字典序最小的
id
。
返回一个二维字符串数组 answer
,其中 answer[i] = [creatori, idi]
表示 creatori
的流行度 最高 且其最流行的视频 id 是 idi
,可以按任何顺序返回该结果*。*
示例 1:
输入:creators = ["alice","bob","alice","chris"], ids = ["one","two","three","four"], views = [5,10,5,4]
输出:[["alice","one"],["bob","two"]]
解释:
alice 的流行度是 5 + 5 = 10 。
bob 的流行度是 10 。
chris 的流行度是 4 。
alice 和 bob 是流行度最高的创作者。
bob 播放量最高的视频 id 为 "two" 。
alice 播放量最高的视频 id 是 "one" 和 "three" 。由于 "one" 的字典序比 "three" 更小,所以结果中返回的 id 是 "one" 。
示例 2:
输入:creators = ["alice","alice","alice"], ids = ["a","b","c"], views = [1,2,2]
输出:[["alice","b"]]
解释:
id 为 "b" 和 "c" 的视频都满足播放量最高的条件。
由于 "b" 的字典序比 "c" 更小,所以结果中返回的 id 是 "b" 。
提示:
n == creators.length == ids.length == views.length
1 <= n <= 105
1 <= creators[i].length, ids[i].length <= 5
creators[i]
和ids[i]
仅由小写英文字母组成0 <= views[i] <= 105
这题当时做了好久最后还超时了,没有想到同时记录最大下标和总和
class Solution {
public:
vector<vector<string>> mostPopularCreator(vector<string>& creators, vector<string>& ids, vector<int>& views) {
int n = creators.size();
// 记录每个作者的播放量总和
unordered_map<string, long long> mp1;
// 记录每个作者播放量最大视频的下标
unordered_map<string, int> mp2;
for (int i = 0; i < n; i++) {
// 更新播放量总和
mp1[creators[i]] += views[i];
// 更新最大播放量下标
if (mp2.count(creators[i])) {
int &t = mp2[creators[i]];
if (views[i] > views[t] || (views[i] == views[t] && ids[i] < ids[t])) t = i;
}
else {
mp2[creators[i]] = i;
}
}
// 求最大播放量总和
long long mx = -1;
for (auto it = mp1.begin(); it != mp1.end(); it++) mx = max(mx, it->second);
// 计算答案
vector<vector<string>> ans;
for (auto it = mp1.begin(); it != mp1.end(); it++) if (it->second == mx) ans.push_back({it->first, ids[mp2[it->first]]});
return ans;
}
};
2457. 美丽整数的最小增量
难度中等19收藏分享切换为英文接收动态反馈
给你两个正整数 n
和 target
。
如果某个整数每一位上的数字相加小于或等于 target
,则认为这个整数是一个 美丽整数 。
找出并返回满足 n + x
是 美丽整数 的最小非负整数 x
。生成的输入保证总可以使 n
变成一个美丽整数。
示例 1:
输入:n = 16, target = 6
输出:4
解释:最初,n 是 16 ,且其每一位数字的和是 1 + 6 = 7 。在加 4 之后,n 变为 20 且每一位数字的和变成 2 + 0 = 2 。可以证明无法加上一个小于 4 的非负整数使 n 变成一个美丽整数。
示例 2:
输入:n = 467, target = 6
输出:33
解释:最初,n 是 467 ,且其每一位数字的和是 4 + 6 + 7 = 17 。在加 33 之后,n 变为 500 且每一位数字的和变成 5 + 0 + 0 = 5 。可以证明无法加上一个小于 33 的非负整数使 n 变成一个美丽整数。
示例 3:
输入:n = 1, target = 1
输出:0
解释:最初,n 是 1 ,且其每一位数字的和是 1 ,已经小于等于 target 。
提示:
1 <= n <= 1012
1 <= target <= 150
- 生成的输入保证总可以使
n
变成一个美丽整数。
得到当前数字的各位数字之和,如果<=target就直接返回0
当前值各位数之和大于了target,那么考虑进行进位,当前数大于,那么个位的数字往上变大更不可能满足条件
寻找最小值,末尾为0则最小 所以我们令此时的个位为0,十位进1,同理,如果计算后仍旧大于target,我们将十位变为0,百位进1
举个例子:123 3
123 和为6>3,那么124-129的各位数和不可能小于6,直到进位后130达到4才小于123的6
130 和为4>3 那么131-139也不可能小于4,也只有进位后200才小于130的4
200 和为2<3,所以200-123的差就是最小增量
当满足条件后,此时的值减去原值就是最小的数字
class Solution {
public:
long long makeIntegerBeautiful(long long n, int target) {
string str=to_string(n);
int len=str.size();
long long ten[64]={1};
for(int i=1;i<=len;i++)
ten[i]=ten[i-1]*10;
int sum=0;
for(int i=0;i<str.size();i++)
sum=sum+str[i]-'0';
if(sum<=target)
return 0;
long long num;
for(int i=1;i<=len;i++)
{
num=n/ten[i]*ten[i]+ten[i];
string s1=to_string(num);
int sum1=0;
for(int i=0;i<s1.size();i++)
sum1+=s1[i]-'0';
if(target>=sum1)
return num-n;
}
return 0;
}
};
2458. 移除子树后的二叉树高度
难度困难15收藏分享切换为英文接收动态反馈
给你一棵 二叉树 的根节点 root
,树中有 n
个节点。每个节点都可以被分配一个从 1
到 n
且互不相同的值。另给你一个长度为 m
的数组 queries
。
你必须在树上执行 m
个 独立 的查询,其中第 i
个查询你需要执行以下操作:
- 从树中 移除 以
queries[i]
的值作为根节点的子树。题目所用测试用例保证queries[i]
不 等于根节点的值。
返回一个长度为 m
的数组 answer
,其中 answer[i]
是执行第 i
个查询后树的高度。
注意:
- 查询之间是独立的,所以在每个查询执行后,树会回到其 初始 状态。
- 树的高度是从根到树中某个节点的 最长简单路径中的边数 。
示例 1:
输入:root = [1,3,4,2,null,6,5,null,null,null,null,null,7], queries = [4]
输出:[2]
解释:上图展示了从树中移除以 4 为根节点的子树。
树的高度是 2(路径为 1 -> 3 -> 2)。
示例 2:
输入:root = [5,8,9,2,1,3,7,4,6], queries = [3,2,4,8]
输出:[3,2,3,2]
解释:执行下述查询:
- 移除以 3 为根节点的子树。树的高度变为 3(路径为 5 -> 8 -> 2 -> 4)。
- 移除以 2 为根节点的子树。树的高度变为 2(路径为 5 -> 8 -> 1)。
- 移除以 4 为根节点的子树。树的高度变为 3(路径为 5 -> 8 -> 2 -> 6)。
- 移除以 8 为根节点的子树。树的高度变为 2(路径为 5 -> 9 -> 3)。
提示:
- 树中节点的数目是
n
2 <= n <= 105
1 <= Node.val <= n
- 树中的所有值 互不相同
m == queries.length
1 <= m <= min(n, 104)
1 <= queries[i] <= n
queries[i] != root.val
解法一:
首先从用一个最简单的 dfs 去求每一个点的 高度 出发来思考:
int depth[N];
void dfs(TreeNode* root, int height) {
depth[root->val] = height; // 因为每一个节点的值都不一样
if (root->left) dfs(root->left, height + 1);
if (root->right) dfs(root->right, height + 1);
}
我们想象对于某一个查询 ii,即我们要去除该节点及其子树。那么用 「先左后右」 这个方法来遍历到该节点时,由于它的子树是没有被遍历到的,所以如果我们用一个变量 max_heightmax_height 来表示遍历过程中的最大高度,那么当遍历到该节点时,如果其他节点(除了它本身和其子节点)都遍历过了,那么去掉该节点及其子树后的最大高度等于此时的 max_{height}max_height。
又因为这是「二叉树」,所以对于某一个节点来说,用「先左后右」 和 「先右后左」 两种遍历方式一定会有其中一种情况满足遍历到该节点时,其他节点(除了它本身和其子节点)都遍历过。
那么我们只用 「先左后右」 和 「先右后左」 两次遍历来记录遍历到当前节点的 max_heightmax_height 即可,最后去掉该节点后的整个树的最大高度就是两者中的较大值。
复杂度
时间复杂度: O(n),两次 dfs 的时间开销。
空间复杂度: O(n)。
const int N = 1e5 + 7;
class Solution {
public:
int mp1[N]; // 先左后右到节点 i 的最大值
int mp2[N]; // 先右后左到节点 i 的最大值
int max_height; // 标记最大值
void dfs1(TreeNode* root, int o) { // 先左后右
mp1[root->val] = max_height;
max_height = max(max_height, o);
if (root->left) dfs1(root->left, o + 1);
if (root->right) dfs1(root->right, o + 1);
}
void dfs2(TreeNode* root, int o) { // 先右后左
mp2[root->val] = max_height;
max_height = max(max_height, o);
if (root->right) dfs2(root->right, o + 1);
if (root->left) dfs2(root->left, o + 1);
}
vector<int> treeQueries(TreeNode* root, vector<int>& qs) {
max_height = 0, dfs1(root, 0);
max_height = 0, dfs2(root, 0);
vector<int> res;
for (auto& i : qs) {
res.push_back(max(mp1[i], mp2[i]));
}
return res;
}
};
解法二:
既然是求树的高度,我们可以先跑一遍 DFS,求出每棵子树的高度height
(这里定义成最长路径的节点数)。
然后再 DFS 一遍这棵树,同时维护当前节点深度 depth
(从 0 开始),以及删除当前子树后剩余部分的树的高度 restH
(这里定义成最长路径的边数)。
具体做法如下:
往左走,递归前算一下从根节点到当前节点右子树最深节点的长度,即 depth +height[node.right]
,与 restH
取最大值,然后往下递归;
往右走,递归前算一下从根节点到当前节点左子树最深节点的长度,即 depth +height[node.left]
,与 restH
取最大值,然后往下递归。
每个节点的答案即为递归到该节点时的 restH
值。
代码实现时可以直接把答案覆盖到queries
数组中。
灵神的代码:
class Solution {
public:
vector<int> treeQueries(TreeNode *root, vector<int> &queries) {
unordered_map<TreeNode*, int> height; // 每棵子树的高度
function<int(TreeNode*)> get_height = [&](TreeNode *node) -> int {
return node ? height[node] = 1 + max(get_height(node->left), get_height(node->right)) : 0;
};
get_height(root);
int res[height.size() + 1]; // 每个节点的答案
function<void(TreeNode*, int, int)> dfs = [&](TreeNode *node, int depth, int rest_h) {
if (node == nullptr) return;
++depth;
res[node->val] = rest_h;
dfs(node->left, depth, max(rest_h, depth + height[node->right]));
dfs(node->right, depth, max(rest_h, depth + height[node->left]));
};
dfs(root, -1, 0);
for (auto &q : queries) q = res[q];
return queries;
}
};