Добавлять элемент массива только в том случае, если его еще нет в Javascript

#javascript #arrays #sorting #set

#javascript #массивы #сортировка #установить

Вопрос:

Мне нужно добавить элемент в массив, только если его еще нет в Javascript. По сути, я рассматриваю массив как набор.

Мне нужно, чтобы данные хранились в массиве, иначе я бы просто использовал объект, который можно использовать как набор.

Я написал следующий прототип массива и хотел услышать, знает ли кто-нибудь лучший способ. Это O (n) вставка. Я надеялся выполнить O (ln (n)) insert, однако я не видел простого способа вставить элемент в отсортированный массив. Для моих приложений длина массива будет очень маленькой, но я бы все же предпочел что-то, что подчинялось принятым правилам для хорошей эффективности алгоритма:

 Array.prototype.push_if_not_duplicate = function(new_element){
    for( var i=0; i<this.length; i   ){
        // Don't add if element is already found
        if( this[i] == new_element ){
            return this.length;
        }
    }
    // add new element
    return this.push(new_element);
}
  

Комментарии:

1. Вы говорите, что это отсортированный массив, но я не понимаю, как ваш алгоритм обеспечивает какой-либо порядок. Я что-то упустил?

2. Эй, Расти, сортировка не применяется для этого массива, потому что алгоритм проверяет все элементы в массиве перед вставкой. Чтобы вставить уникальный элемент в O(ln (n)), массив должен быть уже отсортирован.

Ответ №1:

Если я правильно понимаю, у вас уже есть отсортированный массив (если у вас нет отсортированного массива, вы можете использовать метод Array.sort для сортировки ваших данных), и теперь вы хотите добавить к нему элемент, если он еще не присутствует в массиве. Я извлек метод двоичной вставки (который использует двоичный поиск) в библиотеке закрытия Google. Сам соответствующий код будет выглядеть примерно так, и это операция O (log n), потому что двоичный поиск равен O (log n) .

 function binaryInsert(array, value) {
  var index = binarySearch(array, value);
  if (index < 0) {
    array.splice(-(index   1), 0, value);
    return true;
  }
  return false;
};

function binarySearch(arr, value) {
  var left = 0;  // inclusive
  var right = arr.length;  // exclusive
  var found;
  while (left < right) {
    var middle = (left   right) >> 1;

    var compareResult = value > arr[middle] ? 1 : value < arr[middle] ? -1 : 0;
    if (compareResult > 0) {
      left = middle   1;
    } else {
      right = middle;
      // We are looking for the lowest index so we can't return immediately.
      found = !compareResu<
    }
  }
  // left is the index if found, or the insertion point otherwise.
  // ~left is a shorthand for -left - 1.
  return found ? left : ~left;
};
  

Используется binaryInsert(массив, значение). Это также поддерживает сортировку массива.

Ответ №2:

Удалил мой другой ответ, потому что я пропустил тот факт, что массив отсортирован.

Алгоритм, который вы написали, проходит через каждый элемент в массиве и, если совпадений нет, добавляет новый элемент в конце. Я предполагаю, что это означает, что после вы выполняете другую сортировку.

Весь алгоритм можно улучшить, используя алгоритм «разделяй и властвуй». Выберите элемент в середине массива, сравните с новым элементом и продолжайте, пока не найдете место для вставки. Это будет немного быстрее, чем ваш приведенный выше алгоритм, и впоследствии не потребует сортировки.

Если вам нужна помощь в разработке алгоритма, не стесняйтесь спрашивать.

Ответ №3:

Раньше я создавал (простой и неполный) Set тип, подобный этому:

 var Set = function (hashCodeGenerator) {
    this.hashCode = hashCodeGenerator;
    this.set = {};
    this.elements = [];
};
Set.prototype = {
  add: function (element) {
    var hashCode = this.hashCode(element);
    if (this.set[hashCode]) return false;
    this.set[hashCode] = true;
    this.elements.push(element);
    return true;
  },
  get: function (element) {
    var hashCode = this.hashCode(element);
    return this.set[hashCode];
  },
  getElements: function () { return this.elements; }
};
  

Вам просто нужно найти хорошую hashCodeGenerator функцию для ваших объектов. Если ваши объекты являются примитивами, эта функция может вернуть сам объект. Затем вы можете получить доступ к элементам набора в форме массива из средства getElements доступа. Вставки равны O (1). Требования к пространству — O (2n).

Ответ №4:

Если ваш массив представляет собой двоичное дерево, вы можете вставить в O(log n), поместив новый элемент в конец и вставив его на место. Для проверки дубликатов также потребуется O (log n).

В Википедии есть отличное объяснение.

Комментарии:

1. Спасибо за ваш ответ, написание моего собственного алгоритма пузырьков — это определенно не то, на что я хочу потратить время.

2. Алгоритм действительно не так уж плох. Обычно, чем ближе вы подходите к эффективному алгоритму, тем дальше вы отходите от очевидного, простого в программировании алгоритма. Например, посмотрите на кучу Фибоначчи по сравнению с двоичной кучей. Двоичную кучу легче кодировать и понимать, но куча Фибоначчи имеет некоторые преимущества, которые вы никогда не получите от более простого решения.

3. Это не значит, что проще никогда не бывает лучше. Просто эффективность обычно более сложна с математической точки зрения.

4. Эй, Расти, прошу прощения, если я ошибаюсь, но я предполагаю, что ты либо учишься в школе, либо недавно закончил. Одной вещи, которой вас не учат в колледже, является то, что в производственной среде обычно очень плохая идея внедрять алгоритмы с нуля. Для этого есть две очень веские причины: 1) ВРЕМЯ — вы можете сделать гораздо больше за свое время, если будете использовать ранее существующий код. 2) ОШИБКИ — написание алгоритмов с нуля сопряжено с риском наличия в нем ошибок. Использование проверенного и надежного кода сводит к минимуму этот риск.

5. Ха, я надеюсь, вы не поняли неправильно и не подумали, что я бы посоветовал кому-нибудь реализовать свою собственную кучу Фибоначчи. Это, как вы говорите, пустая трата времени.