wy的leetcode刷题记录_Day42
声明
本文章的所有题目信息都来源于leetcode
如有侵权请联系我删掉
805. 数组的均值分割
今天的每日一题是:805. 数组的均值分割
这道题也是蛮难的主要看这个题解吧:题解
题目介绍
给定你一个整数数组 nums
我们要将 nums 数组中的每个元素移动到 A 数组 或者 B 数组中,使得 A 数组和 B 数组不为空,并且 average(A) == average(B) 。
如果可以完成则返回true , 否则返回 false 。
注意:对于数组 arr , average(arr) 是 arr 的所有元素的和除以 arr 长度。
示例 1:
输入: nums = [1,2,3,4,5,6,7,8]
输出: true
解释: 我们可以将数组分割为 [1,4,5,8] 和 [2,3,6,7], 他们的平均值都是4.5。
示例 2:
输入: nums = [3,1]
输出: false
思路
阅读完题目,通过对俩个平均值的定义以及题目要求得出A和B以及全集的平均值应该相同,并且通过求出平均值的方法可能会引入浮点数,所以我们对所有的数先乘以n这样避免了算出浮点数,然后再将所有数减去平均值,得出这三个集合的均值应该为0。
我觉得先用暴力算法试一下,就是枚举所有可能的结果2的n次方个,虽然结果肯定是会超限的。通过题解发现这道题应该使用meet in middle来实现,简单来说就是将整个集合切成俩块,左边为A右边为B,有三种情况,在左边寻找和为0的数和在右边寻找和为0的数,如果出现了返回true,如果没有的话寻找在左边和的集合中和右边和的集合中是否存在相加为0的元素即可。
代码
class Solution {
public:
bool splitArraySameAverage(vector<int> &nums) {
int n = nums.size(), m = n / 2;
if (n == 1) {
return false;
}
int sum = accumulate(nums.begin(), nums.end(), 0);
for (int &x : nums) {
x = x * n - sum;
}
unordered_set<int> left;
for (int i = 1; i < (1 << m); i++) {//有2的n次方种组合
int tot = 0;
for (int j = 0; j < m; j++) {
if (i & (1 << j)) {//
tot += nums[j];
}
}
if (tot == 0) {
return true;
}
left.emplace(tot);
}
int rsum = accumulate(nums.begin() + m, nums.end(), 0);
for (int i = 1; i < (1 << (n - m)); i++) {
int tot = 0;
for (int j = m; j < n; j++) {
if (i & (1 << (j - m))) {
tot += nums[j];
}
}
if (tot == 0 || (rsum != tot && left.count(-tot))) {
return true;
}
}
return false;
}
};
收获
在leetcode上meet in middle的题目还是比较少的,难得遇见一次,去网上搜了下相关的资料,我就不贴链接了,在这道题的体现就是一种小小的分治思想或者准确的说是一种双指针的感觉,从头或者尾部开始遍历将一个整体的集合划分为了没有重复区域的子集合,以缩小时间复杂度,但是由于引入了新的变量来维护一些数据,从而导致了空间复杂度的增加。
257. 二叉树的所有路径
题目介绍
给你一个二叉树的根节点 root ,按 任意顺序 ,返回所有从根节点到叶子节点的路径。
叶子节点 是指没有子节点的节点。
示例 1:
输入:root = [1,2,3,null,5]
输出:[“1->2->5”,“1->3”]
示例 2:
输入:root = [1]
输出:[“1”]
思路
首先题目要求是把所有根节点到叶子节点的路径打印出来,而且叶子节点的定义是其无左右子节点。这道题可以使用回溯法+前序遍历,使用一个result保存答案,再用一个path来表示当前路径(用来回溯),先从左开始遍历到最左叶子节点,然后将其路径以string形式放入result,然后将path 中的当前节点pop掉回溯到当前节点的父节点然后遍历其右点然后同理…
代码
class Solution {
private:
void traversal(TreeNode* cur, vector<int>& path, vector<string>& result) {
path.push_back(cur->val);
// 这才到了叶⼦节点
if (cur->left == NULL && cur->right == NULL) {
string sPath;
for (int i = 0; i < path.size() - 1; i++) {
sPath += to_string(path[i]);
sPath += "->";
}
sPath += to_string(path[path.size() - 1]);
result.push_back(sPath);
return;
}
if (cur->left) {
traversal(cur->left, path, result);
path.pop_back(); // 回溯
}
if (cur->right) {
traversal(cur->right, path, result);
path.pop_back(); // 回溯
}
}
public:
vector<string> binaryTreePaths(TreeNode* root) {
vector<string> result;
vector<int> path;
if (root == NULL) return result;
traversal(root, path, result);
return result;
}
};
收获
掌握了一些回溯的知识:可以通过一个变量来保存路径,通过维护这个路径来实现回溯。