Ответы пользователя по тегу Алгоритмы
  • Как оптимизировать вычисление биномиального коэффициента?

    0xD34F
    @0xD34F
    function mul(start, end) {
      let result = 1;
    
      for (let i = start; i <= end; i++) {
        result *= i;
      }
    
      return result;
    }
    
    function choose(n, k) {
      return n > k
        ? Math.round(mul(n - k + 1, n) / mul(2, k))
        : +(n === k);
    }
    Ответ написан
    4 комментария
  • Как определить, какое из чисел от 1 до N отсутствует в массиве?

    0xD34F
    @0xD34F
    Отсортировать массив, найти индекс, не равный соответствующему элементу минус один, увеличить индекс на единицу; если такой индекс отсутствует, значит отсутствует последнее число - длина массива плюс один:

    function findN([...arr]) {
      const index = arr.sort((a, b) => a - b).findIndex((n, i) => i !== ~-n);
      return -~index || -~arr.length;
    }

    Желательно, чтобы вычислительная сложность была O(n)

    Ненормальный способ - создать массив длиной N, обойти исходный массив, сохраняя в новый информацию о том, какие числа есть, затем в новом массиве выполнить поиск индекса, для которого число в исходном массиве не встретилось:

    const findN = arr => -~arr
      .reduce((acc, n) => (acc[~-n] = !1, acc), Array(-~arr.length).fill(!0))
      .findIndex(Boolean);

    Нормальный способ - погуглить формулу суммы натуральных чисел от 1 до N, вычесть содержимое массива:

    const findN = arr =>
      arr.reduce((acc, n) => acc - n, (arr.length + 2) * (arr.length + 1) / 2);

    Ну и в качестве бонуса:

    const findN = arr =>
      arr.reduce((acc, n, i) => acc ^ n ^ -~i, -~arr.length);
    Ответ написан
    Комментировать
  • Как получить все уникальные пары из массива?

    0xD34F
    @0xD34F
    $numArr = count($array);
    $numElems = count($array[0]);
    
    $combinations = [];
    $numCombinations = pow($numElems, $numArr);
    
    for ($i = 0; $i < $numCombinations; $i++) {
      $combination = [];
    
      for ($j = 0; $j < $numArr; $j++) {
        $arrIndex = $j;
        $elIndex = $i / pow($numElems, $j) % $numElems;
        $combination[] = $array[$arrIndex][$elIndex];
      }
    
      $combinations[] = $combination;
    }
    Ответ написан
    5 комментариев
  • Есть набор чисел, как определить, что они заканчиваются на определенную цифру?

    0xD34F
    @0xD34F
    Получаем остаток от деления на 10, или превращаем число в строку и смотрим на последний символ:

    arr.filter(n => n % 10 === 4)
    // или
    arr.filter(n => ''.endsWith.call(n, 4))
    // или
    arr.filter(n => `${n}`.slice(-1) === '4')
    // или
    arr.filter(n => /4$/.test(n))
    // или
    (`${arr}`.match(/\d*4(?=\D|$)/g) || []).map(Number)
    Ответ написан
    Комментировать
  • Как корректно обойти массив для построения дерева?

    0xD34F
    @0xD34F Куратор тега JavaScript
    function getTree(data) {
      let root = data.find(n => n.id === 0);
      root = { title: root.name.slice(1,-1), id: root.id };
    
      data.filter(n => n.id !== 0).forEach(item => {
        const node = item.name.slice(1, -1).split('/').reduce((parent, title) => {
          if (!parent.children) {
            Object.assign(parent, { children: [], isDirectory: true });
          }
          let child = parent.children.find(m => m.title === title);
          if (!child) {
            child = { title };
            parent.children.push(child);
          }
          return child;
        }, root);
        Object.assign(node, { id: item.id, isDirectory: !!node.isDirectory });
      });
    
      return root;
    }
    Ответ написан
  • Как объединить перекрывающиеся интервалы?

    0xD34F
    @0xD34F
    Сортируем интервалы по возрастанию нижней границы. Перебираем интервалы и сохраняем текущий интервал в случае если ничего не сохранено (т.е., нулевой интервал сохраняется всегда) или если нижняя граница текущего интервала превышает верхнюю границу последнего сохранённого; в противном случае проверяем, что верхняя граница последнего сохранённого интервала ниже верхней границе текущего, если да - обновляем последний сохранённый - заменяем его верхнюю границу на верхнюю границу текущего.

    function mergeIntervals(intervals) {
      intervals = intervals.map(n => [...n]);
    
      if (intervals.length < 2) {
        return intervals;
      }
    
      intervals.sort((a, b) => a[0] - b[0]);
    
      const stack = [];
    
      intervals.forEach(n => {
        const top = stack[stack.length - 1];
    
        if (!top || top[1] < n[0]) {
          stack.push(n);
        } else if (top[1] < n[1]) {
          top[1] = n[1];
        }
      });
    
      return stack;
    }
    Ответ написан
    6 комментариев
  • Почему двоичный поиск работает только "вверх"?

    0xD34F
    @0xD34F Куратор тега Vue.js
    Косяков и странностей тут навалом.

    Значение max вы перед началом поиска устанавливаете равным максимальному индексу - отлично. А как же min - почему не устанавливаете его равным 0? Забыли? Или думаете что 0 там сам собой появится? Не появится.

    Кстати, а зачем min и max класть в data? - вне binarySearchInit они не используются. Пусть будут локальными переменными этого метода.

    Значения в массиве являются строками, так что у вас 11 будет меньше, чем 2 (или для вас не существует ничего больше девятки?). Надо сделать числами, замените .push(item) на .push(+item) (или .push(Number(item)), или .push(parseInt(item))). Соответственно, надо сделать числом и search - для этого достаточно добавить соответствующий модификатор v-model.

    Сортировка введённых данных - по умолчанию элементы массива сортируются как строки. Заменить .sort() на .sort((a, b) => a - b).

    Зачем такие сложности с отдельной кнопкой для получения данных? Сделайте array вычисляемым свойством:

    computed: {
      array() {
        return this.info
          .split(';')
          .map(n => parseInt(n))
          .filter(n => !Number.isNaN(n))
          .sort((a, b) => a - b);
      },
    },

    Да и сам результат поиска тоже может быть вычисляемым свойством.
    Ответ написан
    4 комментария
  • Как реализовать заполнение матрицы спиралью через вложенный цикл?

    0xD34F
    @0xD34F
    Десять секунд гугления, находим примерный вариант - надо только порядок поменять, от краёв к центру:

    #include <iostream>
    #include <iomanip>
    #include <bits/stdc++.h> 
    
    using namespace std;
    
    int main()
    {
      const int N = 6;
    
      int m[N][N] = { 0 };
    
      for (int i = 0; i < N; i++) {
        for (int j = 0; j < N; j++) {
          int x = min(min(i, j), min(N - 1 - i, N - 1 - j));
          m[i][j] = N * N + 1 - (i > j
            ? (N - 2 * x - 2) * (N - 2 * x - 2) + (i - x) + (j - x)
            : (N - 2 * x) * (N - 2 * x) - (i - x) - (j - x)
          );
        }
      }
    
      for (int i = 0; i < N; i++) {
        for (int j = 0; j < N; j++) {
          cout << setw(5) << m[i][j];
        }
        cout << endl;
      }
       
      return 0;
    }
    Ответ написан
    Комментировать
  • Как реализовать функцию invert для бинарного дерева?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Рекурсия же:

    const invertTree = ({ value, left, right }) => {
      const node = { value };
      if (left) {
        node.right = invertTree(left);
      }
      if (right) {
        node.left = invertTree(right);
      }
      return node;
    };
    Ответ написан
    1 комментарий
  • Реализация алгоритма нахождения минимальной суммы непрерывной последовательности, есть варианты?

    0xD34F
    @0xD34F
    function getMinSum(arr, n) {
      let sum = 0;
      let minSum = 0
      let index = 0;
    
      for (let i = 0; i < n; sum += arr[i++]) ;
      minSum = sum;
    
      for (let i = n; i < arr.length; i++) {
        sum -= arr[i - n] - arr[i];
        if (sum < minSum) {
          minSum = sum;
          index = i - n + 1;
        }
      }
    
      return {
        sum: minSum,
        seq: arr.slice(index, index + n),
      };
    }
    Ответ написан
    1 комментарий
  • Какой метод перебора массивов JS использовать и как?

    0xD34F
    @0xD34F Куратор тега JavaScript
    map/filter:

    arr.map((n, i) => n ? i : null).filter(n => n !== null)
    // или
    arr.map((n, i) => n ? i : NaN).filter(n => n === n)
    // или
    arr.map((n, i) => !!n && i).filter(Number.isInteger)
    // или
    arr.map((n, i) => !n || i).filter(n => n !== !0)

    reduce:

    arr.reduce((acc, n, i) => n ? [ ...acc, i ] : acc, [])
    // или
    arr.reduce((acc, n, i) => (n && acc.push(i), acc), [])

    или, если надо изменить текущий массив, а не создавать новый

    for (let i = arr.length; i--; ) {
      if (arr[i]) {
        arr[i] = i;
      } else {
        arr.splice(i, 1);
      }
    }
    
    // или
    
    arr.reduceRight((_, n, i, a) => n ? a[i] = i : a.splice(i, 1), 0);
    
    // или
    
    let count0 = 0;
    
    arr.forEach((n, i, a) => {
      a[i - count0] = i;
      count0 += !n;
    });
    
    arr.length -= count0;
    Ответ написан
    Комментировать
  • Как написать приложение перестановки набора карточек?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Ну типа вот, дальше допиливайте сами:

    class Game extends React.Component {
      state = {
        panesCurrent: [],
        panesDefault: [ 1, 1, 1, 0, -1, -1, -1 ],
        panesWin: [ -1, -1, -1, 0, 1, 1, 1 ],
      }
    
      componentDidMount() {
        this.reset();
      }
    
      componentDidUpdate() {
        if (this.state.panesCurrent.every((n, i) => n === this.state.panesWin[i])) {
          setTimeout(alert, 25, 'WIN');
        }
      }
    
      onClick(index) {
        const clicked = this.state.panesCurrent[index];
        if (clicked === 0) {
          return;
        }
    
        for (let i = 1; i <= 2; i++) {
          const t = index + clicked * i;
          if (this.state.panesCurrent[t] === 0) {
            const panes = [...this.state.panesCurrent];
            [ panes[index], panes[t] ] = [ panes[t], panes[index] ];
            this.setState({ panesCurrent: panes });
            break;
          }
        }
      }
    
      reset = () => {
        this.setState(({ panesDefault }) => ({
          panesCurrent: [...panesDefault],
        }));
      }
    
      render() {
        return (
          <div>
            <button onClick={this.reset}>reset</button>
            <div className="game">
              {this.state.panesCurrent.map((n, i) => (
                <div
                  className={'pane ' + [ 'left', '', 'right' ][n + 1]}
                  onClick={() => this.onClick(i)}
                ></div>
              ))}
            </div>
          </div>
        )
      }
    }

    .game {
      font-size: 5px;
    }
    
    .pane {
      display: inline-block;
      width: 10em;
      height: 10em;
      margin: 1em;
    }
    
    .pane.right::after,
    .pane.left::after {
      position: relative;
      display: inline-flex;
      justify-content: center;
      align-items: center;
      width: 100%;
      height: 100%;
      font-family: monospace;
      font-size: 4em;
    }
    .pane.right::after {
      content: "-->";
      color: red;
      border: 2px solid red;
    }
    .pane.left::after {
      content: "<--";
      color: lime;
      border: 2px solid lime;
    }
    Ответ написан
    2 комментария
  • Как найти наибольшую последовательность элементов в массиве, чья сумма равна нулю?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Как минимум, проблема в том, что вы хватаете каждый попавшийся подмассив с нулевой суммой. Соответственно, успешно найденный максимальный подмассив будет затёрт меньшим, но расположенным ближе к концу массива. Надо запоминать индексы начала и конца подмассива и смотреть их разности, например:

    function maxZeroSequence(arr) {
      let iMin = 0;
      let iMax = 0;
    
      for (let i = 0; i < arr.length; i++) {
        let sum = 0;
    
        for (let j = i; j < arr.length; j++) {
          if ((sum += arr[j]) === 0 && (iMax - iMin) <= (j - i + 1)) {
            iMin = i;
            iMax = j + 1;
          }
        }
      }
    
      return arr.slice(iMin, iMax);
    }

    Кроме того, проверка всех возможных подмассивов может оказаться слишком медленной при большом размере массива. Так что лучше посчитать префиксные суммы, попутно запоминая индексы, где та или иная сумма встретилась в первый и последний раз; ну а далее взять подмассив с максимальной разностью индексов:

    function maxZeroSequence(arr) {
      const sumIndices = Object.values(arr.reduce((acc, n, i) => {
        const sum = acc[0] += n;
        (acc[1][sum] = acc[1][sum] || [ i + 1 ])[1] = i + 1;
        return acc;
      }, [ 0, { 0: [ 0 ] } ])[1]);
    
      return arr.slice(...sumIndices.reduce((max, n) => {
        const diff1 = n[1] - n[0];
        const diff2 = max[1] - max[0];
        return diff1 > diff2 || (diff1 === diff2 && n[0] > max[0]) ? n : max;
      }, [ 0, 0 ]));
    }
    Ответ написан
    1 комментарий
  • Как правильно собрать вложенный объект без указания родителя?

    0xD34F
    @0xD34F Куратор тега JavaScript
    function createTree(data, levelKey, childrenKey) {
      const tree = [];
    
      data.forEach(n => {
        let arr = tree;
    
        for (let level = 0; n[levelKey] > level++; ) {
          arr = arr[arr.length - 1][childrenKey];
        }
    
        arr.push(Object.assign({ [childrenKey]: [] }, n));
      });
    
      return tree;
    }
    
    
    const tree = createTree([
      { id: 1, title: 'test1', level: 0 },
      { id: 2, title: 'test2', level: 1 },
      { id: 3, title: 'test3', level: 2 },
      { id: 4, title: 'test4', level: 1 },
      { id: 5, title: 'test5', level: 0 },
    ], 'level', 'nodes');
    Ответ написан