【问题标题】:Find the Element Occurring b times in an an array of size n*k+b在大小为 n*k+b 的数组中查找出现 b 次的元素
【发布时间】:2012-03-02 13:33:27
【问题描述】:

说明

给定一个大小为 (n*k+b) 的数组,其中 n 个元素出现 k 次,一个元素出现 b 次,换句话说,有 n+1 个不同的元素。鉴于 0 < b < k 找到出现 b 次的元素。

我尝试过的解决方案

  1. 显而易见的解决方案将使用散列,但如果数字非常大,它将不起作用。复杂度是O(n)

  2. 使用 map 存储每个元素的频率,然后遍历 map 以查找出现 b 次的元素。由于 Map 实现为高度平衡的树,复杂性将为 O(nlogn)

我的两个解决方案都被接受了,但面试官想要一个不使用散列的线性解决方案,并且他给出的提示是使树的高度在您存储频率的树中保持不变,但我无法找出正确的解决方案还没有。

我想知道如何在没有哈希的情况下在线性时间内解决这个问题?

编辑:

示例:

输入:n=2 b=2 k=3

数组:2 2 2 3 3 3 1 1

输出:1

【问题讨论】:

  • 请注意,您的解决方案是O((n*k+b)logn),而不是O(nlogn) - 考虑到问题的条款。
  • 你能给出一个包含样本值的样本数组吗?
  • 我怀疑面试官想听到类似trie 的内容。但这很愚蠢。特里树的高度至少和O(logn)一样大,所以如果你可以说它是一个“常数” - O(logn)也是如此
  • 你确定面试官的意思吗?比如关于元素范围的任何提示?如果它们的范围在 [0..n-1] 范围内,则解决方案很明显。
  • 是的,我确定....我确认只有时间....数字是随机的,不一定在 1 到 n 之间。

标签: arrays algorithm


【解决方案1】:

我假设:

  1. 数组的元素是可比较的。
  2. 我们事先知道 n 和 k 的值。
  3. 解决方案 O(n*k+b) 就足够了。

设只出现 b 次的数为 S。我们试图在 n*k+b 大小的数组中找到 S。

递归步骤:像快速排序一样在线性时间内找到当前数组切片的中值元素。设中间元素为M。

在递归步骤之后,您有一个数组,其中所有小于 M 的元素都出现在 M 的第一次出现的左侧。所有 M 元素彼此相邻,所有大于 M 的元素都出现在所有出现的右侧M.

查看最左边的 M 的索引并计算 S=M。在左切片或右切片上递归。

因此,您正在进行快速排序,但在任何时候都只研究部分部门。您将递归 O(logN) 次,但每次都使用原始数组的 1/2、1/4、1/8、.. 大小,因此总时间仍为 O(n)。

澄清:假设 n=20 和 k = 10。那么,数组中有 21 个不同的元素,其中 20 个出现 10 次,最后一个出现 7 次。我找到了中间元素,假设它是 1111。如果 S=1111 则索引将等于 11*10。

完整示例: n = 4. k = 3. Array = {1,2,3,4,5,1,2,3,4,5,1,2,3, 5} 在第一个递归步骤之后,我发现中值元素为 3,数组类似于:{1,2,1,2,1,2,3,3,3,5,4,5,5,4} 有3左边有6个元素。6是k=3的倍数。所以每个元素必须在那里出现 3 次。所以 S>=3。在右侧递归。以此类推。

【讨论】:

  • 我不明白你怎么能决定你想在左边还是右边递归。 S 的值是未知的,你如何比较 S==M?请您澄清一下这些要点吗?
  • 我添加了一个说明和一个例子。
  • 有一个 deterministic 线性时间算法来查找数组中的中值。请参阅 Cormen 书中的相关部分或查看此链接:en.wikipedia.org/wiki/… 是的,我没有提供所有细节和工作代码,因为它是不必要的,可以通过连续选择中位数来完成,它应该足够好面试答案。还有什么需要澄清的吗?
  • @yi_H: there are O(N), worst-case linear time, selection algorithms (特别是用于找到中值元素)因此答案中的算法也是最坏情况下的线性时间(T(N) = T(N/2) + N: N - 找到中值,@ 987654329@ - 递归步骤)。
  • @deathApril 不,它不会更快。但是使用散列是被禁止的,没有散列怎么能实现一个集合呢?甚至元素也不必是整数,它们可以是任何类型。
