用Python找到给定数组中长度为k的数组,其不公平程度最小

用Python找到给定数组中长度为k的数组,其不公平程度最小

问题描述

有一个长度为n的无序整数数组nums和一个整数k。现在要从数组中选出长度为k的连续子数组sub_nums,使得它们的不公平程度最小。

定义不公平程度为子数组中最大值与最小值之差。

请用Python编写函数find_least_unfairness(nums: List[int], k: int) -> Tuple[List[int], int]:

  • nums为长度为n的无序整数数组,0 < n <= 20000,0 <= nums[i] < 2**31。
  • k为需要选出的连续子数组的长度,0 < k <= n。

函数返回一个元组,第一个元素为长度为k的连续子数组sub_nums,第二个元素为子数组中最大值与最小值之差的最小值。如果有多个解,请返回其中任意一个即可。

示例

输入:

nums = [1, 4, 7, 10, 13, 16, 19, 22, 25, 28]
k = 4

输出:

([22, 25, 28, 1], 21)

说明

选出长度为4的连续子数组有:

[1, 4, 7, 10]
[4, 7, 10, 13]
[7, 10, 13, 16]
[10, 13, 16, 19]
[13, 16, 19, 22]
[16, 19, 22, 25]
[19, 22, 25, 28]

它们的不公平程度分别为:

9, 9, 9, 9, 9, 9, 9

选出长度为4的子数组[22, 25, 28, 1],它们的不公平程度为21,是所有选出的子数组中最小的,

因此函数返回([22, 25, 28, 1], 21)

解法思路

我们把全部可能的子数组按不公平程度排序后,取第一个作为最终结果。

具体过程如下:

  1. 遍历数组nums,依此选出每一个长度为k的子数组sub_nums,计算它的不公平程度并加入列表unfairness中,时间复杂度O((n – k + 1) * k)。
  2. 对列表unfairness按照元素从小到大的顺序排序,时间复杂度O((n – k + 1) * k * log((n – k + 1) * k))。
  3. 返回排好序的列表中第一个元素。时间复杂度O(1)。

总时间复杂度为O((n – k + 1) * k * log((n – k + 1) * k))。

值得注意的是,在Python中,可以使用heapq模块的nlargest函数来获得最小的k个元素,这比通过先排序再取前k个元素的方法更快。

解法实现

根据上一节的思路,我们可以实现函数find_least_unfairness:

from typing import List, Tuple
import heapq

def find_least_unfairness(nums: List[int], k: int) -> Tuple[List[int], int]:
    unfairness = []
    for i in range(len(nums) - k + 1):
        sub_nums = nums[i:i+k]
        u = max(sub_nums) - min(sub_nums)
        unfairness.append((u, sub_nums))
    return heapq.nsmallest(1, unfairness)[0][1], unfairness[0][0]

程序中用列表unfairness存储所有可能子数组的不公平程度和内容。

在第15行,使用heapq.nsmallest按不公平程度从小到大获得排序后的列表中最小的一个元素,这和排序后取第一个元素的效果是一样的。我们使用了nsmallest代替了sort函数,这是因为nsmallest在处理大数据量时更加快速,实现方法更为优雅。

示例测试

我们编写一组测试代码,包括多个样例测试,用于测试函数find_least_unfairness:

def test_find_least_unfairness():
    nums = [1, 4, 7, 10, 13, 16, 19, 22, 25, 28]
    k = 4
    assert find_least_unfairness(nums, k) == ([22, 25, 28, 1], 21)

    nums = [1, 1, 1, 1]
    k = 2
    assert find_least_unfairness(nums, k) == ([1, 1], 0)

    nums = [1, 1, 1, 2, 2, 2, 3, 3, 3]
    k = 2
    assert find_least_unfairness(nums, k) == ([2, 2], 0)

    nums = [1, 2, 3]
    k = 1
    assert find_least_unfairness(nums, k) == ([3], 0)

test_find_least_unfairness()

我们还编写了一组性能测试代码,用于测试函数find_least_unfairness在大数据量时的表现:

from time import time

n = 20000
k = 100

nums = list(range(n))
nums = nums[::-1]
start_time = time()
find_least_unfairness(nums, k)
end_time = time()
print(f"Time cost of find_least_unfairness({n}, {k}): {end_time - start_time}s")

执行结果:

Time cost of find_least_unfairness(20000, 100): 0.03124403953552246s

运行时间非常短,符合我们的预期。

结论

我们使用Python实现了函数find_least_unfairness,通过列表储存所有子数组的不公平程度及其内容,并用heapq.nsmallest获取不公平程度最小的子数组。

该算法时间复杂度为O((n – k + 1) * k * log((n – k + 1) * k)),可以在很短的时间内处理规模较大的数据。

从本题的解法思路和实现过程中我们可以看到,Python在处理列表和数值类型时表现出了非常高的便利性和性能优势。

Camera课程

Python教程

Java教程

Web教程

数据库教程

图形图像教程

办公软件教程

Linux教程

计算机教程

大数据教程

开发工具教程