目前使用JavaScript我需要通过一组数组来确定是否有任何重复的数组,然后删除那些重复的数组。在这种情况下,运行时是至关重要的,所以我想知道最有效的方法是什么。
在这种情况下使用哈希表是否合适?这样做的范围是散列每个序列,然后使用散列来确定该序列是否再次出现。因此,每个序列是主阵列中的一个阵列,任何重复序列都是同一阵列中的其他阵列。此外,非常重要的是所有单独的阵列本身保持有序(即,各个阵列中的元素必须始终保持其位置)。此外,单个数组中的所有元素都是字符串值。
示例:假设有一个数组A ,其元素又包含以下数组:
A[0] = ["one", "two", "three", "four"]
A[1] = ["two", "one", "three", "four"]
A[2] = ["one", "two", "three", "four"]
在上面的例子中,A [0]和A [2]是重复的,因此函数应该返回A [0]和A [1],这样只有一个相同数组的实例。
答案 0 :(得分:7)
保持一个对象,其中键是每个数组的连接元素。如果未找到密钥,请将数组添加到输出数组并将密钥添加到对象。
var hash = {};
var out = [];
for (var i = 0, l = A.length; i < l; i++) {
var key = A[i].join('|');
if (!hash[key]) {
out.push(A[i]);
hash[key] = 'found';
}
}
答案 1 :(得分:1)
好的,让我们先来看看天真解决方案的复杂性:
如果有n个数组,每个数组最多有k个条目,则需要进行O(n^2 * k)
比较,因为对于这n个数组中的每一个,您必须将它与n-1个进行比较,每个数组都进行k次比较。空间复杂度为O(n*k)
因此,如果您愿意换空间以获得更好的性能,您可以执行以下操作: (简短的免责声明:我假设你的所有数组都有相同数量的k元素,这些元素已被指明但未被您的问题批准。)
逐个遍历数组,选择我们假设为a
的第一个元素。
使用哈希映射来验证您是否将此元素视为之前的第一个元素。如果没有,请创建一个以a
为根的树结构,将其存储在哈希映射中的a
下,并将其设置为当前节点。
现在,对于当前数组中的每个后续条目,检查当前节点是否具有该类型的子节点。因此,如果第二个条目为b
,则将b
添加为a的子级。
您的树现在看起来像这样:(从左到右:从根到儿童)
a - b
将c
作为第三个条目完全相同:
a - b - c
现在我们跳过去查看一个数组[a, c, d]
。
您首先遇到元素a
的树。对于第二个元素,检查c
是否已经是a的子元素。如果没有,请添加它:
- b - c
a
- c
下一个条目也是如此:
- b - c
a
- c - d
现在让我们看看当我们检查之前看到的数组时会发生什么:[a, b, c]
首先我们检查a
,看看已经存在一棵树并从哈希映射中获取它。接下来,我们注意到a
有一个名为b
的孩子,所以我们下降到b
。现在,对于最后一个条目,我们看到它已经存在,告诉我们我们遇到了一个我们可以删除的副本。
对于即兴绘图感到抱歉,我希望我可以理解这个想法。
它只是通过每个数组一次,以非冗余的方式存储它。
所以时间复杂度为O(n*k)
。使用的空间增加但受O(n*k)
限制,因为最坏的情况是没有数组共享任何前缀,这导致相同的空间复杂性。
希望我没有忽视某些事情。
答案 2 :(得分:0)
A.filter((r={},a=>!(r[a]=++r[a]|0)))
我假设您的字符串不包含,
字符。如果包含,则将r[a]
更改为r[a.join('|')]
(其中|
是任意分隔符)两次,或使用r[a.map(x=>x.length+','+x)]
允许字符串中的所有字符。这是working example。
说明
在r={}
中,我们只设置了一个临时对象。在过滤器函数a=>...
中,仅用于在参数r={}
中声明一次空的临时对象。在a=>...
中的函数a
中,我们具有当前的A
元素。 JS将a
隐式转换为r[a]
中的字符串。然后在!(r[a]=++r[a]|0)
中,我们增加出现元素a
的计数器,如果元素a
第一次出现,则返回true(作为过滤器功能值)。