Нахождение ближайшей подходящей пары в массиве - PullRequest
0 голосов
/ 16 апреля 2019

Я пытаюсь определить ближайшую подходящую пару в массиве с помощью алгоритма по n времени.

Я создал решение в следующем репле, но я не уверен, что это за Big O. Я бы догадался, может быть (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 ]

2 голосов
/ 16 апреля 2019

Вы делаете много работы, которая не нужна. Часто хорошей идеей является создание объекта заранее, как у вас, но в этом случае это не победа. Вам нужно только выполнить итерацию по массиву один раз, и есть лишь несколько фактов, которые необходимо отслеживать между итерациями:

  • prevPositions - Объект для отслеживания самого последнего индекса каждого элемента, видимого на данный момент.
  • distance - Наименьшее расстояние между парами.
  • position1, position2 - Индексы пары с наименьшим на данный момент расстоянием.

(Технически distance всегда можно рассчитать, вычитая position1 из position2, но мы сэкономим небольшую работу, сохранив ее отдельно.)

В каждой итерации вам необходимо:

  1. Проверьте, видели ли вы текущий элемент раньше.
    • Если да, проверьте, меньше ли расстояние между текущим элементом и его предыдущим вхождением, чем наименьшее расстояние до настоящего времени.
      • Если да, сохраните новое наименьшее расстояние в distance и индексы текущего и предыдущего вхождений в position1 и position2.
  2. Сохранить индекс текущего элемента в prevPositions.

После того, как вы перебрали каждый элемент один раз, position1, position2 и distance будут содержать индексы ближайшей пары и расстояние между ними.

Это 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 вместо объекта.

0 голосов
/ 16 апреля 2019

Вы можете попробовать это. Этот код намного лучше понятен. Вы можете пропустить строку №. 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);

Вы также можете проверить там ссылка

0 голосов
/ 16 апреля 2019

Эта строка

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

выглядит расточительно.Для вашего входного массива let array = [1,2,1,3,2,2] вы проходите через все это.Проверка object[1], object[2], object[1] снова и т. Д.

Итерируйте вместо клавиш object.

...