我最近在接受采访时被问到这个问题 . 我有一个n元素的数组 . 该数组只有100个不同的值 . 我需要打印每个数字的出现次数 .
1<=n<=10^6
1<=A[i]<=10^12
预期的空间复杂度为O(k),其中 k
是数组中不同值的数量 .
例如, 1 2 3 2 1 4 3 2 4 2 3 1 2
;这里 k
是 4
. 首先我建议在stl中使用map,但他希望他实现我自己的数据结构 . 然后我建议对每个元素使用排序插入,就像在二叉搜索树中一样,但这会给出O(nlogn)的时间复杂度 . 他想要一个O(n)解决方案 . 我试着考虑任何哈希函数,但我无法想出任何这样的函数 . 我还试图考虑trie数据结构,但我将再次扫描每个数字的每个数字,从而再次给出O(nlogn)复杂度 . 有什么办法可以解决这个问题?
3 回答
哈希表不保证O(n * k)的理论复杂度 . 但制作这样的东西很容易 .
首先,我们需要对值概率分布做一些假设 - 让它统一(或者我们需要一些专门的散列函数) .
接下来,让我们选择哈希表大小,比如201条目(因此它将小于50%已满) .
接下来,让哈希函数只是
hash(A[i]) = A[i] mod 201
.然后使用具有201个条目对的开放寻址哈希表H []:A [i]或NULL;频率值 .
我认为哈希表是一个很好的解决方案,但我想面试官期望你构建自己的哈希表 .
这里's a solution I came up with in Python. I' m使用
mod 100
作为我的哈希函数并使用Separate chaining来处理冲突 .EDIT
稍微解释一下代码:
我的哈希表是一个100个元素的数组 . 数组中的每个条目都是
(number, count)
条目的列表 . 要散列数字,我将其值取模100来查找数组的索引 . 我扫描那个桶中已有的数字,如果它们中的任何一个与当前数字匹配,我会增加它的计数 . 如果我没有找到该号码,我会在列表中添加一个新条目,其中包含数字和初始计数为1 .在视觉上,数组看起来像这样:
注意,在索引n处,存储在桶中的每个值等于n(mod 100) . 平均而言,每个桶只有一个值,因为阵列中最多有100个不同的值和100个元素 .
要打印出最终计数,所需要的只是遍历阵列和每个桶中的每个条目并将其打印出来 .
EDIT 2
这是一个稍微不同的实现,它使用Open addressing代替线性探测 . 我想我其实更喜欢这种方法 .
注意:如果实际上有超过100个不同的数字,则此代码将失败 . (它将永远挂起,试图在阵列中找到一个开放点 . )保持检测到这种情况(例如,一旦你在阵列中走了整整一圈)并引发异常将会很好 .
实际上,你错了,特里会给你复杂性 .
trie的一次插入/查找/擦除操作需要
O(L)
时间,其中L
是推入此trie的字符串的长度 . 幸运的是,您只需插入不超过1万亿的数字,这意味着L
不大于log(10^12)
(对数基数取决于您在此系列中使用的计数系统 . 我个人会选择256
或65536
,具体取决于整个系统的哪个部分这种结构是否有效) .总结一下,
O()
的定义需要O(N) * O(log(10^12))
等于O(N)
.