给定一列数(未排序)和一列目标值, 找出唯一的一个组合和等于目标值的组合, 数组中的数不能重复使用.
算法思路: 使用递归.
对数组排序, 从小到大;
令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