我正在寻找解决以下问题的最有效方法
问题:
given an array Before = { 8, 7, 2, 1} and an array After ={1, 3, 8, 8}
find the added and the removed elements
the solution is:
added = 3, 8
removed = 7, 2
到目前为止我的想法是:
for i = 0 .. B.Lenghtt-1
{
for j= 0 .. A.Lenght-1
{
if A[j] == B[i]
A[j] = 0;
B[i] = 0;
break;
}
}
// B elemnts different from 0 are the Removed elements
// A elemnts different from 0 are the Added elemnts
有没有人知道一个更好的解决方案,或许更有效,而且不会覆盖原始数组
答案 0 :(得分:9)
排序是你的朋友。
对两个数组(a和b)进行排序,然后遍历它们(使用x和y作为计数器)。一次向下移动1。您可以从那里获得所有测试:
(我可能错过了一个边缘案例,但你得到了一般的想法。)
(编辑:这里没有涉及的主要边缘情况是当你在另一个数组之前到达其中一个数组的末尾时的处理,但是不难发现。)
答案 1 :(得分:5)
您还可以使用Dictionary<int, int>
和类似的算法:
foreach i in source_list: dictionary[i]++;
foreach i in dest_list: dictionary[i]--;
最终字典会告诉您插入/删除了哪些元素(以及频率)。即使对于较大的列表,此解决方案也应该非常快 - 比排序更快。
答案 2 :(得分:2)
如果您的语言为多重集(可使用元素计数设置),则您的问题是标准操作。
B = multiset(Before)
A = multiset(After)
结果是A.symdiff(B)(symdiff是union减去交集,这正是你要删除和添加的内容)。
显然,您也可以仅删除或仅使用集合之间的经典差异添加。
它可以简单地使用哈希实现,它的O(n)(使用sort的效率稍低,因为它是O(n.log(n))因为排序本身)。
答案 3 :(得分:1)
在某种C ++伪代码中:
Before.sort();
After.sort();
int i = 0;
int j = 0;
for (; i < Before.size() && j < After.size(); ) {
if (Before[i] < After[j]) {
Removed.add(Before[i]);
++i;
continue;
}
if (Before[i] > After[j]) {
Added.add(After[j]);
++j;
continue;
}
++i;
++j;
}
for (; i < Before.size(); ++i) {
Removed.add(Before[i]);
}
for (; j < After.size(); ++j) {
Added.add(After[j]);
}
答案 4 :(得分:1)
这可以在线性时间内解决。
创建一个用于计算每个元素重复次数的映射。
浏览before
数组并填充地图。
浏览after
数组并减少每个元素的地图值。
最后,浏览地图,如果找到负值,则添加该元素 - 如果为正,则删除该元素。
这是一些Java代码(未经测试,现在刚刚编写):
Map<Integer, Integer> repetitionMap = new HashMap<Integer, Integer>();
for (int i = 0; i < before.length; i++) {
Integer number = repetitionMap.get(before[i]);
if (number == null) {
repetitionMap.put(before[i], 1);
} else {
repetitionMap.put(before[i], number + 1);
}
}
for (int i = 0; i < after.length; i++) {
Integer number = repetitionMap.get(after[i]);
if (number == null) {
repetitionMap.put(after[i], -1);
} else {
repetitionMap.put(after[i], number - 1);
}
}
Set<Integer> keySet = repetitionMap.keySet();
for (Integer i : keySet) {
Integer number = repetitionMap.get(i);
if (number > 0) {
System.out.println("removed " + number + "times value " + i);
}
if (number < 0) {
System.out.println("added " + number + "times value " + i);
}
}
答案 5 :(得分:0)
perl的:
@a = ( 8, 7, 2, 2, 1 ); @b = ( 1, 3, 8, 8, 8 ); $d{$_}++ for(@a); $d{$_}-- for(@b); print"added = "; for(keys %d){print "$_ " x (-$d{$_}) if($d{$_}<0)} print"\n"; print"removed = "; for(keys %d){print "$_ " x ($d{$_}) if($d{$_}>0)} print"\n";
结果:
$ ./inout.pl added = 8 8 3 removed = 7 2 2
答案 6 :(得分:0)
在Groovy中:
def before = [8, 7, 2, 1, 1, 1], after = [1, 3, 8, 8, 8]
def added = before.countBy{it}
def result = after.inject(added){map, a -> map[a] ? map << [(a):map[a] - 1]: map << [(a):-1]}
.inject([:]){m, k, v -> v == 0 ? (m << [:]) : (v < 0 ? m << [(k):"added ${v.abs()} times"] : m << [(k):"removed ${v.abs()} times"])}
println "before $before\nafter $after"
println "result: $result"
结果:
before [8, 7, 2, 1, 1, 1]
after [1, 3, 8, 8, 8]
result: [8:added 2 times, 7:removed 1 times, 2:removed 1 times, 1:removed 2 times, 3:added 1 times]
对于countBy
,我从Some groovy magic post
在groovy中inject
与其他函数式语言中的reduce
类似。
我还在Groovy collection api slides from Trygve Amundsen中提到了功能方法非常好的表
第二个解决方案:
def before = [8, 7, 2, 1, 1, 1], after = [1, 3, 8, 8, 8]
def sb = before.countBy{it}
def sa = after.countBy{it}
def result = sa.inject(sb){m, k, v -> m[k] ? m << [(k): m[k] - v] : m << [(k): -v]}
.inject([:]){m, k, v -> v == 0 ? (m << [:]) : (v < 0 ? m << [(k):"added ${v.abs()} times"] : m << [(k):"removed ${v.abs()} times"])}