【递归】部分和问题

作者: 一字马胡
转载标志 【2017-12-10】

更新日志

日期 更新内容 备注
2017-12-10 学习递归 关于递归的算法问题

问题描述

给定一个长度为n的整数数组a1,a2,a3....,判断是否可以从这组数中挑选若干个数,使得这些被挑选的数的和恰好为k。

限制条件:

  • 1 <= n <= 20
  • -10^8 <= ai <= 10^8
  • -10^8 <= k <= 10^8

题意分析

这是一种可以提取共性的问题,也就是说题目给出了一组元素(Element),而你的程序需要对每个元素进行判断,判断这个元素是否可选,接下来你需要分析,如果一个元素可选,会对结果造成什么影响,如果不可选呢?说到这里,你应该想到了一棵二叉树了吧,每个节点(Node)就是一个Element,而它的两个分叉就是可选和不可选两种状态下的结果。下面展示了一个简单的思路图:

【递归】部分和问题_第1张图片
某种递归模型

在上面的图片中,Root Node属于choose动作的起点,在这里可以进行一些初始化,然后开始进行选择,如果选择当前Element,则状态转移到Check Node,否则转移到UnChecked Node,然后到达相应的Node上之后再进行一些状态更新,然后做同样的处理,很明显,这里面有递归的过程。

对于本例子中的问题,从第一个Element开始进行选择,每一个Element都是可选或者不可选两种状态,我们应该全局持有一个sum,标志这目前的状态,并且每次进行选择之后都需要做一下判断,如果已经满足要求,那么就应该立刻返回,结束递归,这也叫剪枝,当然,还可以做一些其他的剪枝,比如选择了某个Element之后,sum和大于要求了,那么该路径肯定不符合要求,没有必要递归下去了,可以立刻结束。

基于上面的分析,本例使用上面的算法时间复杂度为O(2^n)。

问题解决

下面是该问题的某个解决方案,使用java语言:


package io.hujian.common;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

/**
 * project: algo-collection
 * package: io.hujian.common
 * Author: 

hujian

* Created on * 2017/12/3 10:13. *

* #Rich Comment layer# * */ public class partSumProblem { public static void main(String[] args) { int[] array = new int[]{3, 5, 9, 10, 2, 4}; List chooseEleList = new ArrayList(); boolean result = new Solver(array, 17).check(0, 0, chooseEleList); System.out.println(result); chooseEleList.forEach(new Consumer() { public void accept(Integer integer) { System.out.print(integer + " "); } }); } } class Solver { private final int[] array; private final int length; private final int requireSum; public Solver(int[] array, int requireSum) { this.array = array; this.length = array.length; this.requireSum = requireSum; } /** * 递归检测是否已经符合要求了 * * @param index 当前的choose Element * @param partSum 当前的sum * @return 客服满足要求 */ public boolean check(int index, int partSum, List chooseEleList) { if (index == length) { // 已经到头了,防止数组越界,只能返回了 return partSum == requireSum; } if (partSum == requireSum) { // 判断是否满足要求了 return true; } if (partSum > requireSum) { // 是否可以剪枝 return false; } /* 试试check这条路 */ if (check(index + 1, partSum, chooseEleList)) { return true; } /* 试试uncheck这条路 */ if (check(index + 1, partSum + array[index], chooseEleList)) { chooseEleList.add(array[index]); return true; } /* 无路可走了 */ return false; } }

如果有这样的路径,那么会将选择出来的Element打印出来。更为高效的解放将在未来进行补充。

你可能感兴趣的:(【递归】部分和问题)