关于模糊标题的道歉,并不确定如何描述问题。
我最近碰到了一个案例,我必须遍历一个对象数组来比较多个值,我选择在for循环中使用for循环来比较每个对象和其他所有对象。
虽然这在小数组上运行良好,但是一旦我的数组变得更大(比如10,000个对象),性能往往是个大问题。
此数组包含以下类型的对象:
[{ char: '_', from: 0, to: 2, chrLength: 2 },
{ char: '_', from: 0, to: 7, chrLength: 7 },
{ char: 'a', from: 1, to: 3, chrLength: 2 },
{ char: 'a', from: 1, to: 6, chrLength: 5 },
{ char: '_', from: 2, to: 7, chrLength: 5 },
{ char: 'a', from: 3, to: 6, chrLength: 3 }]
我的想法是,我只能选择from
和to
不与任何其他对象重叠的对象。 (from
和to
是另一个数组中的索引)
因此,对于示例数组,可能的结果将是:
[{ char: '_', from: 0, to: 2, chrLength: 2 },
{ char: 'a', from: 1, to: 3, chrLength: 2 },
{ char: 'a', from: 1, to: 6, chrLength: 5 },
{ char: 'a', from: 3, to: 6, chrLength: 3 }]
我处理它的方式如下:
var canUse = true,
posibilities = [];
for(i = 0; i < l; i++) {
canUse = true;
for(var j = 0; j < l; j++) {
if((results[i].from < results[j].from && results[i].to > results[j].to)) {
canUse = false;
break;
}
}
if(canUse) posibilities.push(results[i]);
}
对于更大的阵列,看到性能非常糟糕,我想知道是否有更好的解决方案可以做到这一点?
答案 0 :(得分:1)
首先对chrLength
属性上的对象进行排序。当您查找使对象不被包含的对象时,您只需要检查至少两个字符的对象。
results.sort(function(x, y){ return x.chrLength - y.chrLength; });
var posibilities = [];
for (var i = 0; i < l; i++) {
var canUse = true, len = results[i].chrLength - 2;
for (var j = 0; results[j].chrLength <= len; j++) {
if((results[i].from < results[j].from && results[i].to > results[j].to)) {
canUse = false;
break;
}
}
if(canUse) posibilities.push(results[i]);
}
使用您的示例数据,这会将检查次数从原始代码中的36减少到仅为8。
比较:http://jsfiddle.net/Guffa/5jsSb/
您可以创建一个数组,其中每个项目都是具有相同chrLength
的对象数组,然后对from
属性上的每个数组进行排序。通过这种方式,您可以轻松跳到对象开始重叠的位置,并在它们不再重叠时立即停止比较:
var map = [];
for (var i = 0; i < l; i++) {
var ch = results[i].chrLength;
while (map.length <= ch) map.push([]);
map[ch].push(results[i]);
}
for (var i = 1; i < map.length; i++) {
map[i].sort(function(x, y){ return x.from - y.from; });
}
var posibilities = [];
for (var i = 0; i < l; i++) {
var canUse = true, len = results[i].chrLength - 2, from = results[i].from, to = results[i].to;
for (var j = 1; canUse && j <= len; j++) {
if (map[j][map[j].length - 1].from > from) {
var k;
for (k = 0; map[j][k].from <= from; k++);
for (;k < map[j].length && map[j][k].from < to; k++) {
if (map[j][k].to < to) {
canUse = false;
break;
}
}
}
}
if(canUse) posibilities.push(results[i]);
}
这将分两个阶段对from
和to
属性的检查进行拆分,因此完整检查的数量(评估map[j][k].to < to
的数量)实际上小于总数对象。
免责声明:当然,您需要验证代码是否正确。我已经检查过结果的项目数是否相同,但我没有比较每个项目。
答案 1 :(得分:1)
这是一个想法(Demo):
(from + to)/2
。k
项”插入树中并即将插入k+1
。您的步骤是:k+1
覆盖根目录 - 请忽略它。k+1
- 从树中删除根并再次尝试。k+1
的mid和root的子树,继续左边或右边的子树。代码
function process() {
console.log('Processing results of length: ' + l);
console.time('Processing');
var comparator = function(a, b) { //Comparator to build a tree
return a.mid - b.mid;
},
isAinB = function(a, b) { //util function to check if a is inside b
return b.from < a.from && b.to > a.to;
},
rbtree = new RBTree(comparator), //Build an empty tree
i = results.length - 1, item, posibilities = [];
function check(root, x) { //Recursive checker
var data;
if(!root) { //Either tree is empty or we've reached a leaf
rbtree.insert(x);
return;
}
data = root.data;
if(isAinB(data, x)) { //4
return;
}
if(isAinB(x, data)) { //5
rbtree.remove(data);
check(rbtree._root, x);
return;
}
check(root[comparator(data, x) > 0 ? 'left' : 'right'], x); //6
}
for(; i >= 0; i--) {
item = results[i];
item.mid = (item.from + item.to)/2; //2
check(rbtree._root, item); //3
}
rbtree.each(function(item) { //7
posibilities.push(item);
});
console.timeEnd('Processing');
console.log(posibilities.length);
}
BTW我已经使用了这个RBTree implementation。不确定它是否是最好的一个:)
答案 2 :(得分:0)
对于初学者来说,一旦canUse
为false
,您就不需要继续进行内循环了。
您可以添加break;
或将第二个for循环更改为:
for (var j = 0; canUse && (j < l); j++)
你可能会看到一个有用的加速。