Диаметр двоичного дерева — неудачный случай, когда самый длинный путь не проходит через корень

Итак, я работаю над этой проблемой:

Учитывая корень двоичного дерева, верните длину диаметра дерева.

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

Длина пути между двумя узлами представлена ​​количеством ребер между ними.

Вот как я пытался решить:

var diameterOfBinaryTree = function(root) {

  if (root === null) return 0;
  let max_height = 0;

  function maxDepth(node) {
    if (node === null) return 0;
    var lh = maxDepth(node.left);
    var rh = maxDepth(node.right);

    return 1 + Math.max(lh, rh);
  }

  max_height = Math.max(max_height, maxDepth(root.left) + maxDepth(root.right));

  diameterOfBinaryTree(root.left);
  diameterOfBinaryTree(root.right);

  return max_height
}

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

diameterOfBinaryTree(root.left);
diameterOfBinaryTree(root.right);

Где я ошибаюсь? Цените любую помощь. Обратите внимание, что мне нужно оптимизировать это с O(N2) до O(N), но сейчас я просто пробую грубую силу.

Тестовый пример, для которого это не удалось, - это дерево:

Рассмотрим самый длинный путь в этом проходе от -1 до -2.

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

President James K. Polk 20.08.2024 20:55

@PresidentJamesK.Polk Подумайте об этом. Я получаю результат 7, тогда как он должен быть 8 (Путь между -1 и -2): [4,-7,-3,null,null,-9,-3,9,-7,-4,null,6,null,-6,-6,null,null‌​,0,6,5,null,9,null,n‌​ull,-1,-4,null,null,‌​null,-2] Вот моя работа: leetcode.com/problems/diameter-of-binary-tree/submissions/…

ABGR 20.08.2024 20:58

@PresidentJamesK.Polk: Обратите внимание, что это не обязательно должно быть сбалансированное двоичное дерево. Итак, представьте себе случай, когда одно поддерево очень короткое, а другое очень высокое; вы можете получить более длинный путь, взяв две разные ветви из высокого поддерева (это означает, что вы не проходите через корень), чем если взять по одной ветви из каждого поддерева (это означает, что вы проходите).

ruakh 20.08.2024 21:16
Поведение ключевого слова "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
3
52
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Вам нужно начать перемещение от корня, а затем вычислить максимальное значение диаметра текущего диаметра и сложить лево + право на итерации узла, в котором вы находитесь.

function diameterOfBinaryTree(root) {
    let max_height = 0;
    
    function maxDepth(node) {
        if (!node) return 0;// if our node is falsy, means no path anymore, return 0

        // Assign the left  of tree to leftHeight and right of tree to rightHeight
        const leftHeight = maxDepth(node.left); 
        const rightHeight = maxDepth(node.right);

        // take the max of current diameter and combination of left+right (in case path goes through root)
        max_height = Math.max(diameter, leftHeight + rightHeight);
        return Math.max(leftHeight, rightHeight) + 1; // add 1 to go a level above
    }
    maxDepth(root); // begin traversing from root
    return max_height;
}

Хм, я полагаю, вы тоже хотели бы, чтобы let diameter =0 было объявлено max_height. Даже в этом случае это не работает для одного и того же тестового примера. Кроме того, мне было действительно любопытно, почему мое решение не работает.

ABGR 20.08.2024 22:01
Ответ принят как подходящий

Но я попытался учесть этот случай, т. е. я выполняю итерацию на каждом узле дерева:

diameterOfBinaryTree(root.left);
diameterOfBinaryTree(root.right);

Где я ошибаюсь?

Функция diameterOfBinaryTree выполняет вычисление и возвращает значение, но у нее нет никаких «побочных эффектов» — на самом деле она ничего не делает — поэтому нет причин вызывать ее и отбрасывать результат. И это хорошо! Чистые функции, подобные этой, легче рассуждать (как для людей, так и для компиляторов). Но это означает, что эти рекурсивные вызовы бессмысленны.

Вместо этого вам нужно фактически использовать результат:

return Math.max(
    maxDepth(root.left) + maxDepth(root.right),
    diameterOfBinaryTree(root.left),
    diameterOfBinaryTree(root.right)
);

Хм, а я думал, что обновляю max_height при каждом звонке и наконец перезваниваю. Так в чем же была ошибка?

ABGR 20.08.2024 21:30

@ABGR max_height является локальным для функции; он не будет сохраняться при вызовах функций.

Unmitigated 20.08.2024 21:31

Ааа, теперь я немного в замешательстве :) Посмотрите это: stackoverflow.com/questions/78592674/… Была аналогичная ситуация, и я узнал, что, хотя локальный вызов каждый раз сбрасывает объявленную переменную, верхний вызывающий объект будет делать его все больше и больше по мере раскручивания рекурсии, и это имело для меня смысл. Цените свои идеи, и ваше решение будет работать идеально.

ABGR 20.08.2024 21:38

Хм, если немного подумать, все ясно. Спасибо. Вы правы, более ранние рекурсивные вызовы были фактически бессмысленными, поскольку max_height оценивался только на основе верхнего вызова. Это правильно? Так что я мог бы заставить это работать таким образом, даже если объявить max_height: max_height = Math.max(max_height, maxDepth(root.left) + maxDepth(root.right), diameterOfBinaryTree(root.left), diameterOfBinaryTree(root.right));

ABGR 20.08.2024 21:55

@ABGR: Верно — ключевое слово let означает, что для каждого вызова этой функции создается новый/отдельный max_height, и эти разные max_height не мешают друг другу. return max_height возвращает только max_height из текущего вызова. Re: «Я мог бы заставить это работать таким образом, даже с объявлением max_height»: На самом деле нет причин так делать, но да: если это важно для вас, вы можете.

ruakh 20.08.2024 22:14

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