leetcode算法实例---组合和枚举问题

给定一列数(未排序)和一列目标值, 找出唯一的一个组合和等于目标值的组合, 数组中的数不能重复使用.

算法思路: 使用递归.

对数组排序, 从小到大;
令i = 起始下标(初始为0), 对于每一个数,
如果它等于目标值, 则在缓存结果中加入此数并将缓存结果加入输出队列, 随后在缓存结果中删除此数;
如果它小于目标值, 则在缓存结果中加入此数并递归调用此算法, 目标值更新为差值, 起始下标为i;
如果它大于目标值, 算法返回.
对于2.2的理解: 如果这个数小于目标值, 由于算法是循环递归, 那么起始的下标必定不能小于i(否则出现重复情况)
代码如下:

import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
 * 将手续费作为一笔交易,如果拼凑不出来,则拆分剩余明细中金额最大的那笔交易
 * @author kenan.zhang
 * @date 2017-08-23
 */
public class Calc {

    public static void recursion(List<Integer> sourceList,LinkedList<Integer> tmpList,List<LinkedList<Integer>> resultList,Integer targetValue,int startIndex){
        for (int i = startIndex; i < sourceList.size(); i++) {
            Integer currentValue = sourceList.get(i);
            if(currentValue <= targetValue){
                if(currentValue.equals(targetValue)){
                    tmpList.add(currentValue);
                    resultList.add(new LinkedList<Integer>(tmpList));
                    tmpList.remove(currentValue);
                    break;
                }else if(currentValue < targetValue){
                    tmpList.add(currentValue);
                    recursion(sourceList, tmpList, resultList, targetValue-currentValue, i+1);
                    tmpList.remove(currentValue);
                    //只取第一个符合条件的数据组
                    if(resultList.size() == 1){
                        //从源数据列表中去掉已经使用过的数据
                        sourceList.removeAll(resultList.get(0));
                        break;
                    }
                }
            }else{
                break;
            }
        }

    }

    public static Map<Integer,LinkedHashMap<Integer,List<Integer>>> getResult(List<Integer> sourceList,List<Integer> targetValues){
        Collections.sort(sourceList);
        Collections.sort(targetValues);
        int startIndex = 0;
        List<Integer> remainTargetValues = new LinkedList<Integer>();
        Map<Integer,LinkedHashMap<Integer,List<Integer>>> resultMap = new LinkedHashMap<Integer,LinkedHashMap<Integer,List<Integer>>>();
        for(int i=0;i<targetValues.size();i++){
            Integer currentValue = targetValues.get(i);
            List<LinkedList<Integer>> resultList = new LinkedList<LinkedList<Integer>>();
            LinkedList<Integer> tmpList = new LinkedList<Integer>();
            recursion(sourceList, tmpList, resultList,currentValue, startIndex);
            if(resultList.size() == 0){
                remainTargetValues.add(currentValue);
                resultMap.put(currentValue, null);
            }else{
                LinkedHashMap<Integer,List<Integer>> res = new LinkedHashMap<Integer,List<Integer>>();
                //为了区分该汇总交易是否通过某笔交易明细拆分拼凑而来:如果子map的key为Integer.MAX_VALUE,否则对应的是被拆分的交易明细
                res.put(Integer.MAX_VALUE, resultList.get(0));
                resultMap.put(currentValue, res);
            }
        }
        //对最大的一笔交易进行拆分
        int lastIndex = sourceList.size()-1;
        Integer lastValue = sourceList.get(lastIndex);
        for(int i=0;i<remainTargetValues.size();i++){
            Integer targetValue = remainTargetValues.get(i);
            List<Integer> myTmpList = new LinkedList<Integer>();
            Integer gap = lastValue - sourceList.get(i);
            if(gap >= 0){
                myTmpList.add(sourceList.get(i));
                myTmpList.add(targetValue-sourceList.get(i));
                Collections.sort(myTmpList);
                LinkedHashMap<Integer,List<Integer>> res = new LinkedHashMap<Integer,List<Integer>>();
                //为了区分该汇总交易是否通过某笔交易明细拆分拼凑而来:如果子map的key为Integer.MAX_VALUE,否则对应的是被拆分的交易明细
                res.put(lastValue, myTmpList);
                resultMap.put(targetValue, res);
                sourceList.set(lastIndex, gap);
            }else{
                System.out.println(sourceList);
                for(int k=remainTargetValues.size()-1;k<sourceList.size();k++){
                    myTmpList.add(sourceList.get(k));
                }
                myTmpList.add(lastValue);
                LinkedHashMap<Integer,List<Integer>> res = new LinkedHashMap<Integer,List<Integer>>();
                //为了区分该汇总交易是否通过某笔交易明细拆分拼凑而来:如果子map的key为Integer.MAX_VALUE,否则对应的是被拆分的交易明细
                res.put(lastValue, myTmpList);
                resultMap.put(targetValue, res);
            }
        }
        return resultMap;
    }

