zoukankan      html  css  js  c++  java
  • Python编程题39所有奇数长度子列表的和

    题目

    给定一个非空的正整数列表 arr ,请计算所有可能的奇数长度子列表的和(子列表指原列表中的一个连续子序列)。

    例如:

    给定一个列表:[1, 4, 2, 5, 3],返回结果:58

    解释:所有奇数长度子列表及它们的和为:
    [1] = 1
    [4] = 4
    [2] = 2
    [5] = 5
    [3] = 3
    [1,4,2] = 7
    [4,2,5] = 11
    [2,5,3] = 10
    [1,4,2,5,3] = 15

    我们将所有值求和得到 1 + 4 + 2 + 5 + 3 + 7 + 11 + 10 + 15 = 58

    实现思路1

    • 直接进行暴力破解,需要3层循环,时间复杂度为O(n^3)
    • 第一层循环,遍历出所有符合条件的子列表长度 small_arr_len
    • 第二层循环,遍历出原列表的所有元素 arr[i]
    • 第三层循环,对当前找到的奇数子列表,即 arr[i:i+small_arr_len] 中的所有元素进行求和

    代码实现1

    def sumOddLengthSubarrays(arr):
        res_sum, arr_len = 0, len(arr)
        for small_arr_len in range(1, arr_len + 1, 2):  # 遍历所有可能符合条件的子列表长度
            for i in range(arr_len):  # 遍历原列表的所有元素
                if i + small_arr_len <= arr_len:  # 判断是否越界
                    for j in range(i, i + small_arr_len):  # 对当前奇数子列表求和
                        res_sum += arr[j]
        return res_sum
    

    上面的方法中,可以看到第三层循环时,每次都需要对 i 到 i + small_arr_len - 1 下标的元素进行重复的求和计算,如果我们能找到一种方法,实现快速计算出两个下标之间的所有元素的和,那么就可以对上面方法实现优化。

    在这里,我们可以利用 前缀和 来实现,比如针对列表 arr = [1, 4, 2, 5, 3],我们可以得到从第一个位置到当前位置的所有元素之和,也就是一个前缀和的列表 pre_sum_list = [1, 5, 7, 12, 15],当我们需要计算下标 2 到下标4 的子列表 [2, 5, 3] 所有元素之和,就可以直接通过计算 pre_sum_list[4] - pre_sum_list[2 - 1] = 15 - 5,得到结果 10

    实现思路2

    • 使用前缀和列表,时间复杂度为O(n^2)
    • 最开始先使用一个循环,计算并得到一个包含有前缀和的列表 pre_sum_list
    • 接着再计算所有奇数长度子列表的和,第一层循环,遍历出所有符合条件的子列表长度 small_arr_len
    • 第二层循环,遍历出原列表的所有元素,下表为 i
    • 第三层循环,对当前找到的奇数子列表 arr[i:i+small_arr_len] ,借助 pre_sum_list 求出所有元素之和,如果当前位置为 0 ,那么当前奇数子列表之和为 pre_sum_list[i + small_arr_len - 1],否则为 pre_sum_list[i + small_arr_len - 1] - pre_sum_list[i - 1]

    代码实现2

    def sumOddLengthSubarrays(arr):
        res_sum, arr_len = 0, len(arr)
        pre_sum_list = [0] * arr_len
        for i in range(arr_len):  # 获取前缀和列表
            pre_sum_list[i] = pre_sum_list[i - 1] + arr[i] if i != 0 else arr[i]
        for small_arr_len in range(1, arr_len + 1, 2):  # 遍历所有可能符合的子列表长度
            for i in range(arr_len):  # 遍历原列表
                if i + small_arr_len <= arr_len:   # 判断是否越界
                    if i == 0:
                        res_sum += pre_sum_list[i + small_arr_len - 1]
                    else:
                        res_sum += pre_sum_list[i + small_arr_len - 1] - pre_sum_list[i - 1]
        return res_sum
    

    上面方法的时间复杂度为O(n^2),那么有没有更优的解决办法呢?

    我们可以直接利用 数学方式 来求和,本题目的是为了计算出所有奇数长度子列表的和,换个角度思考下,我们只需要求出原列表中每个元素在所有奇数长度子列表中出现的次数即可,这样一来,我们再对每个元素按其出现次数求和,最后再相加就能得到最终结果了。

    针对列表 arr = [1, 4, 2, 5, 3] 中的元素 4,其左侧元素个数为 1 ,右侧元素个数为 3,包含元素 4 的所有奇数长度子列表分为以下两种情况:

    第一种情况,因为 奇数个 + 1 + 奇数个 = 奇数个,所以可从元素4 左右两侧均取奇数个,从左侧取奇数个的方法有 1 种(取1个),从右侧取奇数个的方法有 2 种(取1个、取3个),组合起来总的方法就有 1 * 2 = 2种:[1, 4, 2]、[1, 4, 2, 5, 3]

    第二种情况,因为 偶数个 + 1 + 偶数个 = 奇数个,所以可从元素4 左右两侧均取偶数个,从左侧取偶数个的方法有 1 种(取 0 个),从右侧取奇数个的方法有 2 种(取0个、取2个),组合起来总的方法就有 1 * 2 = 2种:[4]、[4, 2, 5]

    接着把上面两种情况相加 2 + 2 = 4,该结果表示所有奇数长度子列表中,包含有元素4的情况一共有4种,也就是说元素4在 所有奇数长度子列表 中的出现次数为4,所以此时可以求出所有奇数长度子列表中的元素4之和:4 * 4 = 16

    最后,分别求出每个元素在 所有奇数长度子列表 中的出现次数并依次求和,就可以得到最终结果。

    实现思路3

    • 使用数学方式求和,只需循环一次,时间复杂度为O(n)
    • 遍历过程中,首选分别求出当前元素左侧及右侧的元素个数 count_left,、count_right
    • 从当前元素左右侧只取奇数个元素,分别求出从当前元素左侧及右侧取奇数个元素的可能性 left_odd、right_odd
    • 从当前元素左右侧只取偶数个元素,分别求出从当前元素左侧及右侧取偶数个元素的可能性 left_even、right_even
    • 接着分别求出取奇数个和偶数个的所有组合的可能性 odd_all、even_all
    • 最后得到当前元素在 所有奇数长度子列表 中的出现次数为 odd_all + even_all ,并可对当前元素进行最终求和

    代码实现3

    def sumOddLengthSubarrays(arr):
        res_sum, arr_len = 0, len(arr)
        for i in range(arr_len):
            # count_left 表示当前元素左侧的元素个数,count_left 表示当前元素右侧的元素个数
            count_left, count_right = i, arr_len - i - 1
            # 奇数 + 1 + 奇数 = 奇数, left_odd 表示从当前元素左侧取奇数个元素, right_odd 表示从当前元素右侧取奇数个元素
            left_odd, right_odd = (count_left + 1) // 2, (count_right + 1) // 2
            # 偶数 + 1 + 偶数 = 奇数, left_even 表示从当前元素左侧取偶数个元素, right_even 表示从当前元素右侧取偶数个元素
            left_even, right_even = count_left // 2 + 1, count_right // 2 + 1
            # odd_all 表示从两侧均取奇数个元素的所有可能性, even_all 表示从两侧均取偶数个元素的所有可能性
            odd_all, even_all = left_odd * right_odd, left_even * right_even
            # 所有子列表中统计出当前元素的个数 odd_all + even_all ,对当前元素求和即可
            res_sum += arr[i] * (odd_all + even_all)
        return res_sum
    

    更多Python编程题,等你来挑战:Python编程题汇总(持续更新中……)

    作者:wintest
    本文版权归作者和博客园共有,欢迎转载,但必须在文章页面明显位置给出原文链接,并保留此段声明,否则保留追究法律责任的权利。
  • 相关阅读:
    Unity3D读取assetbundle
    Unity3D 发布成exe之后黑屏
    Unity3D优化总结
    Unity3D中中 rect[2] == rt->GetGLWidth() && rect[3] == rt->GetGLHeight()错误的原因及解决方法
    C# mysql 插入数据,中文乱码的解决方法
    WPF Canvas做自动缩放时获取控件的实际高度
    面向对象
    常用模块介绍
    python异常处理,多线程,多进程
    python生成器,递归调用
  • 原文地址:https://www.cnblogs.com/wintest/p/15731387.html
Copyright © 2011-2022 走看看