Вот проблема

Существует бинарное дерево с корнем 0, состоящее из n узлов. Узлы помечены от 0 до n - 1. Вам дан 0-индексированный целочисленный массив parents, представляющий дерево, где parents[i] является родителем узла i. Поскольку узел 0 является корнем, parents[0] == -1.

У каждого узла есть оценка. Чтобы найти оценку узла, проверьте, были ли узел и связанные с ним ребра удалены. Дерево станет одним или несколькими непустыми поддеревьями. Размер поддерева — это количество узлов в нем. Оценка узла — это произведение размеров всех этих поддеревьев.

Возвращает количество узлов с наибольшей оценкой.

Пример 1:

Input: parents = [-1,2,0,2,0]
Output: 3
Explanation:
- The score of node 0 is: 3 * 1 = 3
- The score of node 1 is: 4 = 4
- The score of node 2 is: 1 * 1 * 2 = 2
- The score of node 3 is: 4 = 4
- The score of node 4 is: 4 = 4
The highest score is 4, and three nodes (node 1, node 3, and node 4) have the highest score.

Пример 2:

Input: parents = [-1,2,0]
Output: 2
Explanation:
- The score of node 0 is: 2 = 2
- The score of node 1 is: 2 = 2
- The score of node 2 is: 1 * 1 = 1
The highest score is 2, and two nodes (node 0 and node 1) have the highest score.

Вот решение

function countHighestScoreNodes(parents) {
  const totalNodes = parents.length;
  const children = createChildrenMap(parents);
  let maxProd = 0;
  let maxProdCount = 0;
  const memo = new Array(parents.length).fill(undefined);

  dfs(parents[0]);

  return maxProdCount;

  function dfs(node) {
    if (node == undefined) return 0;
    if (memo[node] !== undefined) return memo[node];

    const [one, two] = children.get(node) ?? [];
    const childOneSize = dfs(one);
    const childTwoSize = dfs(two);
    const restSize = totalNodes - childOneSize - childTwoSize - 1;
    const prod = [childOneSize, childTwoSize, restSize]
      .filter(Boolean)
      .reduce((val, prod) => val * prod, 1);

    if (maxProd < prod) {
      maxProd = prod;
      maxProdCount = 1;
    } else if (maxProd === prod) {
      maxProdCount++;
    }

    memo[node] = childOneSize + childTwoSize + 1;
    return memo[node];
  }

  function createChildrenMap(parents) {
    const children = new Map();
    for (let i = 0; i < parents.length; i++) {
      const parent = parents[i];
      children.set(parent, (children.get(parent) ?? []).concat(i));
    }
    return children;
  }
}

Эта строка объявляет основную функцию и присваивает общее количество узлов в дереве переменной totalNodes.

function countHighestScoreNodes(parents) {
  const totalNodes = parents.length;

Эта строка вызывает функцию createChildrenMap, передавая массив parents в качестве аргумента, и присваивает возвращаемое значение переменной children. Эта функция создает карту, где ключами являются родительские узлы, а значениями являются массивы их дочерних узлов.

  const children = createChildrenMap(parents);

Эти строки объявляют переменные maxProd и maxProdCount и инициализируют их значением 0. maxProd хранит максимальное произведение размеров трех групп узлов (дочерние, внуки и остальная часть дерева), а maxProdCount подсчитывает количество раз, которое maxProd имеет место.

  let maxProd = 0;
  let maxProdCount = 0;

Эта строка объявляет массив memo с parents.length элементами и заполняет его undefined . Этот массив будет использоваться для хранения результатов ранее вычисленных вызовов dfs для целей запоминания.

  const memo = new Array(parents.length).fill(undefined);

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

  dfs(parents[0]);

Эта строка возвращает окончательное значение maxProdCount, которое представляет собой количество раз, когда встречается максимальное произведение размеров трех групп узлов.

  return maxProdCount;

Эта строка является началом функции dfs. Функция принимает один параметр, текущий посещаемый узел. Если текущий узел не определен, функция возвращает 0.

  function dfs(node) {
    if (node == undefined) return 0;

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

    if (memo[node] !== undefined) return memo[node];

Эта строка удаляет дочерние элементы текущего узла из карты children. Если для текущего узла нет дочерних элементов, назначение деструктурирования назначает пустой массив one и two.

    const [one, two] = children.get(node) ?? [];

Эти строки рекурсивно вызывают функцию dfs, передавая в качестве аргументов первого и второго дочерних элементов текущего узла. Функция вызывается дважды, по одному разу для каждого потомка, а возвращаемые значения присваиваются childOneSize и childTwoSize соответственно. Эти значения представляют размер потомков и внуков текущего узла.

    const childOneSize = dfs(one);
    const childTwoSize = dfs(two);

Эта строка вычисляет размер остальной части дерева, которая не является потомком или внуком текущего узла. Он вычитает размеры потомков и внуков из общего количества узлов и вычитает 1, чтобы исключить текущий узел из расчета.

    const restSize = totalNodes - childOneSize - childTwoSize - 1;

Эта строка вычисляет произведение размеров трех групп узлов (детей, внуков и остальной части дерева). Он создает массив [childOneSize, childTwoSize, restSize] , отфильтровывает любые ложные значения, а затем использует метод сокращения для умножения всех элементов массива вместе. Результат сохраняется в переменной prod.

    const prod = [childOneSize, childTwoSize, restSize]
      .filter(Boolean)
      .reduce((val, prod) => val * prod, 1);

Этот блок кода сравнивает текущий продукт prod с текущим максимальным продуктом maxProd. Если prod больше maxProd, maxProd обновляется до prod, а maxProdCount сбрасывается до 1. Если prod равно maxProd, maxProdCount увеличивается на 1.

    if (maxProd < prod) {
      maxProd = prod;
      maxProdCount = 1;
    } else if (maxProd === prod) {
      maxProdCount++;
    }

Эта строка сохраняет результат текущего узла в массиве мемоизации, а затем функция возвращает размер поддерева текущего узла.

    memo[node] = childOneSize + childTwoSize + 1;
    return memo[node];

Это функция createChildrenMap, которая принимает в качестве аргумента массив parents. Он создает новую карту, children, и перебирает массив parents. Для каждого элемента в массиве он добавляет текущий индекс в качестве дочернего к соответствующему родительскому узлу на карте. Если у родительского узла еще нет дочерних элементов, он добавляет пустой массив в качестве значения. Наконец, он возвращает карту children.

function createChildrenMap(parents) {
    const children = new Map();
    for (let i = 0; i < parents.length; i++) {
      const parent = parents[i];
      children.set(parent, (children.get(parent) ?? []).concat(i));
    }
    return children;
  }

Таким образом, в целом эта реализация более эффективна за счет использования мемоизации, она позволяет избежать многократного пересчета размера дочерних элементов для одного и того же узла, что значительно снижает временную сложность решения до O (n), где n — количество узлов в дереве. .