一次通过在 O(n) 时间内从大量 URL 列表中找到唯一的 URL

Posted

技术标签:

【中文标题】一次通过在 O(n) 时间内从大量 URL 列表中找到唯一的 URL【英文标题】:Finding a unique url from a large list of URLs in O(n) time in a single pass 【发布时间】:2012-07-01 05:20:58 【问题描述】:

最近我在一次采访中被问到这个问题。我在 O(n) 时间内给出了答案,但分两次。他还问我如果 url 列表无法放入内存中该怎么做。非常感谢任何帮助。

【问题讨论】:

你能提供更多细节吗?你应该找到一个特定的网址吗?还是列表中可能有重复项的唯一项? “唯一的网址”是什么意思?给定的 URL 在列表中只出现一次?或者至少有一个 URL 只出现在列表中一次?唯一出现在列表中一次但没有给出该 URL 的 URL? 唯一的 url 意味着它在列表中只出现一次 很高兴看到你的回答 如果完整的 URL 列表不适合内存,散列 URL 可能会为您节省一些空间。 【参考方案1】:

如果这一切都适合内存,那么问题很简单:创建两个集合(选择您最喜欢的数据结构),最初都是空的。一个将包含唯一的 URL,另一个将包含多次出现的 URL。扫描一次 URL 列表。对于每个URL,如果存在于唯一集中,则将其从唯一集中移出,放入多重集中;否则,如果它在多重集中不存在,则将其添加到唯一集中。

如果集合不适合内存,问题就很困难。 O(n) 的要求并不难满足,但“单次通过”的要求(似乎排除了随机访问等)是困难的;我认为如果没有对数据的一些限制,这是不可能的。您可以使用对集合有大小限制的集合方法,但这很容易被不幸的数据排序所破坏,并且无论如何只有一定的概率(

编辑:

如果您可以设计一个存在于大容量存储中的集合数据结构(因此它可以大于内存中的容量)并且可以在 O(1)(摊销)时间内进行查找、插入和删除,那么您可以只需使用该结构和第一种方法来解决第二个问题。也许面试官所寻找的只是将 URL 转储到具有 URL 唯一索引和计数列的数据库中。

【讨论】:

如果它们不适合内存,持久哈希表会有所帮助 @FelicePollano - 是的,当你的评论出现在我的屏幕上时,我正在写的内容。 :) @FelicePollano - 但是,我不知道支持 O(1) 操作的持久哈希表实现。没有它,它可以用来满足单次通过的要求,但不能满足 O(n) 的要求。我知道的最好的方法是 O(n log n)。 同意你提出的db方案,是否满足复杂性约束?【参考方案2】:

可以尝试使用 Trie 结构来保存数据。它被压缩了,所以它会占用更少的内存,作为常见 url 部分的内存重用。

循环看起来像:

add string s to trie;
check that added string is not finished in existing node
    internal node -> compress path
    leaf node -> delete path

【讨论】:

我想知道它是否满足复杂性要求。在 Trie(或 Patricia 树或任何自定义版本)中插入不是严格意义上的 O(1),是吗? 我知道它是 O(s),其中 s = 插入字符串的长度。 MM,你认为提出的算法是O(n)吗? 我只是使用了一个 python 字典,并使用 url 作为字典的键,并计算了每个 url 的出现次数。现在在第二遍中,我迭代了 dict 以获取计数。 ——mousey 11 小时前 所以我们要么忘记这些小事,要么我们必须注意每一个细节。【参考方案3】:

对于“适合内存”的情况,您可以使用如下两个哈希表(伪代码):

hash-table uniqueTable = <initialization>;
hash-table nonUniqueTable = <initialization>;
for-each url in url-list 
    if (nonUniqueTable.contains(url)) 
        continue;
    
    else if (uniqueTable.contains(url)) 
        nonUniqueTable.add(url);
        uniqueTable.remove(url);
    
    else 
        uniqueTable.add(url)
    

if (uniqueTable.size() > 1)
    return uniqueTable.first();

【讨论】:

如果数据不适合内存? @Ted Hopp:我不认为它可以一次性完成,尽管我还不能“证明”这一点。 (是的,虽然上述解决方案没有存储 all 的 URL,只存储每个多个 URL 的一个实例,但我怀疑它算作第二个问题的一般解决方案。)【参考方案4】:

基于 Python

你有一个list - 不确定它“来自”哪里,但如果你已经在内存中找到它,那么:

L.sort()
from itertools import groupby
for key, vals in groupby(L, lambda L: L):
    if len(vals) == 1:
       print key

否则使用存储(可能使用):

import sqlite3
db = sqlite3.connect('somefile')
db.execute('create table whatever(key)')

将数据放入其中,然后执行“select * from any group by key where count(*) = 1)”

【讨论】:

【参考方案5】:

这实际上是一个经典的面试问题,他们期望的答案是您首先对 url 进行排序,然后进行二进制搜索。 如果它不适合内存,您可以对文件执行相同的操作。

【讨论】:

好点,尽管您的方法仍然需要不止一次通过。 这将是我与面试官辩论的事情,因为我不完全确定 URL 的排序是否应该考虑在内,因为这很可能是静态数据并且应该存储那样,但如果是这样,那么您可以将您要查找的 url 包含在排序中,找到它的位置,然后左右查看列表中是否有另一个类似的 URL 但是基数排序本身需要在这里多次通过。

以上是关于一次通过在 O(n) 时间内从大量 URL 列表中找到唯一的 URL的主要内容,如果未能解决你的问题,请参考以下文章

在 O(n) 时间内从容器中移除 <number> 个元素

一次显示 n 个列表元素,jQuery

CH2401送礼物

大量内存溢出导致堆大小在大约 8 秒内从大约 64mb 变为 1.5gb。垃圾收集器的问题?

在列表中找到一个下降。这可以在 O(log n) 中完成吗?

前缀 " 不能在同一起始元素标记内从 " 重新定义为 <url>