题目:1863.找出所有子集的异或总和再求和

​​题目来源:

        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 计算异或求和即可。

        第三种是按位考虑+二项式展开。是根据展开式和数学解答的。没看太懂。 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值