我认为散列表是一个很好的解决方案,但我想象一下面试官希望你建立自己的散列表。
这是我在Python中提出的解决方案。我使用mod 100
作为我的散列函数,并使用Separate chaining来处理冲突。
import random
N = random.randint(1, 10**6)
K = 100
HASH_TABLE_SIZE = 100
distinct = [random.randint(1, 10**12) for _ in range(K)]
numbers = [random.choice(distinct) for _ in range(N)]
hash_table = [[] for _ in range(HASH_TABLE_SIZE)]
def hash(n):
hash_key = n % HASH_TABLE_SIZE
bucket = hash_table[hash_key]
for value in bucket:
if value[0] == n:
value[1] += 1
return
bucket.append([n, 1])
for number in numbers:
hash(number)
for bucket in hash_table:
for value in bucket:
print('{}: {}'.format(*value))
EDIT
解读码的位:
我的哈希表是一个100个元素的数组。数组中的每个条目都是(number, count)
条目的列表。为了散列一个数字,我取其模值为100来查找数组中的索引。我扫描那个桶中的数字,如果它们中的任何一个匹配当前的数字,我会增加它的数量。如果我没有找到多少,我追加了新的条目的数量和1
视觉上的初始计列表中,数组看起来有点像这样:
[
[ [0, 3], [34500, 1] ]
[ [101, 1] ],
[],
[ [1502, 1] ],
...
]
注意在索引n,存储在桶中的每个值等于n(mod 100)。平均而言,每个存储桶只有一个值,因为阵列中有多达100个不同的值和100个元素。
要打印出最终计数,只需要遍历数组和每个桶中的每个条目并打印出来。
EDIT 2
这里有一个稍微不同的实现,它使用Open addressing与线性探测来代替。我想我其实更喜欢这种方法。
hash_table = [None] * HASH_TABLE_SIZE
def hash(n):
hash_key = n % HASH_TABLE_SIZE
while hash_table[hash_key] is not None and hash_table[hash_key][0] != n:
hash_key = (hash_key + 1) % HASH_TABLE_SIZE
if hash_table[hash_key] is None:
hash_table[hash_key] = [n, 1]
else:
hash_table[hash_key][1] += 1
for number in numbers:
hash(number)
for entry in hash_table:
print('{}: {}'.format(*entry))
注:此代码将失败如果确实有超过100个不同的数字。 (它会永远挂起,试图在数组中找到一个开放的位置)。保持检测到这种状态(例如,一旦你走完了数组中的整圈)并引发异常将会很好。
如果您只关心理论上的复杂性,最简单的无限数字大小就是将该值转换为字符串,然后使用它。对于10亿个,它适合32位整数,所以你可以直接使用它,以模块的方式放置。 – Photon
成对值/计数的简单向量(大小为k)可以完成这项工作。内存复杂度为O(k),通过对数组进行“排序”,复杂度可以是“O(n * k)”(可以降低到“O(n * log(k)))”。 – Jarod42
一个可能与你有关的随机评论。作为一个采访者,我的第一个问题是“什么是最坏的情况?”,所以我希望你能理解哈希表是如何处理碰撞的,以及为什么不能达到理想的O(n)最坏情况的性能。 – Charlie