【每日一题】823. 带因子的二叉树
- 823. 带因子的二叉树
- 题目描述
- 解题思路
823. 带因子的二叉树
题目描述
给出一个含有不重复整数元素的数组 arr ,每个整数 arr[i] 均大于 1。
用这些整数来构建二叉树,每个整数可以使用任意次数。其中:每个非叶结点的值应等于它的两个子结点的值的乘积。
满足条件的二叉树一共有多少个?答案可能很大,返回 对 109 + 7 取余 的结果。
示例 1:
输入: arr = [2, 4]
输出: 3
解释: 可以得到这些二叉树: [2], [4], [4, 2, 2]
示例 2:
输入: arr = [2, 4, 5, 10]
输出: 7
解释: 可以得到这些二叉树: [2], [4], [5], [10], [4, 2, 2], [10, 2, 5], [10, 5, 2].
提示:
1 <= arr.length <= 1000
2 <= arr[i] <= 109
arr 中的所有值 互不相同
解题思路
思路:求解满足条件的二叉树有多少个,该如何求解保证唯一性呢?枚举根节点!由于整数的因子必定比该整数小,故可以先对整数数组进行排序。由于求解时只会使用到以整数数组元素作为根节点的二叉树数目,故dfs(i)的i表示数组下标,记忆化数组memo[i]的i也表示数组下标。其中i=j*k,i是当前乘积,j是乘积因子且j<i,如何查找k呢?使用arr[i]/arr[j]对应的下标,故此时需要使用umap将数组元素与下标对应。总而言之,即先对数组排序,接着将数组元素与对应下标进行映射,然后枚举数组元素,求解以每一个元素作为根节点的二叉树数量并进行累加,对于以某一节点i为根节点的二叉树数量求解方法为,枚举<i的节点j,判断是否能够在数组中找到k使得i=j*k,此处可以加入记忆化。
// 超时递归
class Solution {
public:const int MOD=1e9+7;int numFactoredBinaryTrees(vector<int>& arr) {// 存储数组元素 用于O(1)存取unordered_set<int> s(arr.begin(),arr.end());function<long long (int)> dfs=[&](int val) -> long long{// 如果val无分解因子 则相当于return 1long long res=1;// 遍历查找因子for(int x:arr){if(val%x==0&&s.count(val/x))res+=dfs(x)*dfs(val/x);}return res;};long long res=0;// 枚举每一个根节点for(int x:arr)res+=dfs(x);return res%MOD;}
};
// 通过 记忆化
class Solution {
public:const int MOD=1e9+7;int numFactoredBinaryTrees(vector<int>& arr) {// 对数组排序 方便记忆化sort(arr.begin(),arr.end());int n=arr.size();// 记忆化数组 vector<long long> memo(n,-1);// 存储数组元素 用于O(1)存取unordered_map<int,int> s;// 将数组元素与数组下标映射for(int i=0;i<n;i++)s[arr[i]]=i;function<long long (int)> dfs=[&](int i) -> long long{// 已经计算过则直接返回if(memo[i]!=-1)return memo[i];// 如果val无分解因子 则相当于return 1long long res=1;int val=arr[i];// 遍历查找因子for(int j=0;j<i;j++){int x=arr[j];if(val%x==0&&s.count(val/x))res+=dfs(j)*dfs(s[val/x]);}return memo[i]=res;};long long res=0;// 枚举每一个根节点 根节点只有一个 每个根节点不同即为不同 根节点代表唯一性for(int i=0;i<n;i++)res+=dfs(i);return res%MOD;}
};
// 通过 动态规划
class Solution {
public:const int MOD=1e9+7;int numFactoredBinaryTrees(vector<int>& arr) {// 对数组排序 方便记忆化sort(arr.begin(),arr.end());int n=arr.size();// 记忆化数组 vector<long long> f(n,1);// 存储数组元素 用于O(1)存取unordered_map<int,int> s;// 将数组元素与数组下标映射for(int i=0;i<n;i++)s[arr[i]]=i;for(int i=0;i<n;i++){int val=arr[i];// 遍历查找因子for(int j=0;j<i;j++){int x=arr[j];if(val%x==0&&s.count(val/x))f[i]+=f[j]*f[s[val/x]];}}long long res=0;// 枚举每一个根节点 根节点只有一个 每个根节点不同即为不同 根节点代表唯一性for(int i=0;i<n;i++)res+=f[i];return res%MOD;}
};
// 通过 动态规划+对称性
class Solution {
public:const int MOD=1e9+7;int numFactoredBinaryTrees(vector<int>& arr) {// 对数组排序 方便记忆化sort(arr.begin(),arr.end());int n=arr.size();// 记忆化数组 vector<long long> f(n,1);// 存储数组元素 用于O(1)存取unordered_map<int,int> s;// 将数组元素与数组下标映射for(int i=0;i<n;i++)s[arr[i]]=i;for(int i=0;i<n;i++){int val=arr[i];// 遍历查找因子for(int j=0;j<i;j++){int x=arr[j];// 算到一半即可if(1LL*x*x>val)break;// 临界点if(x*x==val){f[i]+=f[j]*f[j];break;}// 12 = 2*6 = 6*2if(val%x==0&&s.count(val/x))f[i]+=f[j]*f[s[val/x]]*2;}}long long res=0;// 枚举每一个根节点 根节点只有一个 每个根节点不同即为不同 根节点代表唯一性for(int i=0;i<n;i++)res+=f[i];return res%MOD;}
};
总结:递归->记忆化搜索->动态规划->滚动数组。