Как повысить производительность этого алгоритма Javascript / взлома кода?

так вот вопрос ниже, с моим ответом на него. Я знаю, что из-за двойного вложенного цикла for эффективность составляет O (n ^ 2), поэтому мне было интересно, есть ли способ улучшить большой O моего алгоритма / функции.

// Разработка алгоритма и написание кода для удаления повторяющихся символов в строке без использования дополнительного буфера. ПРИМЕЧАНИЕ. Можно использовать одну или две дополнительные переменные. Дополнительной копии массива нет.

function removeDuplicates(str) {
    let arrayString = str.split("");
    let alphabetArray = [["a", 0],["b",0],["c",0],["d",0],["e",0],["f",0],["g",0],["h",0],["i",0],["j",0],["k",0],["l",0],["m",0],["n",0],["o",0],["p",0],["q",0],["r",0],["s",0],["t",0],["u",0],["v",0],["w",0],["x",0],["y",0],["z",0]]

    for (let i=0; i<arrayString.length; i++) {
        findCharacter(arrayString[i].toLowerCase(), alphabetArray);
    }

    removeCharacter(arrayString, alphabetArray);
};


function findCharacter(character, array) {
    for (let i=0; i<array.length; i++) {
        if (array[i][0] === character) {
        array[i][1]++;
        }
    }
} 

function removeCharacter(arrString, arrAlphabet) {
    let finalString = "";
    for (let i=0; i<arrString.length; i++) {
        for (let j=0; j<arrAlphabet.length; j++) {
            if (arrAlphabet[j][1] < 2 && arrString[i].toLowerCase() == arrAlphabet[j][0]) {
            finalString += arrString[i]
            }
        }
      } 
    console.info("The string with removed duplicates is:", finalString)
}

removeDuplicates("Hippotamuus")

Вы можете сначала отсортировать массив, а затем перебрать элементы, сохраняя символ «последний просмотр» как переменную, удаляя элементы, если они совпадают, и обновляя «последний просмотренный», если они не совпадают. Таким образом, ваш большой O равен (n logn) для сравнительного алгоритма сортировки и (n), если вы реализуете сортировку по основанию и сопоставляете буквы с числами

izb 08.09.2018 01:48

В вопросе упоминается «строка», а затем «массив»; так это ввод - строка или массив? И удаляем ли мы последовательные повторяющиеся символы или все повторяющиеся символы? Кроме того, я не думаю, что 26-буквенный массив считается «одной или двумя дополнительными переменными»; на самом деле вы используете много дополнительного места.

m69 ''snarky and unwelcoming'' 08.09.2018 01:51

@ m69 26-буквенный массив по-прежнему остается постоянным пространством, поскольку он не зависит от длины ввода.

גלעד ברקן 08.09.2018 18:53

@ גלעדבר Это может быть постоянное пространство, но я не думаю, что это то, что подразумевается под "одной или двумя дополнительными переменными". Кроме того, подход с двумя указателями на месте возможен буквально с двумя простыми переменными (при условии, что вы получаете ввод в виде массива символов).

m69 ''snarky and unwelcoming'' 08.09.2018 18:56

@ m69 вы предлагаете пространство O (1) и лучше, чем время O (n ^ 2) с массивом? Как?

גלעד ברקן 08.09.2018 19:36

@ גלעדברקן Я не уверен, что теоретический O (n ^ 2) можно улучшить, но это можно сделать намного проще (и, вероятно, быстрее на практике), чем код в вопросе.

m69 ''snarky and unwelcoming'' 08.09.2018 19:41

@ גלעדברקן На самом деле, поскольку количество символов, которые вы должны проверить на наличие дубликата, никогда не превышает 26, вы можете утверждать, что временная сложность равна O (N), так же, как 26-буквенный массив является постоянным пространством.

m69 ''snarky and unwelcoming'' 08.09.2018 20:14

@ m69 количество символов может исчисляться десятками тысяч, если мы разрешим Unicode и все языки мира.

גלעד ברקן 08.09.2018 20:27

@ גלעדברקן Это все еще константа :-)

m69 ''snarky and unwelcoming'' 08.09.2018 20:28

@ m69 теоретически можно представить себе алфавит O (n).

גלעד ברקן 08.09.2018 20:38
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
2
10
123
4

Ответы 4

Коды символов ASCII / Unicode всех букв одного и того же регистра являются последовательными. Это позволяет провести важную оптимизацию: вы можете найти индекс символа в массиве подсчета символов по его символьному коду ASCII / Unicode. В частности, индекс символа c в массиве счетчика символов будет c.charCodeAt(0) - 'a'.charCodeAt(0). Это позволяет вам искать и изменять количество символов в массиве во времени O(1), что сокращает время выполнения алгоритма до O(n).

