我们给出了n组不同大小的整数。每个集合也可以包含重复项。我必须找到集合的交集。如果一个元素在所有集合中多次出现,则应将其添加到结果中。
例如,考虑有三组{0,5,5,3,4} {5,2,3,5,6} {1,3,5,5,6}。给定集合的交集应为{3,5,5}
我的方法是:
1.排序数组。
2.比较从最小数组开始的每个元素并更新计数。
找到交叉点是否有更有效的方法?
答案 0 :(得分:3)
如果你的“集合”只包含小整数,那么它们可以用一系列计数表示......例如,{5,2,3,5,6}是
index 0 1 2 3 4 5 6
count 0 0 1 1 0 2 1
这些集合的交集是计数的最小值:
index 0 1 2 3 4 5 6
-------------
{0,5,5,3,4} 1 0 0 1 1 2 0
{5,2,3,5,6} 0 0 1 1 0 2 1
{1,3,5,5,6} 0 1 0 1 0 2 1
min 0 0 0 1 0 2 0 = {3,5,5}
如果值不是小整数,但它们很少,只需保留一个值数组 - 用作值和小整数之间的映射,小整数是数组的索引。
如果有这么多的值,每个集合的计数数组太贵,请使用从值到计数的映射来表示每个“集合”,以及值的数组...然后迭代数组生成每个值,迭代映射以获取计数并计算它们的最小值。为此,您需要一个哈希表或二叉树库来实现这些地图......或者使用比C更多的现代语言中的任何一种语言来提供这样的集合类型。
答案 1 :(得分:0)
O(n*m)
,其中M是您的集合大小的最大值,N是它们的数量,而如果您对集合进行排序,则复杂度为O(n*m*log(m))
如果你的集合每个都包含1000多个元素,则会大得多。
答案 2 :(得分:0)
这是我的代码,在C99中编译(不要忘记先实现get,insert,remove函数):
struct MyNode { MyNode * next; int value; int frequency; }
// returns MyNode pointer when value exist
MyNode * get(MyNode * head, int val);
// insert a new value, with frequency = 1
void insert(MyNode * head, int val);
// remove an element from the linked-list
bool remove(MyNode * head, int val);
int * intersection (int ** set, int w, int * h)
{
MyNode * head = 0;
MyNode * temp = 0;
int finalSize = 0;
int k = 0;
for (int i=0; i<w; i++)
{
for (int j=0; j<h[i]; j++)
{
temp = get(head, set[i][j]);
if (temp == 0)
{
insert(head, set[i][j]);
finalSize++;
}
else
{
temp->frequency++;
}
}
}
temp = head;
while (temp != 0)
{
if (temp->frequency != w)
{
temp = temp->next;
remove(head, temp->value);
finalSize--;
}
else
temp = temp->next;
}
int * intersection = (int*)malloc(finalSize*sizeof(int));
temp = head;
while (temp != 0)
{
intersection[k++] = temp->data;
temp = temp->next;
}
return intersection;
}
答案 3 :(得分:0)
我建议你的解决方案唯一的优化是将你的数组(它们不是真正的集合,因为它们有重复)转换为键值字典,这样键就是数组的元素,值将是发生的次数。对于您的测试示例:{0,5,5,3,4} {5,2,3,5,6} {1,3,5,5,6}字典看起来像那样
{0 => 1, 3 => 1, 4 => 1, 5 => 2}
{2 => 1, 3 => 1, 5 => 2, 6 => 1}
{1 => 1, 3 => 1, 5 => 2, 6 => 1}
然后,您可以从最小的字典开始比较字典对,如果元素出现在两者中 - 您可以使用较少的出现次数。 此优化将节省处理重复项所需的时间。
结果字典将是:{3 =&gt; 1,5 =&gt; 2} - 你可以将它转换回数组。
答案 4 :(得分:0)
其他人已经涵盖了用arrays of counts, or maps of counts表示每个“集合”(或更正式地说是“袋子”)的想法。如果正在进行大量重复操作,并且每个包中没有那么多钥匙,则此功能特别有用。给定N个包,每个包中有M个元素,其中K个是不同的,转换成数组/地图表示形式和生成结果的复杂度将为O(N x M) + O(N x K)
。请注意,重复查找B袋的交集仅花费O(B x K)
,因为您可以重复使用地图表示。
如果正确地订购成对的交叉点,还可以提高效率。例如,如果其中一个袋仅包含一个元素,则只有两个可能的答案:所有其他袋也都包含该元素(结果是该元素本身),或者至少其中一个不包含。这将使您完全忽略其他集合的其余内容。在这种极端情况下,实际路口的运行时间将降至O(N)
,提高了K倍。
通常,如果背包中的唯一元素数量差异很大,则按大小(唯一元素数量)的大小对地图进行排序会增加O(N log N)
的费用,但您可以跳过计算交点时需要很多键,将交点时间减少到O(N x K_min)
,其中K_min
是最小唯一元素数的大小。
在数据库查询优化过程中也做了类似的事情,以大大缩短查询时间。