给定一个排序数组,找到最大的重复值子数组。

4

最近有一个面试问题要求我在尽可能短的计算时间内找到重复数值的最大子数组,给定一个已排序的数组。

Let input array be A[1 ... n]
Find an array B of consecutive integers in A such that:
for x in range(len(B)-1):
     B[x] == B[x+1]

我认为最好的算法是将数组分成两半,从中间向外比较整数之间的差异,找到相同整数的最长序列。然后通过将数组分成两半并在两半上调用该方法来递归地调用该方法。

我的面试官说我的算法很好,但我的分析算法的时间复杂度是O(logn)是错误的,但从未告诉我正确答案。我的第一个问题是这个算法的时间复杂度是什么?(请尽可能展示工作过程!Big-O不是我的强项。)我的第二个问题只是出于好奇,是否有更高效的算法?


我对你的问题感到相当困惑。你能否更详细地描述面试官的意思?(什么是“strain”?)并且你能否更详细地描述你的解决方案?(可能使用伪代码。) - svick
更新了更多细节。我基本上使用了分治策略。 - user1246462
请修改您的标题,使其对该网站未来的用户更有用。 - Raymond Chen
是的,完全正确,谢谢。我对确切措辞的记忆有些模糊。 - user1246462
4个回答

4

对于这个问题,最好的解决方案是O(n),因此你的算法不能同时正确且O(lg n)

例如,考虑数组不包含重复元素的情况。要确定这一点,需要检查每个元素,而检查每个元素的时间复杂度为O(n)

这是一个简单的算法,可以找到重复元素的最长子序列:

start = end = 0
maxLength = 0
i = 0
while i + maxLength < a.length:
    if a[i] == a[i + maxLength]:
        while i + maxLength < a.length and a[i] == a[i + maxLength]:
            maxLength += 1
        start = i
        end = i + maxLength
    i += maxLength

return a[start:end]

如果您有理由相信子序列将会很长,您可以将maxLength的初始值设置为一些启发式选择的值以加快速度,然后只有在找不到较长序列时才寻找较短的序列(即第一次遍历之后您将获得end == 0) 。

应该有比O(n)更紧密的限制。楼主的算法听起来比从第一个元素到最后一个元素扫描数组要高效得多。 - Jeow Li Huan
3
我们在谈论最坏情况复杂度。在最坏情况下(即每个元素都是独一无二的),你必须检查每一个元素,时间复杂度为 O(n) - verdesmarald
你忘了说你的解决方案的时间复杂度是多少。 - svick
@svick 这是 O(n)。在最坏的情况下,maxLength1,而 i += maxLength 只是变成了 i += 1 - verdesmarald

0
在这个算法中,n 个元素被访问,每个被访问的元素都有一个恒定数量的计算,因此运行时间为 O(n)
给定排序数组 A[1..n]
max_start = max_end = 1
max_length = 1
start = end = 1
while start < n
    while A[start] == A[end] && end < n
        end++
    if end - start > max_length
        max_start = start
        max_end = end - 1
        max_length = end - start
    start = end 

你最好从 end = start + max_length 开始而不是 end = start + 1。虽然时间复杂度仍为 O(n),但大多数情况下速度更快。 - verdesmarald
你是正确的。这个特定算法的重点在于简单性,这样每个数组元素都有恒定的额外操作,更容易看到。 - Avi Cohen

0

我认为我们都同意,在最坏的情况下,即所有A都是唯一的或所有A都相同的情况下,您必须检查数组中的每个元素,以确定是否存在重复项或确定数组包含一个数字。就像其他帖子中所说的那样,这将是O(N)。我不确定分治法在算法复杂度上能够帮助您很多,尽管您可能可以通过使用递归来简化代码。当您可以丢弃大量输入时(例如二分搜索),分治法确实有助于减少大O,但在您可能需要检查所有输入的情况下,它并没有什么不同。

我假设这里的结果只是返回您找到的最大B的大小,尽管您可以轻松修改此结果以返回B。

关于算法,考虑到A已经排序,我不确定是否有比按顺序遍历数组更快/更简单的答案。最简单的方法似乎是使用两个指针,一个从索引0开始,另一个从索引1开始。比较它们,然后将它们都增加;每次它们相同时,您将计数器向上移动以给出当前B的大小,并且当它们不同时,您将该计数器重置为零。您还需要保留一个变量来存储迄今为止找到的最大B的大小,并在找到更大的B时更新它。


同意最坏情况下,“全部唯一”需要O(N)的时间。 但是,“全部相同”的情况可以立即从A[1]==A[n]中判断出来;这是O(1),我认为这是最好的情况。 - Quigi

-1

假设最长连续整数只有长度为1,那么您将扫描整个n项数组A。因此,复杂度不是以n为单位,而是以len(B)为单位。

不确定复杂度是否为O(n/len(B))。

检查2个边缘情况

- 当n == len(B)时,您会得到即时结果(仅检查A [0]和A [n-1] - 当n == 1时,您会得到O(n),检查所有元素 - 在正常情况下,我太懒了,不想编写算法进行分析...

编辑

考虑到len(B)事先未知,我们必须采取最坏情况,即O(n)


这个答案是不正确的,计算复杂度类通常是针对输入元素数量进行的,并默认指算法的最坏情况运行时间,即使没有另外说明。如果没有这个默认值,我可以说破解符合标准的AES加密的暴力算法的运行时间是O(1),因为它可能非常幸运而第一次测试出正确的密钥... - lol
因为尝试定义更严格的界限而被踩?:( OP的算法绝对比线性搜索好,并且肯定与len(B)有关。 - Jeow Li Huan
如果已知 AES 加密存在缺陷并且倾向于连续重用密钥,而您编写了一款利用该模式的破解工具,那么您的算法仍然是 O(n) 吗?还是这取决于连续重用密钥的数量? - Jeow Li Huan
“if given that…”是假设前提条件,当你写这个时,必须小心不要实质性地改变问题。数学家们用“不失一般性”来作为这类语句的前缀,以澄清他们在假设前提条件时知道自己在做什么。我的意思是AES破解从来不是O(1)或者O(n)...那n到底是多少呢...?(请不要回答这个问题) - lol

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接