Времева сложност на проверката дали едно двоично дърво е балансирано

Практикувам някои въпроси за интервю и се опитвам да разбера времевата сложност на моето решение, за да определя дали дадено двоично дърво е балансирано.

Вярвам, че решение 2 има времева сложност O(n), докато решение 1 има времева сложност O(n^2). Това се дължи на факта, че в решение 1 се връщате надолу към дъното на дървото, за да проверите дали дървото е балансирано и също така проверявате за разлики във височината на поддървото. Допълнителната сложност идва, когато отиваме по-високо нагоре в дървото към корена, get_height все още рекурсира надолу в дървото, за да изчисли височината. Следователно, пътуване надолу по дървото още веднъж --> O(n^2).

Фактът, че решение 2 първо сравнява височината, означава, че докато се движим обратно нагоре по дървото, не трябва да се връщаме надолу, за да проверим височината на поддървото.

помощник:

def get_height(root):
    if root is None:
        return 0
    else:
        return max(get_height(root.left), get_height(root.right)) + 1

Решение 1:

def is_balanced(root):
    if root is None:
        return True
    else:
        return is_balanced(root.right) and is_balanced(root.left) and (abs(get_height(root.left) - get_height(root.right)) < 2)

Решение 2:

def is_balanced2(root):
    if root is None:
        return True
    else:
        return (abs(get_height(root.left) - get_height(root.right)) < 2) and is_balanced(root.right) and is_balanced(root.left)

код за проверка на часовите разлики:

s = time.time()
print("result: {}".format(is_balanced(a)))
print("time: {}".format(time.time() - s))

s = time.time()
print("result: {}".format(is_balanced2(a)))
print("time: {}".format(time.time() - s))

резултати за небалансирано дърво:

result: False
time: 2.90870666504e-05    # solution 1
result: False
time: 1.50203704834e-05    # solution 2

person Liondancer    schedule 14.12.2015    source източник


Отговори (1)


Вярвам, че решение 2 има времева сложност O(n), докато решение 1 има времева сложност O(n^2).

Вярвам в противното, както е обяснено по-долу.

...в решение 1 се връщате надолу към дъното на дървото, за да проверите дали дървото е балансирано и също така проверявате за разлики във височината на поддървото. Допълнителната сложност идва, когато отиваме по-високо нагоре в дървото към корена, get_height все още рекурсира надолу в дървото, за да изчисли височината. Следователно, пътуване надолу по дървото още веднъж --> O(n^2).

Не е толкова просто. Да кажем, че извиквате is_balanced() в корена: тъй като той рекурсивно посещава всеки възел в дървото, той извиква get_height, който рекурсивно посещава поддървото там под него. За корена, get_height посещава почти цялото дърво: N-1 операции, така че O(N). За всяко от 2-те деца на корена, get_height посещава тяхната (приблизително) половина дърво: отново O(N). Това продължава, докато get_height работи на ~N None възлите под N/2 листови възли: все още O(N). Като цяло има ~log2N нива в дървото, вие извършвате O(N) обработка на всяко, така че общата сложност е O(NlogN).

Фактът, че решение 2 първо сравнява височината, означава, че докато се движим обратно нагоре по дървото, не трябва да се връщаме надолу, за да проверим височината на поддървото.

не Това, което сте променили в решение две, е редът на извършване на проверките is_balanced срещу get_height. С всяко дърво, за което и двата теста в крайна сметка преминат, общото количество обработка - и следователно ефективността на големия O - остава непроменена.

Отделно, вашата логика не проверява за балансирано двоично дърво: може да искате да прочетете отново дефиницията.

person Tony Delroy    schedule 14.12.2015
comment
Не съм сигурен как моите решения не проверяват дали едно двоично дърво е балансирано. Бихте ли дали примери? Благодаря ти - person Liondancer; 16.12.2015
comment
@Liondancer: разгледайте примерното дърво в Уикипедия тук, въведено с Балансирано двоично дърво има минималната възможна максимална височина (известна още като дълбочина) за листовите възли: за балансираното дърво вляво левият клон ABCD има височина 3 (използвайки техния root=0 броене), докато десният клон E има 1.. . това няма да изпълни вашето abs(get_height(root.left) - get_height(root.right)) < 2) условие. Както се казва в Wikipedia, разлика във височината от ‹2 е често срещана в балансираните двоични дървета - дори гарантирана от някои алгоритми за вмъкване - но не е част от дефиницията. - person Tony Delroy; 16.12.2015