数据结构-学习之路(一)-子序列问题优化

**本博客内容根据 Mooc 浙江大学 数据结构 1.3课 什么是算法一课总结获得
若是对于博客中的算法有任何疑问请您斧正**python


问题: 子序列问题

有一个序列N, 其中包含了 [A1,.... AN]个整数, 求解获得其中的最大连续子序列和。

例子: 
N: [1, 2, -1]
几个连续子序列:
[1]
[2]
[-1]
[1, 2]
[2, -1]
[1, 2, -1]
其中最大的连续子序列就是3

面对这个问题, 最暴力的方式就是,穷举获得全部的子序列,而后获得其中最大的子序列和。算法

C1 循环法:

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数据结构


明显能够发现,在计算子序列和的时候,能够经过上一个子序列和加上下一个整数获得新子序列和优化

C2 循环法2.0

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

将序列按照中间分红左右两个序列
而后计算 左子序列的最大子序列和 右子序列的最大子序列和 以及从中间往两边扫描获得的最大子序列和

子序列问题分治法.png
image.png

第一步 获得左右两边序列 [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 ,若是看不懂你们能够看下参考课程老师的视频)

分治法的思路 就是不断分解问题 而后再将子问题的结果合起来获得最终结果

从而, 咱们能够获得第三种算法视频

C3 分治法

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,那咱们就彻底能够抛弃掉这个子序列,而直接从下一个数继续往下获得新的子序列和。这种处理方法也叫作"在线处理问题"。

C4 在线处理法

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。


参考

Mooc-数据结构

相关文章
相关标签/搜索