假设您有一个大小为的数组,其中包含从到(含)在内的整数,并且正好重复了5个。我需要提出一种算法,该算法可以找到O(n)时间中的重复数。我一生都无法想任何事情。我认为排序最好是O(n \ log n)?然后遍历数组将是O(n),从而导致O(n ^ 2 \ log n)。但是,我不确定是否需要排序,因为我已经看到一些棘手的东西,包括链接列表,队列,堆栈等。1O (n )O (n log n )O (n )O (n 2 log n )
假设您有一个大小为的数组,其中包含从到(含)在内的整数,并且正好重复了5个。我需要提出一种算法,该算法可以找到O(n)时间中的重复数。我一生都无法想任何事情。我认为排序最好是O(n \ log n)?然后遍历数组将是O(n),从而导致O(n ^ 2 \ log n)。但是,我不确定是否需要排序,因为我已经看到一些棘手的东西,包括链接列表,队列,堆栈等。1O (n )O (n log n )O (n )O (n 2 log n )
Answers:
你可以创建一个额外的数组大小的。最初将数组的所有元素设置为。然后循环遍历输入数组并为每个将增大1 。之后,您只需检查数组:在循环,如果则重复。你在解决问题时间的内存成本,这是和因为你的整数是与和。n 0 A B [ A [ i ] ] i B A B [ A [ i ] ] > 1 A [ i ] O (n )O (n )1 n - 5
fade2black答案中的解决方案是标准解决方案,但它使用空间。您可以将其改进为O (1 )空间,如下所示:
该算法假定采用RAM机器模型,其中对位字进行基本算术运算需要O (1 )时间。
制定此解决方案的另一种方法是遵循以下思路:
该解决方案表明,如果将5替换为,则可以使用O (d 2)空间获得(我相信)O (d 2 n )算法,该算法对位长为O的整数执行O (d n )算术运算(d log n ),在任何给定时间最多保留O (d )个。(这需要仔细分析我们执行的乘法,其中大多数涉及一个仅长度为O的操作数(log n。)可以想象,可以使用模块化算法将其改进为 O (d n )时间和 O (d )空间。
还有一种基于分区的线性时间和恒定空间算法,如果您尝试将其应用于数学方法无法很好解决的问题的变体,则该方法可能会更加灵活。与数学方法相比,这需要对基础数组进行变异,并且常数因子更差。更具体地说,我相信,就值的总数和重复项d的数量而言,代价是分别为O(n log d )和O(d ),尽管严格证明它将比我目前花费更多的时间。 。
从对的列表开始,其中第一对是整个数组的范围,如果为1索引,则为。
重复以下步骤,直到列表为空:
步骤1至6花费时间,因为找到最小和最大值以及划分可以在线性时间内完成。
列表中的每对要么是第一对((1 ,n ),要么是某对的子代,其对应的子数组包含一个重复的元素。有至多d ⌈ 登录2周 Ñ + 1 ⌉这样的父母,由于每个遍历半部的范围内,其中一个重复的就可以了,所以有至多2 d ⌈ 登录2 Ñ + 1个⌉总包括对在子阵列没有时重复。在任何时候,列表的大小不超过2 d。
考虑找到任何重复的工作。它由一个在指数递减范围内的成对序列组成,因此总功是几何序列的总和,即。这产生了一个明显的推论,即d个重复项的总功必须为O(n d ),它在n中是线性的。
要找到更严格的界限,请考虑最大程度地分散副本的最坏情况。直观地讲,搜索过程分为两个阶段,一个阶段每次遍历整个数组,每个阶段逐渐变小,而另一个阶段小于因此只遍历数组的一部分。第一阶段只能是logd深度,因此成本为O(nlogd),第二阶段具有成本O(n),因为要搜索的总面积再次呈指数下降。
将此作为答案,因为它需要的空间比评论所需要的空间大。
当您建议一种方法时,会在OP中出错。排序列表,然后遍历时间,而不是O (n 2 log n )时间。当您依次执行两项操作(分别取O (f )和O (g ))时,结果时间复杂度为O (f + g )= O (max f ,g)(在大多数情况下)。
为了增加时间复杂度,您需要使用for循环。如果您有一个长度为的循环,并且对于循环中的每个值,您都执行一个函数O (g ),那么您将获得O (f g )时间。
因此,在您的情况下,您以排序,然后以O (n )进行横向运算,从而得出O (n log n + n )= O (n log n )。如果对于排序算法的每次比较,您都必须进行O (n )的计算,则它将采用O (n 2 log n ),但是这里不是这种情况。
如果您对我声称感到好奇,请务必注意并非总是如此。但是,如果˚F ∈ Ô (克)或克∈ ø (˚F )(其适用于常用功能整个主机),它会保持。它最不常用的时间是当涉及到其他参数时,您会得到诸如O (2 c n + n log n )的表达式。
布尔数组技术有一个很明显的就地变体,它使用元素的顺序作为存储(arr[x] == x
“找到”元素的位置)。不像分区变体可以被证明更通用,我不确定何时真正需要这样的东西,但这很简单。
for idx from n-4 to n
while arr[arr[idx]] != arr[idx]
swap(arr[arr[idx]], arr[idx])
这只是反复放置arr[idx]
在该位置,arr[idx]
直到您找到该位置为止,此时该位置必须是重复的。请注意,交换的总数受限制,因为每次交换都会使其退出条件正确。
while
循环平均在恒定时间内运行。否则,这不是线性时间算法。
从和中减去你具有值。
于是,经过时间(假设算术是O(1),它是不是真的,但让我们假装)你有一笔σ 1 1到n之间的5整数:
据说这不好,对吧?您可能无法弄清楚如何将其分解为5个不同的数字。
啊,但这就是有趣的地方!现在执行与以前相同的操作,但从∑ n i = 1 i 2中减去值的平方。现在您有了:
看到我要去哪里了吗?对3、4和5的幂进行相同的操作,您将在5个变量中拥有5个独立方程。我很确定您可以解决。
注意事项:算术不是真的 O(1)。另外,您需要一些空间来表示您的总和。但并不像你想象的-你可以做模块化最一切,只要你有,哦,位; 应该这样做。
解决问题的最简单方法是创建一个数组,在该数组中,我们将对原始数组中每个数字的出现次数进行计数,然后遍历从到n - 5的所有数字,并检查该数字是否出现一次以上,因此这很复杂内存和时间中的解都是线性的,或者是O (N )
将数组映射到1 << A[i]
,然后将所有内容异或。您的重复项将是关闭相应位的数字。
DATA=[1,2,2,2,2,2]
from collections import defaultdict
collated=defaultdict(list):
for item in DATA:
collated[item].append(item)
if len(collated) == 5:
return item.
# n time
collated[item].append(item)
以恒定时间运行。是真的吗