**本博客内容根据 Mooc 浙江大学 数据结构 1.3课 什么是算法一课总结获得
若是对于博客中的算法有任何疑问请您斧正**python
有一个序列N, 其中包含了 [A1,.... AN]个整数, 求解获得其中的最大连续子序列和。 例子: N: [1, 2, -1] 几个连续子序列: [1] [2] [-1] [1, 2] [2, -1] [1, 2, -1] 其中最大的连续子序列就是3
面对这个问题, 最暴力的方式就是,穷举获得全部的子序列,而后获得其中最大的子序列和。算法
def get_max_sequence_sum(nums: list) -> int: length = len(nums) this_sum = max_sum = 0 # 遍历子序列的起始index for first_index in range(length): # 遍历子序列的终止index for end_index in range(first_index, length): # 计算获得当前的子序列和 for num in nums[first_index: end_index+1]: this_sum += num max_sum = this_sum if this_sum > max_sum else max_sum this_sum = 0 return max_sum
对于这个算法, 在计算的过程当中进行了3次循环,T(n) = n ^ 3数据结构
明显能够发现,在计算子序列和的时候,能够经过上一个子序列和加上下一个整数获得新子序列和优化
def get_max_sequence_sum(nums: list) -> int: length = len(nums) this_sum = max_sum = 0 # 遍历子序列的起始index for first_index in range(length): # 遍历子序列的终止index for end_index in range(first_index, length): # 当前子序列和 为 上一个子序列和加上当前整数 this_sum += nums[end_index] max_sum = this_sum if this_sum > max_sum else max_sum this_sum = 0 return max_sum
经过对于子序列和的优化后, 能够减小每次计算子序列和的循环, T(n) = n ^ 2this
在进行减小循环的优化后,咱们还能够进一步的使用,分治法的思想来优化这个算法。spa
对于最大子序列和问题,咱们能够将问题分解成code
将序列按照中间分红左右两个序列 而后计算 左子序列的最大子序列和 右子序列的最大子序列和 以及从中间往两边扫描获得的最大子序列和
第一步 获得左右两边序列 [4, -3, 5, 2], [-1, 2, 6, 2] 第二步 对于左右序列计算最大子序列和 左序列继续分解成 [4, -1], [5, 2] 左左继续分解成 [4],[-1]; [5], [2] 获得左左左 序列和最大为 4 左左右 序列最大和为5 而后左序列[4, -3,|5, 2]从分割线往两边获得最大子序列和为6 所以左序列的最大子序列和为6 以此类推 右序列的最大子序列和为 8 第三步: 最初序列从分割线往左右两边扫描的最大序列和为11 第四步: 比较三个部分的和 获得最大值为11 (写起来, 比较复杂 QAQ ,若是看不懂你们能够看下参考课程老师的视频) 分治法的思路 就是不断分解问题 而后再将子问题的结果合起来获得最终结果
从而, 咱们能够获得第三种算法视频
def get_max_sequence_sum(nums: list) -> int: length = len(nums) middle_index = length // 2 middle_to_left_this_sequence_sum = middle_to_left_max_sequence_sum = 0 middle_to_right_this_sequence_sum = middle_to_right_max_sequence_sum = 0 # 递归终止条件 if length == 0: return 0 if length == 1: return nums[0] if nums[0] > 0 else 0 # 经过递归的方式获得 左边子序列 和 右边子序列的最大子序列和 left_max_sequence_sum = get_max_sequence_sum(nums[:middle_index]) right_max_sequence_sum = get_max_sequence_sum(nums[middle_index:]) # 计算从中间往两边扫描获得的最大子序列和 # 从中间往左边扫描获得最大的 for num in nums[:middle_index]: middle_to_left_this_sequence_sum += num middle_to_left_max_sequence_sum = middle_to_left_this_sequence_sum if \ middle_to_left_this_sequence_sum > middle_to_left_max_sequence_sum else \ middle_to_left_max_sequence_sum # 从中间往右边扫描获得最大的 for num in nums[middle_index:]: middle_to_right_this_sequence_sum += num middle_to_right_max_sequence_sum = middle_to_right_this_sequence_sum if \ middle_to_right_this_sequence_sum > middle_to_right_max_sequence_sum else \ middle_to_right_max_sequence_sum middle_max_sequence_sum = middle_to_left_max_sequence_sum + middle_to_right_max_sequence_sum return max([left_max_sequence_sum, right_max_sequence_sum, middle_max_sequence_sum])
利用这种分治法, 每次都会将问题分解一半, 而后扫描对应整个序列, 所以最后 T(n) = nlogn。blog
通常状况下利用分治法, 咱们已经能够获得比较优化的解法, 可是对于某些问题 咱们还能够经过更为精妙的数学思路来优化问题。递归
例如对于子序列和的计算问题, 由于是连续子序列, 若是获得的某个子序列和 < 0,那咱们就彻底能够抛弃掉这个子序列,而直接从下一个数继续往下获得新的子序列和。这种处理方法也叫作"在线处理问题"。
def get_max_sequence_sum_4(nums: list) -> int: this_sum = max_sum = 0 for num in nums: this_sum += num max_sum = this_sum if this_sum > max_sum else max_sum # 若是当前子序列和 < 0 直接抛弃这个子序列 if this_sum < 0: this_sum = 0 return max_sum
咱们能够很明显的看到,这种算法的, 平均时间复杂度为n。