首页
技术小册
AIGC
面试刷题
技术文章
MAGENTO
云计算
视频课程
源码下载
PDF书籍
「涨薪秘籍」
登录
注册
01 | 合格程序员的第一步:算法与数据结构
02 | 如何事半功倍地学习算法与数据结构
03 | 如何计算算法的复杂度
04 | 如何通过LeetCode来进行算法题目练习
05 | 理论讲解:数组&链表
06 | 面试题:反转一个单链表&判断链表是否有环
07 | 理论讲解:堆栈&队列
08 | 面试题:判断括号字符串是否有效
09 | 面试题:用队列实现栈&用栈实现队列
10 | 理论讲解:优先队列
11 | 面试题:返回数据流中的第K大元素
12 | 面试题:返回滑动窗口中的最大值
13 | 理论讲解:哈希表
14 | 面试题:有效的字母异位词
15 | 面试题:两数之和
16 | 面试题:三数之和
17 | 理论讲解:树&二叉树&二叉搜索树
18 | 面试题:验证二叉搜索树
19 | 面试题:二叉树&二叉搜索树的最近公共祖先
20 | 理论讲解:二叉树遍历
21 | 理论讲解:递归&分治
22 | 面试题:Pow(x,n)
23 | 面试题:求众数
24 | 理论讲解:贪心算法
25 | 面试题:买卖股票的最佳时机
26 | 理论讲解:广度优先搜索
27 | 理论讲解:深度优先搜索
28 | 面试题:二叉树层次遍历
29 | 面试题:二叉树的最大和最小深度
30 | 面试题:生成有效括号组合
31 | 理论讲解:剪枝
32 | 面试题:N皇后问题
33 | 面试题:数独问题
34 | 理论讲解:二分查找
35 | 面试题:实现一个求解平方根的函数
36 | 理论讲解:字典树
37 | 面试题:实现一个字典树
38 | 面试题:二维网格中的单词搜索问题
39 | 理论讲解:位运算
40 | 面试题:统计位1的个数
41 | 面试题:2的幂次方问题&比特位计数问题
42 | 面试题:N皇后问题的另一种解法
43 | 理论理解:动态规划(上)
44 | 理论理解:动态规划(下)
45 | 面试题:爬楼梯
46 | 面试题:三角形的最小路径和
47 | 面试题:乘积最大子序列
48 | 面试题:股票买卖系列
49 | 面试题:最长上升子序列
50 | 面试题:零钱兑换
51 | 面试题:编辑距离
52 | 理论讲解:并查集
53 | 面试题:岛屿的个数&朋友圈(上)
54 | 面试题:岛屿的个数&朋友圈(下)
55 | 理论讲解: LRU Cache
56 | 面试题:设计和实现一个LRU Cache缓存机制
57 | 理论讲解:布隆过滤器
当前位置:
首页>>
技术小册>>
算法面试通关 50 讲
小册名称:算法面试通关 50 讲
### 47 | 面试题:乘积最大子序列 在算法面试中,动态规划(Dynamic Programming, DP)是一个极其重要的概念,它不仅能够帮助我们解决复杂问题,还能提升解题的效率和思维深度。其中,“乘积最大子序列”问题是动态规划应用中的一个经典案例,它要求我们在一个整数数组中找出一个连续子序列,使得该子序列中所有元素的乘积达到最大。这个问题看似简单,实则蕴含了多种边界情况和优化策略,是考察面试者算法设计和分析能力的一个好题目。 #### 问题描述 给定一个整数数组 `nums`,找到一个具有最大乘积的连续子数组(至少包含一个元素),返回该子数组的最大乘积。 #### 示例 ```plaintext 输入: nums = [2,3,-2,4] 输出: 6 解释: 子数组 [2,3] 有最大乘积 6。 输入: nums = [-2,0,-1] 输出: 0 解释: 结果不能为 2, 因为 [-2,-1] 的乘积是负数,而子数组必须至少包含一个元素。 ``` #### 分析 首先,我们需要明确几个关键点: 1. **负数乘以负数得正数**:这意味着,如果当前元素是负数,并且之前的乘积也是负数,那么将当前元素加入乘积可能会使结果变得更大。 2. **零的特殊性**:任何数与零相乘都等于零,这可能会中断当前的乘积增长序列。 3. **动态规划的应用**:我们需要记录到当前位置为止的最大乘积和最小乘积(因为考虑到负数的存在),以便在遍历过程中更新这两个值。 #### 解决方案 基于以上分析,我们可以设计如下动态规划算法: - 定义两个变量 `maxProduct` 和 `minProduct` 分别表示到当前位置为止的最大乘积和最小乘积(注意,初始时,它们都应该被初始化为数组的第一个元素,因为子序列至少包含一个元素)。 - 遍历数组 `nums`,对于每个元素 `num`,更新 `maxProduct` 和 `minProduct`。 - 更新 `maxProduct` 时,需要考虑两种情况:一是当前元素 `num` 自身(如果 `num` 大于之前的 `maxProduct`),二是将 `num` 与之前的 `maxProduct` 或 `minProduct` 相乘(因为负数乘以负数可能得到更大的正数)。 - 更新 `minProduct` 时,同样考虑两种情况:一是当前元素 `num` 自身(如果 `num` 小于之前的 `minProduct`),二是将 `num` 与之前的 `maxProduct` 或 `minProduct` 相乘(因为负数乘以正数可能得到更小的负数)。 - 遍历过程中,同时维护一个全局的最大乘积 `globalMaxProduct`,用于记录遍历过程中的最大乘积。 #### 代码实现 下面是该算法的 Python 代码实现: ```python def maxProduct(nums): if not nums: return 0 maxProduct = minProduct = nums[0] globalMaxProduct = nums[0] for num in nums[1:]: # 临时保存当前maxProduct和minProduct的值 prevMax = maxProduct prevMin = minProduct # 更新maxProduct和minProduct maxProduct = max(num, prevMax * num, prevMin * num) minProduct = min(num, prevMax * num, prevMin * num) # 更新全局最大乘积 globalMaxProduct = max(globalMaxProduct, maxProduct) return globalMaxProduct # 测试样例 print(maxProduct([2,3,-2,4])) # 输出: 6 print(maxProduct([-2,0,-1])) # 输出: 0 ``` #### 复杂度分析 - **时间复杂度**:O(n),其中 n 是数组 `nums` 的长度。我们只需要遍历一次数组即可。 - **空间复杂度**:O(1)。我们只使用了常数个变量来存储中间结果,没有使用与输入规模相关的额外空间。 #### 进一步优化与思考 虽然上述算法已经非常高效且易于理解,但在实际面试中,面试官可能还会进一步询问是否有其他方法或优化思路。例如,可以尝试使用分治法(Divide and Conquer)来解决这个问题,虽然分治法的时间复杂度也是 O(n),但其思想更加复杂,且在某些情况下(如数组中包含大量零时)可能不是最优选择。 此外,还可以思考如何处理数组中存在大量负数或零的情况,以及这些特殊情况如何影响算法的选择和效率。通过这些深入思考,可以进一步展现面试者的算法素养和问题解决能力。 总之,“乘积最大子序列”问题是一个典型的动态规划应用案例,它不仅考察了面试者对动态规划的理解和应用能力,还通过负数、零等特殊情况考验了面试者的边界条件处理能力和思维深度。希望通过本文的讲解,读者能够对该问题有更深入的理解和掌握。
上一篇:
46 | 面试题:三角形的最小路径和
下一篇:
48 | 面试题:股票买卖系列
该分类下的相关小册推荐:
数据结构与算法(下)
业务开发实用算法精讲
数据结构与算法之美
编程之道-算法面试(上)
编程之道-算法面试(下)
数据结构与算法(中)
数据结构与算法(上)