在数组中查找最接近的匹配对

时间:2019-04-16 13:45:51

标签: javascript algorithm big-o

我正在尝试使用n次算法来识别数组中最接近的匹配对。

我在下面的副本中创建了一个解决方案,但是我不确定这是什么大问题。我想也许是(Onlogn)或O(logn)。如果可能的话,我希望将其恢复为O(n)时间。

let closestMatchingPair = function(array) {
  if(!array || array.length < 2) {
		return 'invalid data get bent';
	}
	let pairFound = false;
	let position1 = 0;
	let position2 = 0;
	let distance = array.length;

	object = {};
	//build object 
	for(let i = 0; i < array.length; i++) {
		if(!object[array[i]]) {
			object[array[i]] = [i];
		} else {
			object[array[i]].push(i);
		}
	}
  //loop over object properties
	for(let i = 0; i < array.length-1; i++) {
		if(object[array[i]].length >= 2) {
			var closest = object[array[i]].filter(a => a > i).shift();
			if(distance > (closest - i)) {
				position1 = i;
				position2 = closest;
				distance = closest - i;
				pairFound = true;
			}
		}
	}

	return {
		pairFound: pairFound,
		position1: position1,
		position2: position2,
		distance: distance
	}
}

//example [1] = 'invalid data get bent'
//example [1,2] = { pairFound: false, position1: 0, position2: 0, distance: 2 }
//example [1,2,1] = { pairFound: true, position1: 0, position2: 2, distance: 2 }
//example [1,2,1,3,2,2] = { pairFound: true, position1: 4, position2: 5, distance: 1 }
let array = [1,2,1,3,2,2];

console.log(closestMatchingPair(array));

https://repl.it/@zack_fanning/PushyGoodnaturedOutput

按预期工作,希望尽可能避免在第22行使用过滤器

var closest = object[array[i]].filter(a => a > i).shift();

3 个答案:

答案 0 :(得分:2)

您正在做很多不必要的工作。事先构建一个对象通常是一个好主意,但是在这种情况下这不是胜利。您只需要对数组进行一次迭代,就需要在迭代之间跟踪以下事实:

  • prevPositions –用于跟踪到目前为止看到的每个项目的最新索引的对象。
  • distance –迄今为止任何一对之间的最小距离。
  • position1position2 –到目前为止距离最小的那对索引。

(从技术上讲,distance总是可以通过从position1中减去position2来计算得出的,但是我们通过单独存储来节省一些工作。)

在每次迭代中,您需要:

  1. 检查您之前是否看过当前项目。
    • 如果是,请检查当前项目与其先前出现的项目之间的距离是否小于到目前为止的最小距离。
      • 如果是,则将新的最小距离存储在distance中,并将当前和以前出现的索引存储在position1position2中。
  2. 将当前项目的索引存储在prevPositions中。

遍历每个项目一次之后,position1position2distance将包含最接近的一对索引以及它们之间的距离。

这是 O n )。您可以在下面的代码段中看到JavaScript的外观。

function closestMatchingPair(array) {
  if (array.length < 2) {
    throw new Error('Input must have at least two items');
  }
  
  // An object to track the most recent positions of the items we've seen so far
  const prevPositions = {};

  // Variables to track the smallest distance so far and corresponding indexes
  let distance, position1, position2;

  for (let index = 0; index < array.length; index++) {
    const currentItem = array[index];

    if (currentItem in prevPositions) {
      // We've seen this item before; check if the distance from the previous 
      // instance is less than the smallest distance we've seen so far
      const currentDistance = index - prevPositions[currentItem];

      if (!distance || currentDistance < distance) {
        // New smallest distance; update our variables
        distance = currentDistance;
        position1 = prevPositions[currentItem];
        position2 = index;
        
        if (currentDistance === 1) {
          // Can't do any better than 1; no reason to continue
          break;
        }
      }
    }
    prevPositions[currentItem] = index; // Save item's index
  }

  return { pairFound: !!distance, position1, position2, distance };
}

test([1]);
test([1,2]);
test([1,2,1]);
test([1,2,1,3,2,2]);

function test(input) {
  console.log('input:', input);
  try {
    console.log('output:', closestMatchingPair(input));
  } catch (e) {
    console.error(e.message);
  }
}
.as-console-wrapper{min-height:100%}

注意:该代码对prevPositions使用普通对象。只要您的输入数组仅包含数字或字符串,就可以正常工作。但是,如果您输入的是[1, '1']这两个值将被视为相等,因为当用作对象属性名称时,数字键被强制转换为字符串。 (从技术上讲,它要复杂一些,但是您可以理解。)如果您希望将它用于混合数组,则需要使用Map而不是对象。

答案 1 :(得分:0)

此行

    for(let i = 0; i < array.length-1; i++) {

看起来很浪费。对于输入数组let array = [1,2,1,3,2,2],您将迭代整个过程。再次检查object[1]object[2]object[1],等等。

相反,通过object的键进行迭代。

答案 2 :(得分:0)

您可以尝试一下。这段代码更好理解。您可以跳过行号。您的代码中的22。实际上,这是您的代码进行了一些更改。而且这段代码的复杂性肯定是O(n)。

  let closestMatchingPair = function(array) {
  if(!array || array.length < 2) {
        return 'invalid data get bent';
    }
    let pairFound = false;
    let position1 = 0;
    let position2 = 0;
    let distance = array.length;

    object = {};
    //build object 
    for(let i = 0; i < array.length; i++) {
        if(!object[array[i]]) {
            object[array[i]] = [i];
        } else {
            object[array[i]].push(i);
        }
    }
  //loop over object properties
    for(x in object) {
    var item = object[x];
    for(let i=1; i<item.length; i++){
      if(item[i]-item[i-1] < distance){
        pairFound = true;
        position1 = item[i-1],
        position2 = item[i],
        distance = item[i]-item[i-1];
      } 
    }
  }

    return {
        pairFound: pairFound,
        position1: position1,
        position2: position2,
        distance: distance
    }
}

//example [1] = 'invalid data get bent'
//example [1,2] = { pairFound: false, position1: 0, position2: 0, distance: 2 }
//example [1,2,1] = { pairFound: true, position1: 0, position2: 2, distance: 2 }
//example [1,2,1,3,2,2] = { pairFound: true, position1: 4, position2: 5, distance: 1 }
let array = [1,2,1,3,2,2];

closestMatchingPair(array);

您也可以在这里link