    //重载
    public static Map<Integer,LinkedHashMap<Integer,List<Integer>>> getResult(Integer[] source,Integer[] target){
        List<Integer> sourceList = new LinkedList<Integer>();
        sourceList.addAll(Arrays.asList(source));
        LinkedList<Integer> targetValues = new LinkedList<Integer>(Arrays.asList(target));
        return getResult(sourceList, targetValues);
    }
    public static void main(String[] args) {
        Integer[] source = new Integer[]{69, 35, 29, 26, 18, 12, 46, 92, 96, 75, 58, 34, 110};
        Integer[] target = new Integer[]{104,44,58,61,73,260,100};
        Map<Integer,LinkedHashMap<Integer,List<Integer>>> resultMap = getResult(source, target);
        System.out.println("结果resultMap是:"+resultMap);
        List<Integer> sourceList = new LinkedList<Integer>();
        sourceList.addAll(Arrays.asList(source));
        LinkedList<Integer> targetValues = new LinkedList<Integer>(Arrays.asList(target));
        Map<Integer,LinkedHashMap<Integer,List<Integer>>> resultMap2 = getResult(sourceList, targetValues);
        System.out.println("结果resultMap2是:"+resultMap2);
    }

}

改进版:

import java.math.BigDecimal;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
 * 将手续费作为一笔交易,如果拼凑不出来,则拆分剩余明细中金额最大的那笔交易
 * 该方案理论上可能存在需要拆分多笔交易的情况
 * @author kenan.zhang
 * @date 2017-08-23
 */
public class Calculator2 {

    public static void recursion(List<Map<String,Object>> detailList,LinkedList<Map<String,Object>> tmpList,LinkedList<Object> resultList,Map<String,Object> targetSummary,int startIndex){

        for (int i = startIndex; i < detailList.size(); i++) {
            Map<String,Object> currentDetail = detailList.get(i);
            BigDecimal summaryMoney = (BigDecimal) targetSummary.get("summaryMoney");
            BigDecimal detailMoney = (BigDecimal) currentDetail.get("detailMoney");
            if(detailMoney.compareTo(summaryMoney) != 1){
                if(detailMoney.compareTo(summaryMoney) == 0){
                    tmpList.add(currentDetail);
                    resultList.add(new LinkedList<Map<String,Object>>(tmpList));
                    tmpList.remove(currentDetail);
                    resultList.add(targetSummary);
                    //从源数据列表中去掉已经使用过的数据
                    detailList.removeAll((Collection<?>) resultList.get(0));
                    break;
                }else if(detailMoney.compareTo(summaryMoney) == -1){
                    tmpList.add(currentDetail);
                    targetSummary.put("summaryMoney", summaryMoney.subtract(detailMoney));
                    recursion(detailList, tmpList, resultList, targetSummary, i+1);
                    //值还原
                    targetSummary.put("summaryMoney", ((BigDecimal)(targetSummary.get("summaryMoney"))).add(detailMoney));
                    tmpList.remove(currentDetail);
                    //只取第一对符合条件的数据,第一个元素代表明细数据列表,第二个元素代表对应的汇总数据
                    if(resultList.size() == 2){
                        //从源数据列表中去掉已经使用过的数据
                        detailList.removeAll((Collection<?>) resultList.get(0));
                        break;
                    }
                }
            }else{
                break;
            }
        }

    }


