业务需求:用户可以用优惠券在支付相关费用时减免优惠券对应的金额,优惠券分为三种类型:现金券,代金券,折扣券,需要根据用户的优惠券列表,找出优惠券金额最多的使用方案。

优惠券使用说明:所有优惠券都是分批次的,根据公司活动,按批次进行发放。同批次现金券可以叠加使用,不同批次不可叠加;代金券,折扣券不可叠加,每次只能使用一张。优惠券抵销金额,可以大于支付金额。

直接上代码:

优惠券实体类:

public class Coupon implements Comparable<Coupon> {private int id;private int batchId;private String name;private BigDecimal amount;private String couponType;public Coupon() {}public Coupon(int id, String name, BigDecimal amount) {this.id = id;this.name = name;this.amount = amount;}public int getId() {return id;}public void setId(int id) {this.id = id;}public int getBatchId() {return batchId;}public void setBatchId(int batchId) {this.batchId = batchId;}public String getName() {return name;}public void setName(String name) {this.name = name;}public BigDecimal getAmount() {return amount;}public void setAmount(BigDecimal amount) {this.amount = amount;}public String getCouponType() {return couponType;}public void setCouponType(String couponType) {this.couponType = couponType;}@Overridepublic int compareTo(Coupon o) {return id - o.id;}@Overridepublic String toString() {return "[" + "id=" + id + ", amount=" + amount + ']';}}

找出现金券同批次所有组合算法,以及找出组合中最接近支付金额的组合的算法

