使用递归找到最大产品
Posted
技术标签:
【中文标题】使用递归找到最大产品【英文标题】:Find maximum product using recursion 【发布时间】:2020-07-17 22:32:25 【问题描述】:我看到了一个问题,我想知道是否可以使用递归来解决它。如下:
编写一个算法,当给定一个输入数组时,从这些输入中找到最大乘积。例如:
Input: [1, 2, 3]
Output: 6 (1*2*3)
Input: [-1, 1, 2, 3]
Output: 6 (1*2*3)
Input: [-2, -1, 1, 2, 3]
Output: 12 (-2*-1*1*2*3)
我正在尝试找到一种使用递归来解决它的方法,但是我尝试的算法不起作用。我用Java编写的算法如下
Integer[] array;
public int maximumProduct(int[] nums)
array=new Integer[nums.length];
return multiply(nums, 0);
public int multiply(int[] nums, int i)
if (array[i]!=null)
return array[i];
if (i==(nums.length-1))
return nums[i];
int returnval=Math.max(nums[i]*multiply(nums, i+1), multiply(nums, i+1));
array[i]=returnval;
return returnval;
这个算法的问题是,如果有偶数个负数,它就不能很好地工作。例如,如果 nums[0]=-2、nums[1]=-1 和 nums[2]=1,则 multiply(nums, 1) 将始终返回 1 而不是 -1,因此它始终会看到 1在 multiply(nums, 0) 处大于 1*-2。但是,我不确定如何解决这个问题。有没有办法使用递归或动态编程来解决这个问题?
【问题讨论】:
如果你被限制为 int,计算负整数的个数。如果为奇数,则丢弃最小的负整数,否则将所有其他整数相乘以获得最大的乘积。首先删除所有零。不需要递归。 是递归还是双for循环也可以? 我希望使用递归,只是因为我想通过递归来提高我的技能,这一直是我的弱点。 【参考方案1】:如果数组中只有一个非零元素,并且它恰好是一个负数,那么答案是 0,如果输入中存在 0,或者如果数组只包含那个否定元素,答案就是那个元素本身。
在所有其他情况下,最终答案都是肯定的。
我们首先进行线性扫描以找到负整数的个数。如果这个数字是偶数,那么答案是所有非零元素的乘积。如果有奇数个否定元素,我们需要从答案中去掉一个否定元素,这样答案就是肯定的。由于我们想要最大可能的答案,我们想要遗漏的数字应该具有尽可能小的绝对值。所以在所有的负数中,找到绝对值最小的那个,然后找到剩下的非零元素的乘积,应该就是答案了。
所有这些只需要对数组进行两次线性扫描,因此运行时间为 O(n)。
【讨论】:
我把 OP 的“我不确定如何解决这个问题”表示他们无法解决整个问题,但在第二次阅读时,我猜他们指的是特定的他们在递归解决方案中发现的问题。我的错。我应该删除这个答案吗?不确定协议。 不。就留着吧。有人会发现它很有用。 如果需要使用递归,则需要两个函数,一个查找最小值,一个查找最大值或剩余元素。首先,让它在没有任何优化的情况下工作,例如小于一、负数等,同时调用最小值和最大值,看看是否乘以最小值和最大值,这四个中哪个给出最大值。最少做类似(但相反)。【参考方案2】:整数的最大乘积是多少?
要获得最大和,您需要将所有正整数与最大负整数的乘积相乘,乘积中包含的负整数个数是偶数,以获得正的最终结果。
在单次遍历算法中
我将分别处理输入中的正整数和负整数。您需要保留正整数的运行积、负整数的运行积和迄今为止找到的最大负整数(即绝对值最小的负整数)。 让我们忽略最终答案为
//Initialization
int [] nums // Input
int posProduct = 1;
int negProduct = 1;
int smallestNeg = 1;
//Input Traversal
for (int i : nums)
if ( i == 0 )
// ignore
else if ( i < 0 )
if (smallestNeg == 1)
smallestNeg = i;
else if ( i > smallestNeg )
negProduct *= smallestNeg; //Integrate the old smallest into the running product
smallestNeg = i; // i is the new smallest
else
negProduct *= i;
else
// i is strictly positive
posProduct *= i;
//Result Computation
int result = posProduct;
if ( negProduct < 0 )
// The running product of negative number numbers is negative
// We use the smallestNeg to turn it back up to a positive product
result *= smallestNeg;
result *= negProduct;
else
result *= negProduct
编辑:在递归遍历中
我个人发现以递归方式编写数组遍历很笨拙,但可以做到。 为了练习的美感并实际回答 OP 的问题,我将这样做。
public class RecursiveSolver
public static int findMaxProduct (int [] nums)
return recursiveArrayTraversal(1, 1, 1, nums, 0);
private static int recursiveArrayTraversal(int posProduct, int negProduct,
int smallestNeg, int [] nums, int index)
if (index == nums.length)
// End of the recursion, we traversed the whole array
posProduct *= negProduct;
if (posProduct < 0)
posProduct *= smallestNeg;
return posProduct;
// Processing the "index" element of the array
int i = nums[index];
if ( i == 0 )
// ignore
else if ( i < 0 )
if (smallestNeg == 1)
smallestNeg = i;
else if ( i > smallestNeg )
negProduct *= smallestNeg;
smallestNeg = i;
else
negProduct *= i;
else
// i is strictly positive
posProduct *= i;
//Recursive call here!
//Notice the index+1 for the index parameter which carries the progress
//in the array traversal
return recursiveArrayTraversal(posProduct, negProduct,
smallestNeg, nums, index+1);
【讨论】:
【参考方案3】:首先,在子问题中打破数组,总是在列表中找到 0:
1 -2 4 -1 8 0 4 1 0 -3 -4 0 1 3 -5
|_____________| |____| |____| |_______|
p1 p2 p3 p4
然后,对于每个问题pi
,计算其中有多少个负数。
如果pi
有偶数个负数(或根本没有负数),则pi
的答案是其所有元素的乘积。
如果pi
只有1个负数(比如n
),答案将是n
右边所有元素的乘积与n
中所有元素的乘积之间的最大值离开了。
如果pi
有一个奇数(大于仅1)的负数,则调用最左边的负数的索引l
和最右边的负数的索引r
。假设pi
有n
元素,答案将是:
max(
pi[ 0 ] * pi[ 1 ] * ... * pi[r - 1],
pi[l + 1] * pi[l + 2] * ... * pi[ n ]
)
知道了这一点,很容易为这个问题的解决方案的每一步编写一个递归:在 O(n) 中将问题划分为零,另一个用于计算负数,另一个用于寻找答案。
【讨论】:
【参考方案4】:线性版本
List<Integer> vals = new ArrayList<>(List.of(5,1,-2,1,2,3,-4,-1));
int prod = 0;
int min = 1;
for (int v : vals)
if (v == 0)
// ignore zero values
continue;
if (prod == 0)
prod = 1;
prod *= v;
// compute min to be the largest negative value in the list.
if (v < 0 && min < Math.abs(v))
min = v;
if (prod < 0)
prod /= min;
System.out.println("Maximum product = " + prod);
递归版本
int prod = prod(vals, new int[] 0 , vals.size());
System.out.println("Maximum product = " + prod);
public static int prod(List<Integer> vals, int[]min, int size)
int prod = 0;
if(vals.size() > 0)
int t = vals.get(0);
if (t < 0 && min[0] < Math.abs(t))
min[0] = t;
prod = prod(vals.subList(1,vals.size()), min, vals.size());
if (vals.isEmpty() || vals.get(0) == 0)
return prod;
if (prod == 0)
prod = 1;
prod *= t;
if (vals.size() == size && prod < 0)
prod/=min[0];
return prod;
【讨论】:
【参考方案5】:这是我的解决方案 - 将其打开以进行优化并计算运行时。这是一种通用解决方案,可在列表中找到所有整数组合的乘积。当然,有一个 O(n) 解决方案,但我也提出了这个解决方案。
import java.util.ArrayList;
import java.util.List;
public class MaxProd
int[] input = 1, 2, 3;
// int[] input = -2, -1, 1, 2, 3;
public static void main(String[] args)
MaxProd m = new MaxProd();
List<Integer> ll = m.max(0);
for (int i : ll)
System.out.println(i);
ll.sort((x,y) -> Integer.compare(x, y));
System.out.println("The max: " + ll.get(ll.size() -1 ));
private List<Integer> max(int index)
if (index < input.length)
List<Integer> l = new ArrayList<>();
List<Integer> retList = max(index + 1);
for (int j : retList)
l.add(input[index] * j);
l.add(input[index]);
l.addAll(retList);
return l;
else return new ArrayList<>();
打印出来:
6
2
3
1
6
2
3
The max: 6
如果需求受到限制(如本例所示),则无需生成所有组合即可获得线性解决方案。另外,我在最后进行排序。注意:您可以通过在返回的列表上单次传递来轻松获得结果,以找到其他答案中指定的最大产品。
【讨论】:
这看起来使用 O(n^2log(n)) 时间(如果不排序,则为 O(n^2)),而有一个 O(n) 解决方案。 @PaulHankin: 不是 2^n 吗? 您无需排序即可一次性获得产品。 @WJS:你是对的。这是处理所有组合的通用解决方案。我已经提到过了。我可以让它更清楚。以上是关于使用递归找到最大产品的主要内容,如果未能解决你的问题,请参考以下文章
office2007 激活时说产品密钥的激活次数已达到microsoft软件许可款的最大软件许可允许值