    public static LinkedList<Object> getResult(Map<String,List<Map<String,Object>>> sourceData){

        List<Map<String,Object>> detailList = sourceData.get("detail");
        List<Map<String,Object>> summaryList = sourceData.get("summary");
        DetailValueComparator detailComparator = new DetailValueComparator();
        SummaryValueComparator summaryComparator = new SummaryValueComparator();
        Collections.sort(detailList,detailComparator);
        Collections.sort(summaryList,summaryComparator);
        int startIndex = 0;
        List<Map<String,Object>> remainSummaryList = new LinkedList<Map<String,Object>>();
        LinkedList<Object> returnList = new LinkedList<Object>();
        for(int i=0;i<summaryList.size();i++){
            Map<String,Object> currentSummary = summaryList.get(i);
            LinkedList<Object> resultList = new LinkedList<Object>();
            LinkedList<Map<String,Object>> tmpList = new LinkedList<Map<String,Object>>();
            recursion(detailList, tmpList, resultList,currentSummary, startIndex);
            if(resultList.size() == 0){
                remainSummaryList.add(currentSummary);
            }
            returnList.addAll(resultList);
        }

        Collections.sort(detailList,detailComparator);
        for(int i=0;i<remainSummaryList.size();i++){
            //对最大的一笔交易进行拆分
            int lastIndex = detailList.size()-1;
            Map<String,Object> lastDetail = detailList.get(lastIndex);
            Map<String,Object> targetSummary = remainSummaryList.get(i);
            LinkedList<Map<String,Object>> myTmpList = new LinkedList<Map<String,Object>>();
            BigDecimal lastDetailMoney = (BigDecimal) lastDetail.get("detailMoney");
            BigDecimal summaryMoney = (BigDecimal) targetSummary.get("summaryMoney");
            BigDecimal sum = new BigDecimal(0);
            //待删除列表
            List<Map<String,Object>> listToRemove = new LinkedList<Map<String,Object>>();
            for(int j=0;j<detailList.size();j++){
                myTmpList.add(detailList.get(j));
                Object t = detailList.get(j).get("detailMoney");
                BigDecimal detailMoney = new BigDecimal(t.toString());
                listToRemove.add(detailList.get(j));
                sum = sum.add(detailMoney);
                if(!less(sum, summaryMoney)){
                    listToRemove.remove(detailList.get(j));
                    myTmpList.remove(detailList.get(j));
                    Map<String,Object> m = new HashMap<String,Object>();
                    //gap代表拼凑的金额
                    BigDecimal gap = summaryMoney.subtract(sum.subtract(detailMoney));
                    m.put("detailMoney", gap);
                    m.put("ifBorrow", "yes");
                    //被拆交易的序列号
                    m.put("borrowSerial", lastDetail.get("serialNo"));
                    myTmpList.add(m);
                    returnList.add(myTmpList);
                    returnList.add(targetSummary);
                    //更新被拆分交易的金额
                    lastDetail.put("detailMoney", lastDetailMoney.subtract(gap));
                    detailList.removeAll(listToRemove);
                    if(equal(sum, summaryMoney)){
                        detailList.remove(lastDetail);
                    }
                    //重新排序,保证每次取到最大的交易明细进行拆分
                    Collections.sort(detailList, detailComparator);
                    break;
                }
            }
        }
        return returnList;
    }

    public static boolean greater(BigDecimal d1,BigDecimal d2){
        return d1.compareTo(d2)==1;
    }

    public static boolean equal(BigDecimal d1,BigDecimal d2){
        return d1.compareTo(d2)==0;
    }

    public static boolean less(BigDecimal d1,BigDecimal d2){
        return d1.compareTo(d2)==-1;
    }

