题目来源:
leetcode题目,网址:1863. 找出所有子集的异或总和再求和 - 力扣(LeetCode)
解题思路:
尝试一:
思路:暴力递归,每次将数组中的元素删去一个得到新的数组,异或后再求和。在递归过程中会产生大量重复,所以需要对当前层数计数,原始数组层数为 0,每删去一个元素层数加一,最后返回时若层数 x 大于1 ,则当前子数组计算了 x 次,需要将结果除以 x 后返回。
注意: 计算时因为要除以 x ,所以要以递归函数返回类型为 double,因为除法过程中可能产生无限循环小数或其余情况,因此最后的和与实际和可能有细微区别,因此在强制转换时需要四舍五入取整而不是默认的向 0 取整。
结果:第 24 个测试用例超时。
尝试二:
思路:在暴力递归时使用哈希表记录计算过的元素组合从而避免一些重复计算。
结果:第 8 个测试用例 [1,1,1] 报错,前两个元素组成的 [1,1] 和后两个元素组成的 [1,1] 需要计算两次,但哈希表中是相同的,计算被跳过了。
尝试三:
思路:在暴力递归时使用哈希表记录计算过的位置组合从而避免重复计算。
结果:通过,但执行用时仅击败 5.33%,内存消耗仅击败 5.34%。应该有基于数学的 O(n) 或 O(1) 解法。
解题代码:
//尝试一:
class Solution {
public int subsetXORSum(int[] nums) {
int total=nums[0];
ArrayList<Integer> list=new ArrayList<>();
list.add(nums[0]);
for(int i=1;i<nums.length;i++){
total=total^nums[i];
list.add(nums[i]);
}
return (int)(assist(list,total,0)+0.5);
}
public double assist(ArrayList<Integer> list,int total,int depth){
double res=total;
for(int i=0;i<list.size();i++){
ArrayList<Integer> temp=new ArrayList<>(list);
int tempTotal=total^list.get(i);
temp.remove(i);
res+=assist(temp,tempTotal,depth+1);
}
return depth==0?res:res/depth;
}
}
//尝试三
class Solution {
Set<ArrayList<Integer>> subset=new HashSet<>();
public int subsetXORSum(int[] nums) {
int total=nums[0];
ArrayList<Integer> list=new ArrayList<>();
ArrayList<Integer> posList=new ArrayList<>();
list.add(nums[0]);
posList.add(0);
for(int i=1;i<nums.length;i++){
total=total^nums[i];
list.add(nums[i]);
posList.add(i);
}
return assist(list,total,posList);
}
public int assist(ArrayList<Integer> list,int total,ArrayList<Integer> posList){
if(!subset.contains(posList)){
subset.add(posList);
}else{
return 0;
}
int res=total;
for(int i=0;i<list.size();i++){
if(list.get(i)==-1){
continue;
}
ArrayList<Integer> temp=new ArrayList<>(list);
ArrayList<Integer> tempPos=new ArrayList<>(posList);
int tempTotal=total^list.get(i);
temp.set(i,-1);
tempPos.set(i,-1);
res+=assist(temp,tempTotal,tempPos);
}
return res;
}
}
总结:
官方题解给出了三种解法。
第一种是递归枚举子集。从第一个元素开始,取或不取造成两个不同的分支,然后每多一个元素,分支数*2。处理完最后一个元素后,对异或结果求和即可。
第二种是迭代法枚举子集。一个长度为 n 的数组有 2^n 个子集,将其映射到 [0,2^n-1] ,其中一个整数的第 i 位表示第 i 个元素是否被选取,这样遍历从 0 到 2^n-1 计算异或求和即可。
第三种是按位考虑+二项式展开。是根据展开式和数学解答的。没看太懂。