【问题标题】:Merge arrays that diverged合并发散的数组
【发布时间】:2018-07-02 18:11:16
【问题描述】:

我有一个初始数组s0,我复制了两次,分别给出s1 和s2。现在 s1 和 s2 由于并发插入而分道扬镳。

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

我如何将s1 和s2 组合起来,就好像这两个添加都发生在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]

(如果s1和s2都是通过在同一索引处插入一个字符获得的,我不管它们在s3中的顺序是什么,都是有效的)


因为我知道s1 和s2 正好有两个不同,所以我首先搜索第一个不同的索引。

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

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

【问题讨论】:

  • “但是,我无法再访问s0” 这怎么可能?更重要的是,如何验证结果?
  • 我想也许既然您知道确切有两个差异,我们可以确定共同部分并在共同路径之间插入字符?类似的东西:)
  • @guest271314 这是在研究最终一致性的背景下,解释一切都太复杂了。所以接受我们不知道原始状态s0。
  • @HyperZ 不,它不会“太复杂”。您是否试图复制新的(对于那些“接受”的人来说是强制性的)“芯片”借记卡/信用卡的功能?也就是使用量子密码学?
  • 我认为这个问题与 JavaScript 没有太大关系。如果您知道该算法并且在实现它时遇到了困难,那就可以了,但这似乎与语言无关。

标签: javascript arrays


【解决方案1】:

一种方法是同时检查它们。如果它们在给定索引处的值不同,请先查看它们。如果一个在前瞻中的值与另一个在当前位置的值相同,则可能是对那个的插入。

例如,查看上面的示例:

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

索引 0,两者相同。索引1,它们是不同的。查看每个索引 2,您会看到 s2 与 s1 的当前索引具有相同的值,因此 s2 可能有一个插入,所以在 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));

此代码适用于您的特定示例。请注意,还有一个额外的东西。如果你到了那里,这意味着一个插入发生在一个地方,但要么他们都在同一个地方插入,要么一个有多个插入。您必须遍历两者并确定如何解决,但基本方法是相同的:遍历直到找到再次排列的位置,然后在适当地递增迭代器之前获取不匹配的那些。


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

如果你知道它们不会在同一个位置,那么你可以去掉上面代码中的 else,因为它永远不会发生。

如果它们可能处于相同位置,因为您知道您只有两个更改,如果两个值都不匹配(并且您知道每个值都只收到一个更改),那么两者都在同一点收到了更改。在这种情况下,就两者兼而有之。如果他们在这种情况下需要特定的顺序,则必须定义一些逻辑。不过一般来说,可能没关系,所以先选择其中一个。

function merge(a, b) {
  let iA = 0;
  let iB = 0;
  let result = [];
  
  while (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 { // both received a change, take both
      result.push(a[iA]);
      result.push(b[iB]);
      iA++;
      iB++;
    }
  }
  
  // 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([1, 4, 11, 9, 11], [1, 3, 4, 9, 11])); // different places
console.log(merge([1, 3, 4, 9, 11], [1, 2, 4, 9, 11])); // same places
console.log(merge([1, 4, 9, 11, 13], [1, 3, 4, 9, 11]));

【讨论】:

    【解决方案2】:

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

    一旦确定插入发生在哪个数组中,所有信息就可以构建最终数组:

    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

    【讨论】:

    • 投反对票的人能用这个答案解释问题吗?
    • 我没有投反对票,但请考虑以下几点:merge([3, 1, 4, 9, 11], [1, 3, 4, 9, 11]) 返回[1,3,1,4,9,11],但交换参数并得到[3,1,3,4,9,11]。在不知道s0 的情况下可能无法确定哪个是正确的。
    • @RickHitchcock,确实,在这种情况下,s0 有两种可能性。
    • 首先感谢您的努力! @RickHitchcock 因为两者都插入了相同的位置,所以确实有两种可能性,这两种可能性都是正确的。
    • 我在答案中添加了一个 sn-p,它将返回一个解决方案数组,其中可能有 1 个或 2 个元素(数组)。
    猜你喜欢
    • 2014-11-22
    • 2011-07-26
    • 2015-02-03
    • 1970-01-01
    • 2015-09-18
    • 2010-10-10
    • 1970-01-01
    • 2017-11-18
    • 2019-12-22
    相关资源
    最近更新 更多