JAVA穷举法递归实现:查找一个数等于一组数中哪些数相加的和

(最近博客被盗了,此文章被删除,现在重新发一遍)

前两天,同事问我怎么处理这个问题:他手里有一个比较大的数,还有一堆乱七八糟的数,怎样找出那些数相加等于这个比较大的数。我想了想感觉这是个比较恶心的算法,在网上找了有很多例子,都觉得不太合适。



我们把这个较大的数称作“和”,用sum表示,计算查找的时候我们可能会遇到一些问题:
1. sum没有规则,可能非常大,还可能带着小数点。所以不能用普通的int类型来计算。
2. 数组中可能存在多个集合符合条件,且元素个数不同,比如(1+2+3+4)=10,(4+6)=10。还有可能组中存在重复数据,比如(1+1+2)=4,(2+2)=4。这样用循环语句写就非常麻烦,应该用递归方法。
3. 会不会有的元素就等于“和”本身呢?


我想了一下午,觉得可以用穷举法暴力查找实现:
首先,我们拿到了一个乱七八糟的数组,我们先给他排个序,升序排序,从小到大。 
然后,为了增加循环递归的效率,先把相加结果肯定不等于sum的数去掉。数组已经是按大小个排序了,就用最大的数加最小的数,如果相加大于sum,那这个最大数在这个数组里跟谁加都不会等于sum,果断踢走。max+min>sum,则删除max。 
最后,数组都排完序了,“太大个”的都踢走了。剩下的就是我们要循环查找的数了。

double a={a0,a1,a2…..an},如果a[n]+a[n-1]>sum,跳过a[n-1],判断a[n]+a[n-2]和sum的关系;如果a[n]+a[n-2]<sum,则再查找(a[n]+a[n-2])+a[n-3]与sum的关系,循环调用此逻辑,可获我们要查找的结果a[n]+a[x]+….+a[y]=sum或者无任何元素相加等于sum。


 上代码

import java.util.ArrayList;  
import java.util.LinkedList;  
import java.util.List;  
  
public class CreateTest {  
    private static List<Integer> findNumIndex;// 存储符合条件的数组元素下表  
    private static boolean findon;// 是否可以从数组中找到相加等于“和”的元素  
    private static boolean someoneEqualSum;// 数组中是否有某个元素等于“和”本身  
    private static double[] a;// 数组  
    private static double sum;// “和”  
  
    public CreateTest(double[] a, double sum) {  
        this.a = a;  
        this.sum = sum;  
        findon = false;  
        findNumIndex = new ArrayList<Integer>();  
        someoneEqualSum = false;  
    }  
  
    public void start() {  
        a = maopao(a);// 首先冒泡排序  
        List<Double> list = new LinkedList<Double>();  
        for (int i = 0; i < a.length; i++) {// 把double数组付给list  
            list.add(a[i]);  
        }  
        boolean flag = true;  
        do {  
            double mix = list.get(0);// 当前最小值  
            double max = list.get(list.size() - 1);// 当前最大值  
            if (max == sum) {// 找到等于“和”的元素,打个标记  
                someoneEqualSum = true;  
            }  
            if (mix + max > sum && flag) {// 删除没用的最大值  
                list.remove(list.size() - 1);  
            } else {  
                flag = false;  
            }  
        } while (flag);  
        startMath(list, sum);  
        if (!findon) {  
            System.out.println("未找到符合条件的数组");  
        }  
    }  
  
    public double[] maopao(double[] a) {  
        for (int i = 0; i < a.length - 1; i++) {  
            for (int k = 0; k < a.length - 1 - i; k++) {  
                if (a[k] > a[k + 1]) {  
                    double b = a[k];  
                    a[k] = a[k + 1];  
                    a[k + 1] = b;  
                }  
            }  
        }  
        return a;  
    }  
  
    public void startMath(List<Double> list, double sum) {  
        if (someoneEqualSum) {// 先输出等于“和”本身的数  
            System.out.println("相加等于" + sum + "的数组为:");  
            System.out.println(sum);  
            System.out.println("-----------------------");  
        }  
        for (int i = 0; i <= list.size() - 2; i++) {  
            findNumIndex.clear();  
            findNumIndex.add(list.size() - 1 - i);// 记录第一个元素坐标  
            double indexNum = list.get(list.size() - 1 - i);// 从最大的元素开始,依次往前推  
            action(list, indexNum, list.size() - 1 - i, sum);  
        }  
    }  
  
    /** 
     * 递归方法 
     *  
     * @param list 
     *            被查询的数组 
     * @param indexsum 
     *            当前元素相加的和 
     * @param index 
     *            下一个元素位置 
     * @param sum 
     *            要匹配的和 
     */  
    public void action(List<Double> list, double indexsum, int index, double sum) {  
        if (index == 0)  
            return;  
        if (indexsum + list.get(index - 1) > sum) {// 元素【index-1】太大了,跳到下一个元素继续遍历  
            action(list, indexsum, index - 1, sum);  
        } else if (indexsum + list.get(index - 1) < sum) {// 元素【index-1】可能符合条件,继续往下找  
            findNumIndex.add(index - 1);// 记录此元素坐标  
            indexsum = indexsum + list.get(index - 1);// 更新元素的和  
            action(list, indexsum, index - 1, sum);  
        } else if (indexsum + list.get(index - 1) == sum) {  
            findNumIndex.add(index - 1);  
            findon = true;// 告诉系统找到了  
            System.out.println("相加等于" + sum + "的数组为:");  
            for (int a : findNumIndex) {  
                System.out.println(list.get(a));  
            }  
            System.out.println("-----------------------");  
            return;  
        }  
    }  
  
    public static void main(String[] args) {  
        double[] a = { 1, 8.3, 43, 22, 12, 7, 65, 100, 86, 97, 14, 53, 523,  
                555, 265, 74, 2, 556, 76, 98, 34, 78, 33, 50, 22 };  
        double sum = 100;  
        CreateTest s = new CreateTest(a, sum);  
        s.start();  
    }  
}  

执行的结果:
相加等于100.0的数组为:
100.0
-----------------------
相加等于100.0的数组为:
98.0
2.0
-----------------------
相加等于100.0的数组为:
97.0
2.0
1.0
-----------------------
相加等于100.0的数组为:
86.0
14.0
-----------------------
相加等于100.0的数组为:
78.0
22.0
-----------------------
相加等于100.0的数组为:
76.0
22.0
2.0
-----------------------
相加等于100.0的数组为:
65.0
34.0
1.0
-----------------------
相加等于100.0的数组为:
50.0
43.0
7.0
-----------------------
相加等于100.0的数组为:
43.0
34.0
22.0
1.0
-----------------------


代码优点:
1. 可输出多组符合条件的集合
2. 可以输出等于sum本身的元素
3. 循环递归调用
4. 支持小数点


代码缺点:
1. 程序有一个bug:如果sum=100,数组={98,2,1,1},输出的结果只有98+2=100。结果98+1+1=100被忽略了。
2. 穷举法暴力查找,效率差。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值