Основываясь на вашем описании, я предполагаю, что ввод - это строка (которая неизменна в javascript), и я не уверен, что именно означает «одна или две дополнительные переменные», поэтому, исходя из вашей реализации, я собираюсь предположить, что можно использовать O ( N) пробел. Чтобы улучшить временную сложность, я думаю, что реализации различаются в зависимости от различных требований к выводимой строке.

Предположение1: выводимая строка находится в том порядке, в котором она появляется в первый раз. например. «bcabcc» -> «bca»

Предположим, что длина s равна N, в следующей реализации используется пространство O (N) и время O (N).

 function removeDuplicates(s) {
    const set = new Set(); // use set so that insertion and lookup time is o(1)
    let res = "";
    for (let i = 0; i < s.length; i++) {
        if (!set.has(s[i])) {
            set.add(s[i]);
            res += s[i];
        }
    }
    return res;
  }

Предположение2: выводимая строка должна иметь возрастающий порядок.

Вы можете использовать быстрая сортировка для сортировки на месте, а затем перебрать отсортированный массив, чтобы добавить в результат последний элемент. Обратите внимание, что вам может потребоваться сначала разбить строку на массив. Таким образом, реализация будет использовать пространство O (N), а средняя временная сложность будет O (NlogN)

Предположение 3: результат является наименьшим в лексикографическом порядке среди всех возможных результатов. например. «bcabcc» -> «abc»

Следующая реализация использует пространство O (N) и время O (N).

const removeDuplicates = function(s) {
    const stack = []; // stack and set are in sync
    const set = new Set(); // use set to make lookup faster
    const lastPos = getLastPos(s);
    let curVal;
    let lastOnStack;

    for (let i = 0; i < s.length; i++) {
        curVal = s[i];
        if (!set.has(curVal)) {
            while(stack.length > 0 && stack[stack.length - 1] > curVal && lastPos[stack[stack.length - 1]] > i) {
                set.delete(stack[stack.length - 1]);
                stack.pop();
            }
            set.add(curVal);
            stack.push(curVal);
        }
    }
    return stack.join('');
};

const getLastPos = (s) => {
    // get the last index of each unique character
    const lastPosMap = {};
    for (let i = 0; i < s.length; i++) {
        lastPosMap[s[i]] = i;
    }
    return lastPosMap;
}

Я думаю, правильное предположение состоит в том, что персонажи должны оставаться в исходном порядке, так что «бегемоты» -> «гипотамусы». Кроме того, набор, на самом деле, не квалифицируется как "одна или две переменные" imho.

m69 ''snarky and unwelcoming'' 08.09.2018 19:50

Спасибо за предложение @ m69. Я соответствующим образом обновил свой ответ.

Liutong Chen 09.09.2018 04:09

Есть небольшой трюк «без использования какого-либо дополнительного буфера», хотя я не вижу способа улучшить сложность O(n^2) без использования хэш-карты, чтобы определить, был ли замечен конкретный символ. Уловка состоит в том, чтобы пройти по буферу входной строки (предположим, что это массив JavaScript, поскольку строки в JavaScript неизменяемы) и перезаписать текущий символ следующим уникальным символом, если текущий символ является дубликатом. Наконец, отметьте конец результирующей строки нулевым символом.

Псевдокод:

i = 1
pointer = 1

while string[i]:
  if not seen(string[i]):
    string[pointer] = string[i]
    pointer = pointer + 1
  i = i + 1

mark string end at pointer

Функция seen может либо взять время O(n) и пространство O(1), либо время O(1) и пространство O(|alphabet|), если мы используем хэш-карту.

Я не понимал, что имел в виду:

...without using any additional buffer.

Поэтому я подумал, что попробую сделать это за один цикл, и позволю вам сказать мне, если это неправильно.

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

function removeDuplicates(originalString) {
    let outputString = '';
    let lastChar = '';
    let lastCharOccurences = 1;

    for (let char = 0; char < originalString.length; char++) {
        outputString += originalString[char];

        if (lastChar === originalString[char]) {
            lastCharOccurences++;
            continue;
        }
        
        if (lastCharOccurences > 1) {
            outputString = outputString.slice(0, outputString.length - (lastCharOccurences + 1)) + originalString[char];
            lastCharOccurences = 1;
        }

        lastChar = originalString[char];
    }

    console.info("The string with removed duplicates is:", outputString)
}

removeDuplicates("Hippotamuus")

Опять же, извините, если я неправильно понял сообщение ...

Другие вопросы по теме