【问题标题】:Find the longest sub array where the distance between the elements is not bigger than one找到元素之间的距离不大于 1 的最长子数组
【发布时间】:2021-11-03 04:48:41
【问题描述】:

我有这个数组,例如:[1, 1, 2, 3, 4, 5])

所以我想要的数组是[1,1,2],我写了这段代码:

def Comparison(arr, n):

    i = 0
    ct = 0
    print("call")
    while i + len(arr)-n <= len(arr):
        print(arr[i:len(arr)-n+i])
        if max(arr[i:len(arr)-n+i]) - min(arr[i:len(arr)-n+i]) == 1:
            print("change at: "+str(n))
            ct = 1
            break
        i += 1
    if ct == 0:
        l = arrComparison(arr, n+1)
        return l
    else:
        return n

def longestSubarray(arr):

    menor = min(arr)
    mayor = max(arr)
    n = 0
    if mayor - menor > 1:
        n = arrComparison(arr, 1)

    return len(arr) - n

其中“LongestSubarray”函数只是测试最长的子数组是否不是数组本身,如果不是,则调用另一个,它正在逐步沿着数组移动,“n”表示的数量未在子数组中选择的数字,即当 n=2 时,子数组将是:

[1,1,2,3], [1,2,3,4], [2,3,4,5]

当它找到第一个时,它们会中断并返回 n。 这实际上解决了这个问题,但它确实需要很长时间,所以我想要一种更优化的方式。

【问题讨论】:

  • 似乎这个问题可以使用两指针方法在线性时间内解决(“良好范围”周围的左右索引)
  • 值是一直在增加还是可以减少?
  • 这只是为了这个例子,值可以以任何方式放置,甚至所有元素都相同。

标签: python arrays algorithm optimization


【解决方案1】:

这是一个带有 numpy 的矢量解决方案。基本上,它将数组与自身进行比较(因此复杂度是二次的)以计算差异,然后从对角线找到低于阈值的最长拉伸:

import numpy as np
def longest(arr, n=1):
    # ensure numpy array
    arr = np.array(arr)
    # calculate absolute diff matrix with itself on upper triangle
    # make boolean based on diff > threshold
    diffs = (np.argmax(np.c_[np.triu(np.abs(arr-arr[:,None])>n),
                             np.ones(len(arr)) # this handles the case where array is uniform
                            ],
                      axis=1)
             -np.arange(len(arr)) # subtract position of diagonal
            )
    # get position of first non matching value (value is length of stretch)
    m = np.argmax(diffs)
    return arr[m:m+diffs[m]].tolist()

例子:

>>> longest([1,1,2,3,4,5]) 
[1, 1, 2]

>>> longest([1,1,2,3,4,5], n=2) 
[1, 1, 2, 3]

>>> longest([1,1,1,1,1,1,1])
[1,1,1,1,1,1,1]

>>> longest([9, 8, 7, 6, 7])
[7, 6, 7]

>>> np.random.seed(0)
>>> longest(np.random.randint(0, 50, size=10000), n=2)
[49, 47, 49, 49, 47]

【讨论】:

  • 这有二次复杂度?因为我们需要计算差异的方阵。
  • @Stef 你说得对。我最初提供这个答案是为了“有趣”,因为已经有其他答案带有循环,但后来证明这些答案是不正确的。我还提供了一个answer with a loop,它运行得更快。
  • 您可能应该在答案中提到二次复杂度!
【解决方案2】:

您可以使用 itertools.combinations 来简化流程。您从可能的最大组合开始,然后逐步向下。

from itertools import combinations

arr = [1, 1, 2, 3, 4, 5]
for i in range(len(arr),1,-1):
    for el in combinations(arr,i):
        if max(el) - min(el) == 1: break
    else:
       continue
    break

print(el)

输出:

(1, 1, 2)

编辑:如果你只想要连续子集,这里是一个简单的修改。

arr = [1, 1, 2, 3, 4, 5]
n = len(arr)
for i in range(n,1,-1):
    for el in combinations(arr,i):
        if (max(el)-min(el)==1  and
        any([*el]==arr[j:j+len(el)] for j in range(0,n-len(el)+1))): break
    else:
       continue
    break

【讨论】:

  • 错误答案。除了奇怪的效果之外,组合选择元素的子集,而 subarray 假设 continuous range
【解决方案3】:

这是一个命令式工作版本。

它通过循环第一个索引(i)和每个子数组(i 到数组末尾(减去要优化的当前最长的长度))来工作,查找是否有更长的子数组匹配条件:

def longest(array, n=1):
    pos_longest = 0
    len_longest = 0
    i = 0
    while i < len(array)-len_longest+1:
        length = 0
        for j in range(i+1,len(array)-1):
            if abs(array[j]-array[i])>n:
                break
            else:
                length = j-i+1
        if length>len_longest:
            pos_longest = i
            len_longest = length

        i+=1
    return array[pos_longest:pos_longest+len_longest]

例子:

>>> longest([1,1,2,3,4,5]) 
[1, 1, 2]

>>> longest([1,1,2,3,4,5], n=2) 
[1, 1, 2, 3]

>>> longest([1,1,1,1,1,1,1])
[1,1,1,1,1,1,1]

>>> longest([9, 8, 7, 6, 7])
[7, 6, 7]

>>> np.random.seed(0)
>>> longest(np.random.randint(0, 50, size=10000), n=2)
[49, 47, 49, 49, 47]

【讨论】:

    【解决方案4】:

    您可以在线性时间内使用“双指针”方法解决此问题:沿列表移动,将左右指针保持在当前数组周围。如果我们的数组的距离太大,请向左向右移动;否则,向右前进。使用 dict 跟踪当前数组中的元素。

    def longest_subarray(arr):
        if not arr:
            return []
    
        left = 0
        best_length = 1
        best_start = 0
        counts = {arr[0]: 1}
    
        for right in range(1, len(arr)):
            counts[arr[right]] = counts.get(arr[right], 0) + 1
            if len(counts) <= 2 and max(counts)-min(counts) <= 1:
                best_start = left
                best_length = right-left+1
                continue
            counts[arr[left]] -= 1
            if counts[arr[left]] == 0:
                del counts[arr[left]]
            left += 1
        return arr[best_start: best_start+best_length]
    

    【讨论】:

    • 如果数组是 A = [1, 1, 2, 2, 3, 3, 4, 4] 我们期望什么?
    • @DanielHao 看问题描述;有多个有效答案,如果没有给出其他说明,您应该能够返回其中任何一个。默认情况下,此代码将始终给出最左边最长的子数组。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-28
    • 1970-01-01
    • 1970-01-01
    • 2020-07-23
    • 2013-02-07
    • 1970-01-01
    相关资源
    最近更新 更多