第一题
要求
给你一个大小为 m * n 的方阵 mat,方阵由若干军人和平民组成,分别用 0 和 1 表示。
请你返回方阵中战斗力最弱的 k 行的索引,按从最弱到最强排序。
如果第 i 行的军人数量少于第 j 行,或者两行军人数量相同但 i 小于 j,那么我们认为第 i 行的战斗力比第 j 行弱。
军人 总是 排在一行中的靠前位置,也就是说 1 总是出现在 0 之前。
示例
示例 1:
输入:mat =
[[1,1,0,0,0],
[1,1,1,1,0],
[1,0,0,0,0],
[1,1,0,0,0],
[1,1,1,1,1]],
k = 3
输出:[2,0,3]
解释:
每行中的军人数目:
行 0 -> 2
行 1 -> 4
行 2 -> 1
行 3 -> 2
行 4 -> 5
从最弱到最强对这些行排序后得到 [2,0,3,1,4]
示例 2:
输入:mat =
[[1,0,0,0],
[1,1,1,1],
[1,0,0,0],
[1,0,0,0]],
k = 2
输出:[0,2]
解释:
每行中的军人数目:
行 0 -> 1
行 1 -> 4
行 2 -> 1
行 3 -> 1
从最弱到最强对这些行排序后得到 [0,2,3,1]
提示:
m == mat.length
n == mat[i].length
2 <= n, m <= 100
1 <= k <= m
matrix[i][j] 不是 0 就是 1
代码
-
二分法
vector<int> kWeakestRows(vector<vector<int>>& mat, int k) {
int m = mat.size();
int n = mat[0].size();
vector<pair<int,int>> res;
vector<int> ret;
for(int i = 0;i<m;i++)
{
int left = 0,right = n-1;
while(left<right)
{
int mid = (left + right)/2;
if(mat[i][mid]==1) left = mid + 1;
else right = mid - 1;
}
if(mat[i][left]==0) left = left-1;
res.push_back({left,i});
}
sort(res.begin(),res.end());
for(int i =0;i<k;i++) ret.push_back(res[i].second);
return ret;
}
-
从前排查找(按列遍历)
vector<int> kWeakestRows(vector<vector<int>>& mat, int k) {
int m = mat.size(),n = mat[0].size(),flag = 0;
vector<int> res;
for(int i = 0;i<n;i++)
{
for(int j = 0;j<m;j++)
{
if(mat[j][i]==0)
if(find(res.begin(),res.end(),j)==res.end())
res.push_back(j);
if(res.size()==k) return res;
}
}
for(int i = 0; i < m; i++)
{
if(mat[i][n - 1] == 1)
{
res.push_back(i);
}
if(k == res.size()) break;
}
return res;
}
- 从前排查找相当于先按列遍历,第一个遇到0的肯定是士兵数最少的,注意已经满足要求的就不能在算了,要去重。还有一点是遍历完并没有取完,说明有全都是士兵的列,这时,从小序号往大序号直接添加之前没有取到的行。
第二题
要求
给你一个整数数组 arr。你可以从中选出一个整数集合,并删除这些整数在数组中的每次出现。
返回 至少 能删除数组中的一半整数的整数集合的最小大小。
示例
示例 1:
输入:arr = [3,3,3,3,5,5,5,2,2,7]
输出:2
解释:选择 {3,7} 使得结果数组为 [5,5,5,2,2]、长度为 5(原数组长度的一半)。
大小为 2 的可行集合有 {3,5},{3,2},{5,2}。
选择 {2,7} 是不可行的,它的结果数组为 [3,3,3,3,5,5,5],新数组长度大于原数组的二分之一。
示例 2:
输入:arr = [7,7,7,7,7,7]
输出:1
解释:我们只能选择集合 {7},结果数组为空。
示例 3:
输入:arr = [1,9]
输出:1
代码
int minSetSize(vector<int>& arr) {
unordered_map<int,int> mp;
for(int i = 0;i<arr.size();i++)
mp[arr[i]]++;
vector<int> temp;
for(unordered_map<int,int>::iterator it=mp.begin();it!=mp.end();it++)
temp.push_back(it->second);
sort(temp.begin(),temp.end());
int sum = 0,res = 0;
for(int i = temp.size()-1;i>=0;i--)
{
sum += temp[i];
res++;
if(sum>=(arr.size()/2)) break;
}
return res;
}
- 用哈希表储存每个元素储存的次数,因为题目要求至少删除一半,所以将每个元素出现次数按大小排序,从大往小的试,当满足大于容器一半大时,返回结果。
第三题
要求
给你一棵二叉树,它的根为 root 。请你删除 1 条边,使二叉树分裂成两棵子树,且它们子树和的乘积尽可能大。
由于答案可能会很大,请你将结果对 10^9 + 7 取模后再返回。
示例
输入:root = [1,2,3,4,5,6]
输出:110
解释:删除红色的边,得到 2 棵子树,和分别为 11 和 10 。它们的乘积是 110 (11*10)
代码
vector<long> ret;
int maxProduct(TreeNode* root) {
long sum = getcount(root),res = INT_MIN;
for(int i = 0;i<ret.size();i++)
{
long x = ret[i]*(sum - ret[i]);
res = max(res,x);
}
return res%(1000000007);
}
int getcount(TreeNode *root) //用来求以每个节点为根节点其和。
{
if(root==nullptr) return 0;
int temp = root->val + getcount(root->left) + getcount(root->right);
ret.push_back(temp);
return temp;
}
- 此题主要求了一个二叉树所有节点之和这个知识点,其他的较好理解。