public class Combinations {/*** 所有的组合* * @param arr* @return*/public static <R extends Comparable<R>> List<List<R>> allCombination(List<R> arr) {int length = arr.size();// 组合由多少个元素组成的List<List<R>> result = new ArrayList<>();int i = 1;while (i <= length) {// 生成i个元素的组合result.addAll(combinationSelect(arr, i));i++;}return result;}/*** 由n个元素组成的组合* * @param arr 数组* @param i 组合的元素个数* @return 组合集合*/private static <R extends Comparable<R>> List<List<R>> combinationSelect(List<R> arr, int i) {return new DFSCombination<>(arr, i).select();}public static <R extends Comparable<R>> HashMap<String, Integer> getCombinationMap(List<Coupon> couponList) {List<List<Coupon>> allCombination = allCombination(couponList);HashMap<String, Integer> map = new HashMap<>();for (List<Coupon> coupons : allCombination) {List<String> collect = coupons.stream().map(coupon -> String.valueOf(coupon.getId())).collect(Collectors.toList());int total = coupons.stream().mapToInt(coupon -> coupon.getAmount().intValue()).sum();map.put(String.join("-", collect), total);}return map;}// 筛选最接近支付金额的结果public static int binarysearchKey(Object[] array, BigDecimal targetNum) {Arrays.sort(array);int left = 0, right = 0;for (right = array.length - 1; left != right;) {int midIndex = (right + left) / 2;int mid = (right - left);int midValue = (Integer) array[midIndex];if (targetNum.compareTo(new BigDecimal(midValue)) == 0) {return midValue;}if (targetNum.compareTo(new BigDecimal(midValue)) == 1) {left = midIndex;} else {right = midIndex;}if (mid <= 1) {break;}}int rightnum = ((Integer) array[right]).intValue();int leftnum = ((Integer) array[left]).intValue();// 如果一个大于支付金额,一个小于支付金额,优先返回大于支付金额的结果if (new BigDecimal(rightnum).compareTo(targetNum) == 1 && new BigDecimal(leftnum).compareTo(targetNum) == -1) {return rightnum;}BigDecimal rightiffVal = new BigDecimal(rightnum).subtract(targetNum);BigDecimal leftDiffVal = new BigDecimal(leftnum).subtract(targetNum);int ret = Math.abs(leftDiffVal.intValue()) > Math.abs(rightiffVal.intValue()) ? rightnum : leftnum;// System.out.println("要查找的数:" + targetNum + "最接近的数:" + ret);return ret;}/*** DFS实现组合*/private static class DFSCombination<R extends Comparable<R>> {// 标记元素是否已被组合private Set<R> bookSet = new HashSet<>();private List<R> arr;private int n;private Map<Integer, R> bucks;private List<List<R>> result = new ArrayList<>();public DFSCombination(List<R> arr, int n) {this.arr = arr;this.n = n;bucks = new LinkedHashMap<>();}private void dfs(int index) {if (index == n) {// 说明组合数满了result.add(composite());return;}for (int i = 0; i < arr.size(); i++) {R element = arr.get(i);if (!bookSet.contains(element)) {if (index > 0) {// 保证一个组合的顺序,从小到大的顺序R lastElement = bucks.get(index - 1);if (lastElement.compareTo(element) > 0) {continue;}}// 第几个位置放置什么元素bucks.put(index, element);bookSet.add(element);dfs(index + 1);bookSet.remove(element);}}}public List<List<R>> select() {dfs(0);return result;}private List<R> composite() {return bucks.entrySet().stream().map(Map.Entry::getValue).collect(Collectors.toList());}}}

demo

public class TestOptimumCoupon {public static void main(String[] args) {// 用户优惠券列表List<Coupon> couponList = null;// 支付金额BigDecimal money = null;List<Coupon> optimumList = getOptimumList(couponList, money);}private static List<Coupon> getOptimumList(List<Coupon> couponList, BigDecimal amount) {// 现金券集合Map<Integer, List<Coupon>> cashMap = new HashMap<>();// 代金券集合List<Coupon> voucherList = new ArrayList<>();// 折扣券集合List<Coupon> discountList = new ArrayList<>();for (Coupon coupon : couponList) {if ("CASH".equals(coupon.getCouponType())) {// 如果是现金券,对不同批次进行筛选if (cashMap.get(coupon.getBatchId()) == null) {List<Coupon> cacheList = new ArrayList<>();cacheList.add(coupon);cashMap.put(coupon.getBatchId(), cacheList);} else {List<Coupon> cacheList = cashMap.get(coupon.getBatchId());cacheList.add(coupon);}}if ("VOUCHER".equals(coupon.getCouponType())) {voucherList.add(coupon);}if ("DISCOUNT".equals(coupon.getCouponType())) {discountList.add(coupon);}}// endResultMap 同批次现金券最优解,折扣券最优解,代金券最优解// endResultMap key 优惠券id字符串 value 最优解优惠金额HashMap<String, Integer> endResultMap = new HashMap<>();// 判断现金券map长度 现金券处理开始if (!cashMap.isEmpty()) {// 筛选现金券不同批次最优解for (Map.Entry<Integer, List<Coupon>> m : cashMap.entrySet()) {// 获取当前批次所有组合HashMap<String, Integer> map = Combinations.getCombinationMap(m.getValue());// 获取当前批次最优解int result = getOptimumVal4Map(map, amount);// 根据value找最优解优惠券id,放入最优解mapgetOptimumMap4SameBatch(map, result, endResultMap);// 不同批次现金券最优解合集}// 现金券处理结束}// 代金券处理开始 判断长度if (voucherList.size() > 0) {voucherList.sort((Coupon c1, Coupon c2) -> c1.getAmount().compareTo(c2.getAmount()));// 获取代金券最大值,放入最终结果mapendResultMap.put(voucherList.get(voucherList.size() - 1).getId() + "",voucherList.get(voucherList.size() - 1).getAmount().intValue());// 代金券处理结束}// 折扣券处理开始 判断长度if (discountList.size() > 0) {discountList.sort((Coupon c1, Coupon c2) -> c1.getAmount().compareTo(c2.getAmount()));// 获取折扣最小值 并计算最大折扣金额BigDecimal discountTmp = discountList.get(0).getAmount();BigDecimal realCountTmp = new BigDecimal(100).subtract(discountTmp);BigDecimal discountTmpVal = amount.multiply(realCountTmp).divide(new BigDecimal(100));endResultMap.put(discountList.get(0).getId() + "", discountTmpVal.intValue());// 折扣券处理结束}// 获取最终结果int endResult = getOptimumVal4Map(endResultMap, amount);// 获取优惠券id集合字符串String couponDetailStr = getOptimumStr(endResultMap, endResult);// 判断是否为空List<String> couponDetailIdList = new ArrayList<>();if (StringUtils.isNotEmpty(couponDetailStr)) {if (couponDetailStr.contains("-")) {String[] couponDetailArr = couponDetailStr.split("-");for (String str : couponDetailArr) {couponDetailIdList.add(str);}} else {couponDetailIdList.add(couponDetailStr);}}List<Coupon> optimumList = new ArrayList<>();// 循环用户优惠券列表,进行比较for (Coupon coupon : couponList) {for (String str : couponDetailIdList) {if (coupon.getId() == Integer.parseInt(str)) {optimumList.add(coupon);}}}return optimumList;}private static int getOptimumVal4Map(HashMap<String, Integer> map, BigDecimal amount) {ArrayList<Integer> array = new ArrayList<Integer>();for (Map.Entry<String, Integer> m2 : map.entrySet()) {array.add(m2.getValue());}int result = Combinations.binarysearchKey(array.toArray(), amount);return result;}private static void getOptimumMap4SameBatch(HashMap<String, Integer> map, int result,HashMap<String, Integer> endResultMap) {for (Map.Entry<String, Integer> m : map.entrySet()) {if (m.getValue().intValue() == result) {endResultMap.put(m.getKey(), m.getValue());return;}}}private static String getOptimumStr(HashMap<String, Integer> map, int result) {for (Map.Entry<String, Integer> m : map.entrySet()) {if (m.getValue().intValue() == result) {return m.getKey();}}return null;}
}

Java 优惠券最优使用实现相关推荐

