这里写目录标题
- 题目一:6220.可被三整除的偶数的平均值
- 题目二:6221. 最流行的视频创作者
- 题目三:6222. 美丽整数的最小增量
- 题目四:2458. 移除子树后的二叉树高度
题目一:6220.可被三整除的偶数的平均值
AC代码:
class Solution {
public:
int averageValue(vector<int>& nums) {
int sum=0;
int len=nums.size();
int n=0;
for(int i=0;i<len;i++)
{
if(nums[i]%3==0&&nums[i]%2==0)
{
sum+=nums[i];
n++;
}
}
if(n==0) return 0;
sum=sum/n;
return sum;
}
};
题目二:6221. 最流行的视频创作者
熟悉map和vector的使用和嵌套使用即可
注意常用的避免超时的方法:先将后面需要用到的信息存起来,避免不必要的遍历(创作者对应的视频)
AC代码:
class Solution {
public:
vector<vector<string>> mostPopularCreator(vector<string>& creators, vector<string>& ids, vector<int>& views) {
vector<vector<string>> ans;
int lenc=creators.size();
int leni=ids.size();
map<string,long long>tmp;
map<string,vector<int>>index; //将每个创作者的所有视频的编号存起来,便于后面对最大流行度作者进行处理的时候直接输出,就不用遍历所有的视频去匹配了,那样会超时
for(int i=0;i<lenc;i++)
{
tmp[creators[i]]+=views[i];
index[creators[i]].push_back(i);
}
long long max=0;
vector<string>maxName;
for(map<string,long long>::iterator it=tmp.begin();it!=tmp.end();it++)
{
if(it->second>max) max=it->second;
}
for(map<string,long long>::iterator it=tmp.begin();it!=tmp.end();it++)
{
if(it->second==max) maxName.push_back(it->first);
}
for(int i=0;i<maxName.size();i++)
{
string name=maxName[i];
string maxids="";
max=0;
int lenindex=index[name].size();
for(int k=0;k<lenindex;k++)
{
int j=index[name][k];
if(maxids=="") maxids=ids[j];
if(views[j]>max)
{
max=views[j];
maxids=ids[j];
}
else if(views[j]==max)
{
if(maxids>ids[j]) //选取字典序更小的
maxids=ids[j];
}
}
vector<string>t;
t.push_back(name);
t.push_back(maxids);
ans.push_back(t);
}
return ans;
}
};
题目三:6222. 美丽整数的最小增量
一开始用暴力试了试,虽然自己分析的也会超时,但是还是试了试,果然超时了,比如一个数为2*10^ 12,target为1,一个一个地加需要执行8 *10^12,肯定超时了
静下心来想一下,一个数的每个位数和加起来大于target了,只可能将低位的减小,不可能将高位的进行减小(右低左高),因为需要将数增大
要想加得数最少就能达到要求,肯定是从低位到高位开始逐渐减小,对于每一个低位只能直接减小到0,然后向高一位进位,不可能取小于该数的其他数字,因为要想减小必定会向高位进一位,进一位后最小的数就是取0
注意如果用数组来保存每一位是不太好的,涉及进位要不断向高位进位,不好操作,直接利用取余和除法运算来操作
class Solution {
public:
int getIndex(long long tmp,int index) //返回tmp的第index位数
{
long long t=pow(10,index-1);
if(t%10&&t!=1) t++; //这个地方有时候pow后得到的是99,不知道为什么
tmp=tmp/t;
tmp=tmp%10;
return tmp;
}
int getSum(long long tmp,int index) //得到tmp第index位到最高位的和
{
long long t=pow(10,index-1);
if(t%10&&t!=1) t++;
tmp=tmp/t; //剔除掉前面的
int sum=0;
while(tmp)
{
sum+=tmp%10;
tmp/=10;
}
return sum;
}
long long makeIntegerBeautiful(long long n, int target) {
unsigned long long tmp=n; //这个地方在后面进行加和的时候用long long会超出范围,直接改为unsigned long long
int index=1;
while(true)
{
int sum=getSum(tmp,index);
if(sum<=target) break;
int t=getIndex(tmp,index); //得到第index位
tmp-=t*pow(10,index-1); //将第index位置为0
tmp+=pow(10,index); //进位
index++; //处理下一位
}
tmp-=n;
return tmp;
}
};
题目四:2458. 移除子树后的二叉树高度
部分通过:
自己写的只能通过37 / 40 个通过测试用例,超时了,已经尽力了
思路就是,只有在每次删除某个子树的时候,对应子树的根节点是在重链上,也就是在删除了某些结点后会影响树的高度的对应的结点,重链上的结点也就是在计算树的高度的时候每次左子树和右子树中根节点值最大的子树的根节点(如果俩子树的高度相同,对应的根节点也不是重链上的结点),因此在删除的时候可以判断一下,如果删除的不是重链上的结点,就不会影响树原本的高度,如果删除的是重链上的结点,就需要重新进行计算
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
//求解树的高度过程中经过的那些结点的编号,只有这些结点被删除才会影响树的高度(这些结点经过的链路称为重链)
set<int>zhongNum; //存储重链上的结点
map<TreeNode*,int>NodeDeep; //每个结点作为根节点的树的高度
int deepAns[100005]; //重链上的结点被删掉后树的高度是多少
int getDeep(TreeNode* root)
{
if(root==nullptr) return 0;
int l=getDeep(root->left);
int r=getDeep(root->right);
if(l>r)
{
//把删去后影响树高度的结点存起来
zhongNum.insert(root->left->val);
}
else if(l<r)
{
zhongNum.insert(root->right->val);
}
NodeDeep[root]=max(l,r)+1; //保存每个结点最为根节点的子树的高度
return max(l,r)+1;
}
int dfs(TreeNode* root,int p)
{
if(root->val==p) return 0; //找到删除的子树的根节点(重链上的结点)就停止往下继续算
if(root->left&&zhongNum.count(root->left->val))
{
if(root->right) return max(dfs(root->left,p),NodeDeep[root->right])+1;
else return dfs(root->left,p)+1;
}
else if(root->right&&zhongNum.count(root->right->val))
{
if(root->left) return max(dfs(root->right,p),NodeDeep[root->left])+1;
else return dfs(root->right,p)+1;
}
else return NodeDeep[root];
}
//删掉重链上的结点后树的高度是多少
void solve(TreeNode* root)
{
for(set<int>::iterator it=zhongNum.begin();it!=zhongNum.end();it++)
{
int ans=dfs(root,(*it));
deepAns[(*it)]=ans-1;
}
}
vector<int> treeQueries(TreeNode* root, vector<int>& queries) {
int len=queries.size();
int maxDeep=getDeep(root)-1;
solve(root); //求树的高度,顺便求重链上的结点,顺便求每个结点作为根节点的子树的高度
for(int i=0;i<len;i++)
{
if(!zhongNum.count(queries[i])) //删除的不是重链上的结点,不会改变树的高度
{
queries[i]=maxDeep;
}
else queries[i]=deepAns[queries[i]]; //删除重链上的结点树的高度要重新dfs来求
}
return queries;
}
};
其实超时的原因就出在遍历每个重链上的结点,求将该结点删除后整棵树的高度上,外层遍历,内层又dfs,就超时了,想着把这个外层的遍历去掉
再想一下,可以从上往下沿着重链走的过程中就能求出将每个重结点删去后的树的高度,当到达一个重结点时,就表示将该重结点删掉,将该重结点删掉后树的高度为他父节点的到根节点的高度加上它父节点的另一个子树的高度和该树将它父节点删掉后的树的高度二者的最大值,因为是从上往下遍历的,因此每次都会先把该重结点的父节点(也是重结点)删除后的树的高度求出来,直接往下不断循环迭代就行。
AC代码:
class Solution {
public:
//求解树的高度过程中经过的那些结点的编号,只有这些结点被删除才会影响树的高度(这些结点经过的链路称为重链)
map<TreeNode*,TreeNode*>zhongNum; //存储每个结点的重儿子
map<TreeNode*,int>NodeDeep; //每个结点作为根节点的树的高度
int deepAns[100005]; //每个结点被删掉后树的高度是多少
int getDeep(TreeNode* root)
{
if(root==nullptr) return 0;
int l=getDeep(root->left);
int r=getDeep(root->right);
if(l>r)
{
//把删去后影响树高度的结点存起来
zhongNum[root]=root->left;
}
else if(l<r)
{
zhongNum[root]=root->right;
}
NodeDeep[root]=max(l,r)+1; //保存每个结点最为根节点的子树的高度
return max(l,r)+1;
}
//删掉重链上的结点后树的高度是多少,从根节点往下,遍历每个重链上的结点被删除后树的高度是多少
void solve(TreeNode* root)
{
TreeNode* now =zhongNum[root];
TreeNode* fa=root;
int path =-1,maxd=0;
while(now) //当还有重儿子,注意当两棵子树对应的高度都相同的时候该结点也是没有重儿子的
{
path++;
int other=0; //不是重儿子的另一个儿子作为子树的高度
if(fa->right&&now==fa->left) other=NodeDeep[fa->right];
else if(fa->left&&now ==fa->right) other=NodeDeep[fa->left];
maxd=max(maxd,other+path);
//cout<<now->val<<" "<<maxd<<endl;
deepAns[now->val]=maxd;
fa=now;
now=zhongNum[fa];
}
}
vector<int> treeQueries(TreeNode* root, vector<int>& queries) {
int len=queries.size();
int maxDeep=getDeep(root)-1;
for(int i=0;i<len;i++)
{
deepAns[queries[i]]=maxDeep; //先给所有的结点值的高度都赋值为不是重链上的结点删去后的高度,后面再更新重链上的结点
}
solve(root); //求重链上的结点删去后树的高度
for(int i=0;i<len;i++)
{
queries[i]=deepAns[queries[i]];
}
return queries;
}
};
严总的直接存树中结点值的方法:(因为每个结点是1-n并且不重复的数,这样会简单一些,用0表示左孩子,1表示右孩子) 而且时间复杂度减小了好多
const int N=100010;
class Solution {
public:
int son[N]; //存储每个结点的重儿子是左儿子(0)还是右儿子(1),如果没有重儿子即为(-1)
int tr[N][2]; //每个结点的左儿子存在tr[i][0],右儿子存在tr[i][1]
int NodeDeep[N]; //每个结点作为根节点的树的高度
int deepAns[N]; //每个结点被删掉后树的高度是多少
int getDeep(TreeNode* root)
{
if(root==nullptr) return 0;
int l=getDeep(root->left);
int r=getDeep(root->right);
int v=root->val;
if(l) tr[v][0]=root->left->val;
if(r) tr[v][1]=root->right->val;
if(l>r) son[v]=0; //重儿子是左儿子
else if(l<r) son[v]=1; //重儿子是右儿子
NodeDeep[root->val]=max(l,r)+1; //保存每个结点最为根节点的子树的高度
return max(l,r)+1;
}
//删掉重链上的结点后树的高度是多少,从根节点往下,遍历每个重链上的结点被删除后树的高度是多少
void solve(TreeNode* root)
{
int now =root->val; //now=0表示左儿子为重儿子,now=1表示右儿子为重儿子,now=-1表示没有重儿子
int path =-1,maxd=0;
while(now&&son[now]!=-1) //当还有重儿子
{
path++;
int k=son[now]; //得到重儿子
maxd=max(maxd,NodeDeep[tr[now][k ^ 1]]+path); //异或得到另一个儿子,如果当前儿子是左儿子0,异或后得到另一个儿子1
deepAns[tr[now][k]]=maxd;
now=tr[now][k]; //转移到重儿子上去
}
}
vector<int> treeQueries(TreeNode* root, vector<int>& queries) {
// memset(tr,0,sizeof tr); //注意这个数组放在类的外面,即全局作用域中是随机初始化的,要进行手动初始化,作为成员变量时会用默认0,null等初始化
// memset(son, -1,sizeof son);
int len=queries.size();
int maxDeep=getDeep(root)-1;
//先给所有的结点值的高度都赋值为不是重链上的结点删去后的高度,后面再更新重链上的结点
for(int i=0;i<len;i++) deepAns[queries[i]]=maxDeep;
solve(root); //求重链上的结点删去后树的高度
for(int i=0;i<len;i++)
{
queries[i]=deepAns[queries[i]];
}
return queries;
}
};