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

来源:互联网 发布:威尼斯共和国 知乎 编辑:程序博客网 时间:2024/06/02 16:37

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

算法思路: 使用递归.

对数组排序, 从小到大;
令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