合并分歧的数组

时间:2018-01-23 20:03:32

标签: javascript arrays

我有一个初始数组s0我会复制两次,提供s1s2。现在s1s2由于并发插入而分歧。

我知道s1是通过将一个字符插入s0(在随机位置)获得的。同样,s2派生自s0并随机插入。但是,我无法再访问s0

如何合并s1s2,就像在s0上的相应位置上发生了两次添加一样。

举个例子:

s0 = [1, 4, 9, 11]
s1 = [1, 4, 11, 9, 11]
s2 = [1, 3, 4, 9, 11]
-----
s3 = merge(s1, s2) = [1, 3, 4, 11, 9, 11]

(如果s1s2都是通过在相同的索引处插入字符获得的,那么我的订单在s3中的含义并不重要,两者都有效)

因为我知道s1s2只有2个不同,所以我首先搜索第一个差异的索引。

var i = 0;
while (s1[i] === s2[i]) i++;

现在我知道第一个区别在于索引i,但是我无法确定s1[i]是否是添加的字符(因此,必须添加到{ {1}}索引s2)或i是添加的字符。

2 个答案:

答案 0 :(得分:3)

我会建议这个功能。在找到具有差异的第一个索引之后,它向前看以查看下一个值是否与任一原因一致(即分别在第一个或第二个数组中插入)。从下一个角色来看,这可能并不明显,因此需要一个循环来明确这一点。

一旦清楚插入发生在哪个数组中,所有信息都在那里构建最终数组:

function merge(s1, s2) {
    let i, j;
    for (i = 0; i < s1.length; i++) {
        if (s1[i] !== s2[i]) break;
    }
    for (j = i; j < s1.length; j++) {
        if (s1[j+1] !== s2[j]) return s2.slice(0, i + 1).concat(s1.slice(i));
        if (s1[j] !== s2[j+1]) return s1.slice(0, i + 1).concat(s2.slice(i));
    }
    // If we get here, both mutations were the "same"
    return s1[i].slice(); // ignore one.
}

const s1 = [1, 4, 11, 9, 11],
      s2 = [1, 3, 4, 9, 11];
console.log(merge(s1, s2));

解决方案并非总是唯一

有时s3有多个可能的值。例如,如果s1=[1, 2]s2=[2, 1],则无法确定s0=[1]还是s0=[2]

如果你想检测这种情况并获得两种解决方案,那么让函数返回一系列解决方案:

function merge(s1, s2) {
    let i, j, solutions = [];
    for (i = 0; i < s1.length; i++) {
        if (s1[i] !== s2[i]) break;
    }
    for (j = i; j < s1.length; j++) {
        if (s1[j+1] !== s2[j]) solutions.push(s2.slice(0, i + 1).concat(s1.slice(i)));
        if (s1[j] !== s2[j+1]) solutions.push(s1.slice(0, i + 1).concat(s2.slice(i)));
        if (solutions.length) return solutions;
    }
    // If we get here, both mutations were the "same"
    return [s1[i].slice()]; // ignore one.
}
const s1 = [1, 5, 4], 
      s2 = [1, 3, 4];
console.log(merge(s1, s2)); // two solutions

答案 1 :(得分:1)

一种方法是同时进行这些操作。如果它们在给定索引处的值不同,请同时预测它们。如果一个人在前瞻中的值与当前位置中的另一个值相同,那么可能是那个上的值。

例如,查看上面的示例:

s1 = [1, 4, 11, 9, 11]
s2 = [1, 3, 4, 9, 11]

指数0,两者都是一样的。指数1,它们是不同的。查看每个的索引2,您会看到s2s1的当前索引具有相同的值,因此s2可能有插入,因此请在{{1}之前添加该值1}}。之后,再次将它们重新排列(所以请先查看s1一个。

s2

此代码适用于您的特定示例。请注意,还有一个额外的。如果你到达那里,就意味着在一个插件中发生插入,但要么他们都在同一个地方插入,要么插入多个插入。您必须遍历两者并确定如何解决,但基本方法将是相同的:循环直到您找到重新排列的位置,然后在适当增加迭代器之前采用不匹配的方法。

我只是更仔细地重新阅读你的问题并意识到你知道在合并之前每个只发生一次操作。这使事情变得更加容易,因为你不必循环任意距离,你最多只需要两个。

如果你知道他们不会在同一个地方,那么你可以在上面的代码中删除其他内容,因为它永远不会发生。

如果他们可能处于相同位置,因为您知道您只有两个更改,如果两个值都不匹配(并且您知道每个值只接收一个更改),则两个都在同一点收到更改。在这种情况下,只需要两个。如果在这种情况下需要特定订单,则必须定义一些逻辑。但总的来说,可能并不重要,所以只需先拿一个。

const s1 = [1, 4, 11, 9, 11];
const s2 = [1, 3, 4, 9, 11];
const s3 = [1, 3, 4, 11, 9, 11]; // for comparison

function merge(a, b) {
  let iA = 0;
  let iB = 0;
  let result = [];
  let abort = 0;
  
  while (abort < 100 && iA < a.length && iB < b.length) {
    if (a[iA] === b[iB]) {
      result.push(a[iA]);
      iA++;
      iB++;
    } else if (a[iA+1] === b[iB]) { // a had an insert
      result.push(a[iA]); 
      iA++;
    } else if (a[iA] === b[iB+1]) { // b had an insert
      result.push(b[iB]);
      iB++;
    } else {
      // extra stuff here
    }
    
    abort++; // this is just so no infinite loop in partially defined loop
  }
  
  // Catch values at the end
  if (iA < a.length) {
    result = result.concat(a.slice(iA));
  } else if (iB < b.length) {
    result = result.concat(b.slice(iB));
  }
    
  return result;
}

console.log(merge(s1, s2));