python3和5_Python3算法之五:最大子序和
关注微信公众号“酸痛鱼”,获得更多最新最全的文章。
本文中所涉及的代码,在未特殊声明的情况下,都是基于Python3程序设计语言编写的。
建议您在PC浏览器中阅读本文,以获得更好的阅读体验。
如果您未掌握知识提要中的内容,建议您先掌握这些内容之后再阅读本文。
知识提要
1、内置函数max、range、len
2、贪心算法
3、动态规划
4、分治法
5、时间复杂度、空间复杂度
0
最大子序和
因为Python切片语法中,array[i:j]表示从第i个元素到第j个元素(不包含j)的子 集。在本文所有表述中,我们将沿用这个语言规则,并不再额外说明。
给定一个数组array,其长度为n。对于任意非负整数i和j,满足0<=i
如果array是一个整数数组,我们将array[i:j]中所有元素的和称为array在i与j间的子序和。我们将值最大的子序和称为最大子序和。
例如:
输入:[1, 2, 3]
输出:6
输入:[-1, -2, -3]
输出:-1
解释:最大子序是[-1]
输入:[-4, 2, -1, 2, -1]
输出:3
解释:最大子序为[2, -1, 2]
本课题所有文章的特点是,尝试从多种角度,利用多种方法去解答一个题目。贪心算法是本题最简单且最优化的解法。如果你对其它几种解法不太理解,也没有关系,可以把阅读的重心放在贪心算法的解题思路上。
1
最大前缀和
对于下标i,以i为终点的子序有array[0:i+1], array[1:i+1], array[2,i+1], …, array[i,i+1]。我们将这些子序中,和最大者,称为i的最大前缀和,记为S(i)。
还是再强调一下,我们沿用了Python的语法,所以这些子序都不包含array[i+1]。我们将在下文中不停用到最大前缀和,并直接用S(i)表示,所以请先务必理解S(i)的含义。
2
直观解法
假设我们已经求得一个局部子序和cur_sum,它是array[i:j]的和,也就是cur_sum=S(j-1),且只考虑cur_sum>=0的情况。我们用max_sum保存全局最优解。那么对于下一个元素,只需考虑并处理两种如下情况:
A、如果array[j]>=0,那么我们array[i:j+1]必是一个更优解;于是我们可以把array[j]累加到cur_sum中,保存cur_sum与max_sum之较大者到max_sum,并继续往前走。相当于如果array[j] >= 0, S(j) = S(j-1) + S(j)。
B、如果array[j]<0,那么cur_sum必是一个局部最优解,保存cur_sum与max_sum之较大者到max_sum。之后,我们将array[j]累加到cur_sum上,如果cur_sum>=0,那么可以断续向前探索;如果cur_sum<0,则放弃array[i:j+1]的结果,即令cur_sum=0,并从array[j+1]开始探索。
当然了,因为我们的题目并没有要求我们输出i和j的值,只需要返回最大的cur_sum,即max_sum,所有我们的代码中并不需要记录i和j。由于只遍历了一遍array,所以此解法的时间复杂度为O(N)。
此解法虽然叫直观解法,但它需要处理多个边界条件,并在每次修改cur_sum后,判定是否要将cur_sum替换到max_sum。不仅如此,还需要考虑max_sum与array[j]的大小关系。不过为了体现后面解题思路的巧妙性,我还是尝试并实现了这种解法。如果你不太理解上面的描述,或者不太理解以下的代码,请不要太纠结。我在编写这个代码的时候,也是经常几次修改,才最终把各种边界条件处理妥当的。
def maxSubArraySum(array):
if not array:
return 0
max_sum = array[0]
cur_sum = 0
for v in array:
if v >= 0:
cur_sum += v
max_sum = max(max_sum, cur_sum)
else:
if cur_sum > 0:
max_sum = max(max_sum, cur_sum)
elif v > max_sum:
max_sum = v
cur_sum += v
if cur_sum < 0:
cur_sum = 0
return max_sum
3
贪心算法
贪心算法是一个更为容易理解的思路。假设我们已经求得一个局部子序和cur_sum,其初始值是array[0]。对于每第i个元素,cur_sum与cur_sum+array[i]的较大者,就是处理完元素i之后的局部最优解。每次用max_sum存储max_sum和cur_sum之较大者,最终就可以得到全局最优解。
总结成人话就是:S(i)=max(S(i), S(i-1) + array[i])
def maxSubArraySum(array):
if not array:
return 0
max_sum = array[0] # 全局最优解
cur_sum = max_sum # 局部最优解
for i in range(1, len(array)):
v = array[i]
cur_sum = max(v, cur_sum + v)
max_sum = max(max_sum, cur_sum)
return max_sum
4
动态规划
动态规划最在局部最优解中求得全局最优解的通用方法。
动态规划和贪心算法最大的区别在于,贪心算总是只记录当前局部最优解和全局最优解;而动态规划则记录所有的局部最优解。由于巧合,本题目用贪心算法和动态规则的代码实现很接近,但读者要明白它们的核心思路和应用场景是不一样的。
我们用长度为n的数组solution来存储我们的最大前缀和,即solution[i]=S(i);数组solution中最大的值,就是我们的解。
当然了,其实我们并不需要真的创建一个solution数组来存储局部解,我们可以在计算过程中不停地改变array[i]值,用array[i]来存储solution[i]即可。不过为了方便理解,我们还是会在代码中引入solution。读者把solution替换成array,一样可以得到正确的解。
def maxSubArraySum(array):
if not array:
return 0
solution = array[:] # solution[i] 保存0到i为止的局部最优解
for i in range(1, len(array)):
solution[i] = max(solution[i - 1] + array[i], array[i])
return max(solution)
上面的解法中,最后一行代码 return max(solution)对solution进行了一次遍历,找到最大的S(i)以得到最终的解。但事实上,我们在求解solution的每个元素的过程中,就可以顺便把最终的解记录下来,以减少最后面的遍历操作。优化之后的代码如下:
def maxSubArraySum(array):
if not array:
return 0
solution = array[:] # solution[i] 保存0到i为止的局部最优解
max_sum = array[0] # 全局最优解
for i in range(1, len(array)):
solution[i] = max(solution[i - 1] + array[i], array[i])
max_sum = max(max_sum, solution[i])
return max_sum
对比贪心算法,这个优化过的动态规划实现的代码跟贪心算法很像,大家肯定会觉得solution多此一举。的确是如此,我们再优化一下,把solution去掉,就是贪心算法的实现了。
5
分治法
将一个大问题,分解成一系列子问题,并将这些子问题合并得到最终的解。分治法在不同的应用场景中,解题思路稍微有一些不同。但基本的要求都是要可以将一个问题分成若干子问题来求解,并在这些子问题中可以求得或者合并成最终的解。
套用到我们的题目中,其基本思路是:
对于array的子集array[left:right],给定下标m(left<=m
1、左子集array[left:m+1] 中的最优解(分治左子集)
2、右子集array[m+1:right]中的最优解(分治右子集)
3、flank_sum
在实际解题的过程中,我们可以将array两等分成左右两个子集求解;其左右子集再以同样的思路再进一步切分求解。所以我们需要用到递归的方式。分治法的时间复杂度为O(NlogN),空间复杂度为O(logN)。
def flankSum(array, left, right, p):
# 在 [left, right]范围内,求包含P元素的最大连续子序和
#
if left == right:
return array[left]
left_sum = array[p]
cur_sum = 0
for i in range(p, left - 1, -1):
cur_sum += array[i]
left_sum = max(left_sum, cur_sum)
right_sum = array[p + 1]
cur_sum = 0
for i in range(p + 1, right + 1):
cur_sum += array[i]
right_sum = max(right_sum, cur_sum)
return left_sum + right_sum
def devideAndConquer(array, left, right):
if left == right:
return array[left]
p = (left + right) // 2
left_result = devideAndConquer(array, left, p) # 分治求P左侧的解
right_result = devideAndConquer(array, p + 1, right) # 分治求P右侧的解
flank_sum = flankSum(array, left, right, p) # 求含P元素的最大连续子序和
return max(left_result, right_result, flank_sum)
def maxSubArraySum(array):
size = len(array)
if size <= 1: array[0]
return devideAndConquer(array, 0, size - 1)
● 扫码关注我嗄嘎嘎
酸痛鱼,与你分享快乐的代码
python3和5_Python3算法之五:最大子序和相关推荐
- C++ 算法设计 最大子序和问题
测试用例:1,2,3,4,-2,3,-5,6,-1,-1,1 答案:12 输出: 优化算法:12 普通算法:12 请按任意键继续. . . #include<iostream> using ...
- Java算法之最大子序和
class Solution {public int maxSubArray(int[] nums) {int sum=0;int ret=Integer.MIN_VALUE;int len = nu ...
- LeetCode53:最大子序和(分治思想,Python3实现)
最大子序和 给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和. 示例: 输入: [-2,1,-3,4,-1,2,1,-5,4], 输出: 6 解释: ...
- leetcode - 53. 最大子序和 152. 乘积最大子序列 - 两个算法之间的联系和区别
这两道算法题的解题思路是差不多的,但是从整体上分析,乘积最大子序列之和是最大子序和的进阶.先来看看两道算法题的简单描述. 53.最大子序和 给定一个整数数组 nums ,找到一个具有最大和的连续子数组 ...
- 力扣贪心算法专题(一)455.分发饼干 376. 摆动序列 53. 最大子序和 122.买卖股票的最佳时机II 1005.K次取反后最大化的数组和 思路及C++实现 贪心算法 动态规划
文章目录 贪心算法 455.分发饼干 思路 步骤 代码 376. 摆动序列 贪心算法 思路 分析 代码 动态规划 思路 步骤 代码 53. 最大子序和 暴力解法 双层for循环 贪心算法 思路 分析 ...
- 伍六七带你学算法 入门篇 ——最大子序和
力扣 53. 最大子序和 难度简单 给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和. 示例: 输入: [-2,1,-3,4,-1,2,1,-5,4 ...
- python【力扣LeetCode算法题库】53- 最大子序和
最大子序和 给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和. 示例: 输入: [-2,1,-3,4,-1,2,1,-5,4], 输出: 6 解释: ...
- LeetCode-53. 最大子序和-最简单的动态规划(Python3)
题目链接: 53.最大子序和 题目描述: 给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和. 示例: 输入: [-2,1,-3,4,-1,2,1,- ...
- [剑指offer]面试题第[42]题[Leedcode][JAVA][第53题][最大子序和][动态规划][贪心][分治]
[问题描述][第53题][最大子序和][中等] 给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和.示例:输入: [-2,1,-3,4,-1,2,1, ...
最新文章
- python 线程安全链表_教你用 Python 实现 HashMap 数据结构
- Bluetooth ATT介绍
- 新书首发 | 《机器学习 公式推导与代码实现》正式出版!(文末送书)
- bootstrap_bootstrap中日历范围选择插件daterangepicker的使用
- 腾讯“抢”小米黑鲨做元宇宙?
- conda安装tensorflow-gpu简洁版_【DP系列教程02】DeePMDkit:conda安装 amp; 离线安装
- [转]恢复 git reset -hard 的误操作
- python3.x中pip升级报错
- EasyUI笔记(六)数据表格
- 【图像隐写】基于matlab GUI DCT数字水印嵌入+提取【含Matlab源码 1671期】
- iOS 录音功能实现
- 网络安全--通过握手包破解WiFi(详细教程)
- 学习云计算需要哪些软件,需要什么知识面?
- matlab兔子问题,【matlab】狼追击兔子问题的建模
- “M1芯片”电脑杀手:Windows笔记本电脑目前岌岌可危
- 计算机临时保存信息,Windows临时文件夹是什么,Windows临时文件夹保存位置在哪里?...
- 自定义Android视频播放器 - 切换横竖屏
- cJSON详细剖析(四)----cJSON_Print()函数
- 如何免费获取IEEE论文,亲测有效,【分享给有需要的人】
- nginx反向代理实现二级域名转一级域名