    public static void main(String[] args) {

        Map<String,List<Map<String,Object>>> sourceData = new HashMap<String,List<Map<String,Object>>>();
        List<Map<String,Object>> detailList = new LinkedList<Map<String,Object>>();
        Map<String, Object> m1 = new HashMap<String, Object>();
        m1.put("detailMoney", new BigDecimal(69));
        m1.put("serialNo", 69);
        detailList.add(m1);
        m1 = new HashMap<String, Object>();
        m1.put("detailMoney", new BigDecimal(35));
        m1.put("serialNo", 35);
        detailList.add(m1);
        m1 = new HashMap<String, Object>();
        m1.put("detailMoney", new BigDecimal(29));
        m1.put("serialNo", 29);
        detailList.add(m1);
        m1 = new HashMap<String, Object>();
        m1.put("detailMoney", new BigDecimal(26));
        m1.put("serialNo", 26);
        detailList.add(m1);
        m1 = new HashMap<String, Object>();
        m1.put("detailMoney", new BigDecimal(18));
        m1.put("serialNo", 18);
        detailList.add(m1);
        m1 = new HashMap<String, Object>();
        m1.put("detailMoney", new BigDecimal(12));
        m1.put("serialNo", 12);
        detailList.add(m1);
        m1 = new HashMap<String, Object>();
        m1.put("detailMoney", new BigDecimal(46));
        m1.put("serialNo", 46);
        detailList.add(m1);
        m1 = new HashMap<String, Object>();
        m1.put("detailMoney", new BigDecimal(92));
        m1.put("serialNo", 92);
        detailList.add(m1);
        m1 = new HashMap<String, Object>();
        m1.put("detailMoney", new BigDecimal(96));
        m1.put("serialNo", 96);
        detailList.add(m1);
        m1 = new HashMap<String, Object>();
        m1.put("detailMoney", new BigDecimal(75));
        m1.put("serialNo", 75);
        detailList.add(m1);
        m1 = new HashMap<String, Object>();
        m1.put("detailMoney", new BigDecimal(58));
        m1.put("serialNo", 58);
        detailList.add(m1);
        m1 = new HashMap<String, Object>();
        m1.put("detailMoney", new BigDecimal(34));
        m1.put("serialNo", 34);
        detailList.add(m1);
        m1 = new HashMap<String, Object>();
        m1.put("detailMoney", new BigDecimal(120));
        m1.put("serialNo", 120);
        detailList.add(m1);
        m1 = new HashMap<String, Object>();
        m1.put("detailMoney", new BigDecimal(32));
        m1.put("serialNo", 32);
        detailList.add(m1);
        sourceData.put("detail", detailList);

        List<Map<String,Object>> summaryList = new LinkedList<Map<String,Object>>();
        Map<String, Object> m2 = new HashMap<String, Object>();
        m2.put("summaryMoney", new BigDecimal(104));
        m2.put("serialNo", 104);
        summaryList.add(m2);
        m2 = new HashMap<String, Object>();
        m2.put("summaryMoney", new BigDecimal(44));
        m2.put("serialNo", 44);
        summaryList.add(m2);
        m2 = new HashMap<String, Object>();
        m2.put("summaryMoney", new BigDecimal(58));
        m2.put("serialNo", 58);
        summaryList.add(m2);
        m2 = new HashMap<String, Object>();
        m2.put("summaryMoney", new BigDecimal(93));
        m2.put("serialNo", 93);
        summaryList.add(m2);
        m2 = new HashMap<String, Object>();
        m2.put("summaryMoney", new BigDecimal(73));
        m2.put("serialNo", 73);
        summaryList.add(m2);
        m2 = new HashMap<String, Object>();
        m2.put("summaryMoney", new BigDecimal(260));
        m2.put("serialNo", 260);
        summaryList.add(m2);
        m2 = new HashMap<String, Object>();
        m2.put("summaryMoney", new BigDecimal(110));
        m2.put("serialNo", 110);
        summaryList.add(m2);
        sourceData.put("summary", summaryList);

        DetailValueComparator detailComparator = new DetailValueComparator();
        SummaryValueComparator summaryComparator = new SummaryValueComparator();
        Collections.sort(detailList,detailComparator);
        Collections.sort(summaryList,summaryComparator);
        LinkedList<Object> result = getResult(sourceData);
        System.out.println(result);
    }

}

SummaryValueComparator.java

import java.math.BigDecimal;
import java.util.Comparator;
import java.util.Map;

public class SummaryValueComparator implements Comparator<Map<String, Object>> {
    public int compare(Map<String, Object> m, Map<String, Object> n) {
        return ((BigDecimal) m.get("summaryMoney")).compareTo((BigDecimal) n.get("summaryMoney"));
    }
}