  1. Java性能调优实践

    作者:张 俊城, 郭 理勇, 和 刘 建 https://www.ibm.com/developerworks/cn/java/j-lo-performance-tuning-practice/ind ...

  2. 为什么对 Java 性能调优最后都像在调 you?

    不知道你有没有发现,优化Java,或者任何其他语言的代码性能经常被当做是一种暗黑艺术. 性能分析有种神秘感.画面类似是这样的:一个「黑客」经过多年练就的手艺,能够快速深入了解某个系统,并提出神奇的解决 ...

  3. 成为Java GC专家(5)—Java性能调优原则

    2019独角兽企业重金招聘Python工程师标准>>> 这是"成为Java GC专家"系列的第五篇文章.在第一篇深入浅出Java垃圾回收机制中,我们已经学习了不同 ...

  4. 怎么对 Java 程序调优?不看别后悔!

    Java 应用性能优化是一个老生常谈的话题,典型的性能问题如页面响应慢.接口超时,服务器负载高.并发数低,数据库频繁死锁等. 尤其是在"糙快猛"的互联网开发模式大行其道的今天,随着 ...

  5. 11 个简练的 Java 性能调优技巧

    转载自 11 个简练的 Java 性能调优技巧 想要让你的项目一直高性能运作吗?以下有一些技巧你可以拿去消除缓存瓶颈,还有一些其他的性能调优建议. 大多数开发者认为性能优化是一个复杂的话题,它需要大量 ...

  6. java 性能调优_Java性能调优调查结果(第四部分)

    java 性能调优 这是本系列中的最后一篇文章,我们将分析我们在2014年10月进行的Java Performance Tuning Survey的结果.如果您尚未阅读第一篇文章,建议您首先阅读以下内 ...

  7. java 性能调优_Java性能调优调查结果(第三部分)

    java 性能调优 这是本系列文章的第三篇,我们将分析2014年10月进行的调查的结果.如果您尚未这样做,我建议从本系列的前两篇文章开始: 问题严重性分析和监视域分析 . 这篇文章着重于故障排除/根本 ...

  8. java 性能调优_Java性能调优调查结果(第二部分)

    java 性能调优 这是系列文章的第二篇,我们将分析2014年10月进行的性能调整调查的结果.如果您尚未阅读第一部分,我们建议从此处开始 . 第二部分将重点监视Java应用程序的性能问题. 特别是,我 ...

  9. java 性能调优_Java性能调优调查结果(第一部分)

    java 性能调优 我们在2014年10月进行了Java性能调优调查.该调查的主要目的是收集对Java性能世界的见解,以改善Plumbr产品. 但是,我们也很高兴与您分享有趣的结果. 我们收集的数据为 ...

最新文章

  1. 【哈渡谱】带你玩转Hadoop之《CentOS虚拟机安装篇》
  2. java for stl_STL迭代器
  3. 官方文档,才是正途-docker-compose
  4. Linux 高可用(HA)集群之keepalived详解
  5. macsv服务器状态,和利时macs问题(启动服务器无法读出控制器的点名)
  6. python 读写utf8文件_Python关于 文件读写的总结
  7. ubuntu安装python编译器_Ubuntu中安装VIM编辑器
  8. 使用R语言的正确姿势,R包干货奉献
  9. autocad.net QQ群:193522571 判断string中是否包含集合中所有的字符串
  10. java plugin firefox_Firefox 中使用 Java 插件
  11. Layui表单验证lay-verify属性
  12. 史上最详细解释接口自动化测试中cookie、session和token的区别【多测师_王sir】
  13. 8051蜂鸣器程序c语言,单片机驱动蜂鸣器C51程序
  14. SSM——SpringMVC笔记1
  15. 计算机与英语教学的论文开题报告,英语教学法毕业论文开题报告范文
  16. 腾讯MSDK手Q微信授权登录
  17. 青龙面板 Bot配置教程
  18. java9新特性(简述十大新特性)
  19. 在VMware虚拟机上安装统信UOS系统
  20. 考研数据结构——(查找)

热门文章

  1. 使用计算机室刷卡上机,浅析高校计算机实验室综合信息管理系统
  2. python:实现叠加图片(附完整源码)
  3. 44 - 操作MySQL数据库
  4. java8 合并两个list日期相同为一条
  5. 入门级蛋白磷酸化研究
  6. 2022年国自然正式放榜,如何解读基金立项结果?(附查询方法)
  7. 证明:因为矩阵A非0,所以AA'也非零
  8. 手写max,min,abs函数
  9. Android进阶之路 - StringUtils、NumberUtils 场景源码
  10. 重学JavaWeb(11)JDBC