【解决方案2】:

使用循环群的想法。

要猜测第 i 个答案,请按照以下步骤操作:

  1. 计算数组中有多少个数字设置了第i位,存储为cnt
  2. 如果cnt % k 不为零,则设置第i 个答案位。否则就很清楚了。

要猜测整数,请对每一位重复上述操作。

这个解决方案在技术上是O((n*k+b)*log max N),其中max N是表中的最大值,但是由于位数通常是恒定的,所以这个解决方案在数组大小上是线性的。

没有哈希,内存使用是O(log k * log max N)

示例实现:

from random import randint, shuffle

def generate_test_data(n, k, b):
    k_rep = [randint(0, 1000) for i in xrange(n)]
    b_rep = [randint(0, 1000)]
    numbers = k_rep*k + b_rep*b
    shuffle(numbers)
    print "k_rep: ", k_rep
    print "b_rep: ", b_rep
    return numbers

def solve(data, k):
    cnts = [0]*10
    for number in data:
        bits = [number >> b & 1 for b in xrange(10)]
        cnts = [cnts[i] + bits[i] for i in xrange(10)]
    return reduce(lambda a,b:2*a+(b%k>0), reversed(cnts), 0)

print "Answer: ", solve(generate_test_data(10, 15, 13), 3)

【讨论】:

  • 除了*log(max(N)) 问题(这可能会或可能不会在问题限制范围内),我比我更喜欢你的解决方案。
  • @Ali,从技术上讲,所有算术运算都是O(log max N),只是通常我们将自己限制在固定位数。所以你的解决方案也不是没有这个因素。我的算法只是让它明确。
  • 如果 b % k = 0 会不会失败?
  • @rajatkhanduja:见问题的第一段:0
【解决方案3】:

为了有一个恒定的高度B-tree包含n不同的元素,高度h恒定,你需要z=n^(1/h)每个节点的子节点:h=log_z(n),因此@987654325 @,因此log(z)=log(n)/h,因此z=e^(log(n)/h),因此z=n^(1/h)

例如,n=1000000h=10z=3.98,即z=4

在这种情况下到达节点的时间是O(h.log(z))。假设hz 是“常数”(因为N=n.k,那么log(z)=log(n^(1/h))=log(N/k^(1/h))=ct 通过基于k 正确选择h,那么您可以说O(h.log(z))=O(1)... 这有点牵强,但也许这是面试官想听到的那种东西?

【讨论】:

  • 他是如何在这种复杂性中生成表[无哈希]的?
  • 哪个表?如果您谈论频率表,我假设您事先知道n - 如果您不知道,您可以使用地图。对于元素表,它作为输入给出。
  • 查找频率表需要map,它被实现为树[不允许散列]。树上的每个 OP 都是 O(logn)
  • 创建一个没有的表将花费 O((n*k+b)logn) 因为在创建过程中您必须遍历数组并在适当的位置增加频率,这将花费 O(log n)具有高度平衡的树。
  • 你说得对,我看错了问题的一个关键点,不允许散列。
【解决方案4】:

更新:这个使用散列,所以这不是一个好的答案:(

在 python 中,这将是线性时间(set 将删除重复项):

result = (sum(set(arr))*k - sum(arr)) / (k - b)

【讨论】:

  • 我认为结果应该除以k-b 以获得所需的数字 bcoz 数字是第一次总和的 k 次和第二次总和的 b 次。
  • 你怎么能确定它是线性的?? ......是在python线性中设置函数吗?
  • 有趣的解决方案,但它有两个问题。首先,Python 很可能在其集合实现中使用散列,因此使用集合是作弊。其次,它仅在数组元素是整数时才有效,问题中没有说明,它们可能是字符串。
  • @Amol Sharma: set() in Python is linear 但它使用散列。
  • @Ali 是的,我忘记了散列 - 我想在没有散列的情况下删除重复项不会很快:((但我在一条评论中读到 number are random not necessarily between 1 to n 所以我认为它们是数字。 ..
【解决方案5】:

如果 'k' 是偶数而 'b' 是奇数,那么 XOR 就可以了。 :)

【讨论】:

    猜你喜欢
    • 2021-05-27
    • 2015-12-02
    • 1970-01-01
    • 2016-12-07
    • 2017-01-29
    • 1970-01-01
    • 2014-05-21
    • 2018-11-18
    • 2020-04-03
    相关资源
    最近更新 更多