Пространственная/временная сложность двойного равенства деревьев

75
6

Вчера у меня было интервью, в котором была представлена довольно простая структура данных дерева:

t ::= int | (t * t)

где tTree - либо целое число (лист), либо два t, которые представляют собой левое и правое поддеревья. Это означает, что дерево будет иметь значения только на уровне листа.

Пример дерева может выглядеть следующим образом:

         t 
/ \
t t
/ \ / \
1 2 3 4

Задача заключалась в том, чтобы написать функцию, equal(t, t) => bool которая принимает два tTrees и определяет, являются ли они равными, довольно простыми.

Я написал довольно стандартный код, который получился так (ниже псевдокод):

fun equal(a, b) {
if a == b { // same memory address
return true
}

if !a || !b {
return false
}

// both leaves
if isLeaf(a) && isLeaf(b) {
return a == b
}

// both tTrees
if isTree(a) && isTree(b) {
return equal(a->leftTree, b->leftTree)
&& equal(a->rightTree, b->rightTree)
}

// otherwise
return false
}

Когда меня попросили дать время и пространство, я быстро ответил:

O(n) time 
O(1) space

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

Любые мысли или ввод по этому вопросу будут действительно полезны.

спросил(а) 2020-03-26T17:25:11+03:00 2 месяца назад
1
Решение
53

Как бы то ни было, ваш код O (n), и ваш интервьюер ошибся. Код не O (1) в пространстве используется, хотя: O (n) в худшем случае (когда деревья очень неуравновешенны), потому что ваш код рекурсивный (а не хвост рекурсивный).

Вероятно, они просили вас написать функцию, которая проверяет, являются ли два дерева изоморфными. То есть они ожидали, что вы напишете код, который возвращает true при сравнении этих двух деревьев:

  *      *
/ \ / \
1 2 2 1

Затем они неправильно читают ваше решение, предполагая, что вы написали наивный код, который делает это, что будет O (2 ^ n).

Другая возможность заключается в том, что некоторые указатели могут быть повторно использованы как в левой, так и в правой ветвях того же дерева, что позволяет отображать дерево с 2 ^ n узлами в пространстве O (n). Тогда, если "n" - это размер структуры в памяти, а не количество узлов, то позиция интервьюера верна. Вот такое дерево:

  ___   ___   ___   ___   ___
/ \ / \ / \ / \ / \
* * * * * 1
\___/ \___/ \___/ \___/ \___/

Корень слева, и он имеет 32 листовых узла (все 1).

ответил(а) 2020-03-26T17:40:33.140920+03:00 2 месяца назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема