(算法)求数组中数字组合(可多值组合)相加最接近目标数的组合(可能多个)

  今天没事,撸一道算法题

  题目要求:

  给出一个升序排序的可能重复值的数字数组和一个目标值
      其中目标值大于数组中最小数,求数组中数字组合(可多值组合)相加最接近目标数的组合(可能多个)
     不考虑空间复杂度,效率最优的算法;
      样例:
      数组为[3,4,8,6],目标值为9,最接近组合为[8][4,6];

  这道题我的解题思路是:

   1.先得到数组的全部不重复不为空的子集

   2.将子集的和作为key,子集的元素作为value存进map

   3.将子集的和与给定值相减,然后取绝对值,最小的就是题目要的结果

  
  比如:集合是arr={3,4,8},目标值为9
  
  集合的所有不为空的子集为{3},{4},{8},{3,4},{3,8},{4,8},{3,4,8}
  
  六个集合的和分别为3,4,8,7,11,12,15,将这六个数作为key存进map中,value是对应的元素集合,然后把每一个数与目标值(9)相减并取绝对值。
    
  可得到结果为|3-9|=6,|4-9|=5,|8-9=|1,|7-9|=2,|11-9|=3,|15-9|=6,可以看到|8-9|=1是最小的,也就是输出{8}这个子集符合题意。

  不多比比,下面是求集合子集的代码

 public static List<List<Integer>> getArr(List<Integer> list) { 
        if(list.size()>0){ //当数组长度大于0时才进行子集获取
        List<List<Integer>> result = new ArrayList<>();
        long n = (long) Math.pow(2, list.size());//集合的子集数量 2的数组长度次方
        List<Integer> temp;
        for (long l = 0L; l < n; l++) { //遍历出的子集的个数
            temp = new ArrayList<>();
            for (int i = 0; i < list.size(); i++) { 
                if ((l >>> i & 1) == 1) { 
                    temp.add(list.get(i));//用一个List<Integer>来装每个子集中的元素
                }
            }
            if (!temp.isEmpty()) result.add(temp);//当获取到的子集不为空时就放入返回结果中
        }
        return result;
    }
        return null;
    }

   有兴趣可以复制到idea里跑一下

  这部分代码的原理如下:

000 无符号右移0位 与1位与运算后 000=1
000 无符号右移1位 与1位与运算后 000=1
000 无符号右移2位 与1位与运算后 000=1

001 无符号右移0位 与1位与运算后 001   =1 获得下标为0的元素 第一个子集
001无符号右移1位 与1位与运算后 000=1
001无符号右移2位 与1位与运算后 000=1

010 无符号右移0位 与1位与运算后 010=1
010 无符号右移1位 与1位与运算后 001   =1  获得下标1的元素 第二个子集
010 无符号右移2位 与1位与运算后 000=1

011 无符号右移0位 与1位与运算后 001   =1
011 无符号右移1位 与1位与运算后 001   =1 获取下标为0,1的元素 第三个子集
011 无符号右移2位 与1位与运算后 000=1

100 无符号右移0位 与1位与运算后 000=1
100 无符号右移1位 与1位与运算后 000=1
100 无符号右移2位 与1位与运算后 001  =1 获取下标为2的元素 第四个子集

101 无符号右移0位 与1位与运算后 001   =1
101 无符号右移1位 与1位与运算后 000=1 获取下标为0,2的元素 第五个子集
101 无符号右移2位 与1位与运算后 001   =1

110 无符号右移0位 与1位与运算后 000=1
110 无符号右移1位 与1位与运算后 001  =1  获取下标为1,2的元素第六个子集
110 无符号右移2位 与1位与运算后 000  =1

111 无符号右移0位 与1位与运算后 001  =1
111 无符号右移1位 与1位与运算后 001  =1  获取下标为0,1,2的元素 第七个子集
111 无符号右移2位 与1位与运算后 001  =1


输入:
123
输出:
1
2
12
3
13
23
123

  得到所有子集后,求和放入map,将子集的和与目标值相减取最小的绝对值对应的子集并输出,代码如下:

   public static List<List<Integer>> back(List<List<Integer>> list,Integer x){ 
        List<List<Integer>> lists = new ArrayList<>();
        ArrayList<Integer> objects = new ArrayList<Integer>();
        Integer sum=new Integer(0);
        for (int i = 0; i < list.size(); i++) { 
            List<Integer> integers = list.get(i);
            for (int i1 = 0; i1 < integers.size(); i1++) { 
                sum+=integers.get(i1);
                objects.add(Math.abs(sum-x));
            }
            sum=0;
        }
        Integer min = Collections.min(objects);
        for (int i = 0; i < list.size(); i++) { 
            List<Integer> integers = list.get(i);
            for (int i1 = 0; i1 < integers.size(); i1++) { 
                sum+=integers.get(i1);
                }
            if(min==Math.abs(sum-x)){ 
                lists.add(integers);
            }
            sum=0;
            }
        return lists;
    }

  最后测试一下:

     public static void main(String[] args) { 
        List<Integer> integers = new ArrayList<>();
        integers.add(new Integer(6));
        integers.add(new Integer(26));
        integers.add(new Integer(37));
        integers.add(new Integer(50));
        integers.add(new Integer(50));
        integers.add(new Integer(31));
        integers.add(new Integer(16));
        List<List<Integer>> arr = getArr(integers);
        List<List<Integer>> back = back(arr, 100);
        for (int i = 0; i < back.size(); i++) { 
            List<Integer> l1 = back.get(i);
            for (int i1 = 0; i1 < l1.size(); i1++) { 
                System.out.print(l1.get(i1)+" ");
            }
            System.out.println();
        }
    }

    输出:
    50  50  
    6  26  37  31  

    Process finished with exit code 0
    
    6+26+37+31=100

完事

    原文作者:-番茄炒蛋-
    原文地址: https://blog.csdn.net/imagineluopan/article/details/121470667
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