Листоподобные деревья
Описание задачи #
Дано два бинарных дерева.
Все листовые узлы дерева образуют последовательность значений.

Например, для дерева выше последовательность значений будет равна (6, 7, 4, 9, 8).
Два бинарных дерева считаются листоподобными, если последовательность значений их листьев одинакова.
Напишите функцию, которая возвращает true тогда и только тогда, когда два заданных дерева являются листоподобными.
Ограничения #
- Количество узлов в каждом дереве находится в диапазоне
[1, 200] - Узлы обоих деревьев имеют значения в диапазоне
[0, 200]
Примеры #
-
Входные данные:

Ответ:
true -
Входные данные:

Ответ:
false
Решение #
Для решения задачи нам необходимо собрать последовательность всех листовых узлов в обоих деревьях и сравнить их.
Для этого мы реализуем отдельную функцию getSequence, которая будет формировать последовательность значений листовых
узлов для переданного дерева.
Функция осуществляет классический обход дерева в глубину DFS (Depth First Search) и когда встречает листовой узел (узел,
у которого нет потомков), то добавляет его значение в строку sequence, разделяя значения точкой с запятой.
Предварительно мы определяем переменную sequence, равную пустой строке, и управляем ее значением через замыкание.
Вместо строки можно было бы использовать массивы, но после вычисления массивом их пришлось бы сравнивать итерируясь по
всем элементам, что дало бы дополнительных k операций. Строки же можно просто сравнить между самой через ==.
Реализация #
-
package leaf_similar_trees import ( "fmt" "strconv" ) type TreeNode struct { Val int Left *TreeNode Right *TreeNode } func leafSimilar(root1 *TreeNode, root2 *TreeNode) bool { first := getSequence(root1) second := getSequence(root2) return first == second } func getSequence(root *TreeNode) string { var dfs func(node *TreeNode) sequence := "" dfs = func(node *TreeNode) { if node.Left == nil && node.Right == nil { fmt.Println(node.Val) sequence += strconv.Itoa(node.Val) + ";" } if node.Left != nil { dfs(node.Left) } if node.Right != nil { dfs(node.Right) } } dfs(root) return sequence } -
export type TreeNode = { val: number left: TreeNode | null right: TreeNode | null } export function leafSimilar(root1: TreeNode | null, root2: TreeNode | null): boolean { if (!root1 || !root2) { return false } const first = getSequence(root1) const second = getSequence(root2) return first === second } function getSequence(root: TreeNode): string { let sequence = "" const dfs = (node: TreeNode): void => { if (!node.left && !node.right) { sequence = `${sequence}${node.val};` } if (node.left) { dfs(node.left) } if (node.right) { dfs(node.right) } } dfs(root) return sequence }
Оценка сложности #
n1— количество узлов в первом деревеn2— количество узлов во втором деревеh1— высота первого дереваh2— высота второго дерева
По времени
O(n1 + n2), так как нам нужно перебрать все элементы в дереве.
По памяти
Так как мы храним последовательности листовых узлов в виде строки, то выделяемую память мы считаем константной. Однако, это рекурсивный алгоритм поэтому нужно учесть память используемую стеком вызовов.
Таким образом сложность по памяти равна O(h1 + h2).