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
- leetcode算法实例---组合和枚举问题
- 什么是组合算法和组合问题
- 最大子矩阵问题;枚举行的组合,然后利用一维最大子段和的DP算法;
- 递归枚举排列和组合
- Leetcode全组合问题
- 算法实例:枚举的魅力
- 枚举算法--熄灯问题
- 组合问题算法
- 钱组合算法问题
- 组合算法问题
- Leetcode分类解析:组合算法
- USB枚举和HID枚举实例
- USB枚举和HID枚举实例(6)
- USB枚举和HID枚举实例
- USB枚举和HID枚举实例
- USB枚举和HID枚举实例
- USB枚举和HID枚举实例
- 模拟和枚举算法
- zcmu-1929
- Dubbo-概述,示例Demo
- fork函数的解析
- 月总结-美丽都是来之不易的
- quartz数据库分布式创建定时任务,出现任务状态error,这些任务无法执行
- leetcode算法实例---组合和枚举问题
- HDU 6166 二进制分解 + 最短路
- html中的占位符
- fstream读写文件注意问题
- 科普常见的游戏类型
- tomcat启动失败
- 使用atlas
- Sql Server实用操作-数据库一致性检测工具(DBCC)
- echarts3 图表配置小总结