DetailValueComparator.java

import java.math.BigDecimal;
import java.util.Comparator;
import java.util.Map;

public class DetailValueComparator implements Comparator<Map<String, Object>> {
    public int compare(Map<String, Object> m, Map<String, Object> n) {
        return ((BigDecimal) m.get("detailMoney")).compareTo((BigDecimal) n.get("detailMoney"));
    }
}

由于可能存在需要拆分多笔交易的情况,程序针对该情况作了优化

数据输出:
[[{serialNo=12, detailMoney=12}, {serialNo=32, detailMoney=32}], {serialNo=44, summaryMoney=44}, [{serialNo=58, detailMoney=58}], {serialNo=58, summaryMoney=58}, [{serialNo=18, detailMoney=18}, {serialNo=26, detailMoney=26}, {serialNo=29, detailMoney=29}], {serialNo=73, summaryMoney=73}, [{serialNo=35, detailMoney=35}, {serialNo=69, detailMoney=69}], {serialNo=104, summaryMoney=104}, [{serialNo=34, detailMoney=34}, {serialNo=46, detailMoney=46}, {detailMoney=13, ifBorrow=yes, borrowSerial=120}], {serialNo=93, summaryMoney=93}, [{serialNo=75, detailMoney=75}, {detailMoney=35, ifBorrow=yes, borrowSerial=120}], {serialNo=110, summaryMoney=110}, [{serialNo=120, detailMoney=72}, {serialNo=92, detailMoney=92}, {detailMoney=96, ifBorrow=yes, borrowSerial=96}], {serialNo=260, summaryMoney=260}]

通过连续相加进行拼凑:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

/**
 * @author kenan.zhang
 * @date 2017-08-29
 */
public class Calculator3 {

    public static List<Map<Integer,List<Integer>>> patch(List<Integer> source,List<Integer> target){

        List<Map<Integer,List<Integer>>> result = new LinkedList<Map<Integer,List<Integer>>>();
        //找出最大的那笔交易进行拆分
        Integer max = Collections.max(source);
        source.remove(max);
        for(int i=0;i<target.size();i++){
            Integer t = target.get(i);
            int start = 0;
            //偏移量
            int shift= 0;
            //获取最接近汇总数据的明细数据
            int sum = 0;
            for(int j=0;j<source.size();j++){
                Integer s = source.get(j);
                if(sum==0 && t-s < 0){
                    start++;
                    continue;
                }else{
                    sum += s;
                    if(sum > t){
                        break;
                    }
                    shift++;
                }
            }
            List<Integer> sub = source.subList(start, start+shift);
            Map<Integer,List<Integer>> item = new HashMap<Integer,List<Integer>>();
            item.put(t, new LinkedList<Integer>(sub));
            result.add(item);
            source.removeAll(sub);
        }
        //对明细数据总和不等于汇总数据的item进行拼凑
        for(Map<Integer,List<Integer>> item:result){
            Iterator<Entry<Integer, List<Integer>>> iter = item.entrySet().iterator();
            Entry<Integer, List<Integer>> entry = iter.next();
            int sum = 0;
            List<Integer> v = entry.getValue();
            Integer k = entry.getKey();
            for(int i=0;i<v.size();i++){
                sum += v.get(i);
            }
            if(sum < k){
                v.add(k-sum);
            }
        }
        return result;

    }

    public static void main(String[] args) {
        Integer[] source = new Integer[]{1032, 69, 35, 29, 26, 18, 12, 46, 92, 96, 75, 58, 34, 120,9000};
        Integer[] target = new Integer[]{104,44,58,93,73,260,110,10000};
        List<Map<Integer,List<Integer>>> result = patch(new ArrayList<Integer>(Arrays.asList(source)),new ArrayList<Integer>(Arrays.asList(target)));
        System.out.println(result);
    }

}

数据输出样例:
[{104=[69, 35]}, {44=[29, 15]}, {58=[26, 18, 12, 2]}, {93=[46, 47]}, {73=[58, 15]}, {260=[92, 96, 72]}, {110=[75, 34, 1]}, {10000=[1032, 120, 8848]}]

详细代码可参考我的github:https://github.com/iamzken/recursion

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AI传道士

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值