Я попал в статью, в которой рассказывается об алгоритмах LCA, код простой http://leetcode.com /2011/07/lowest-common-ancestor-of-a-binary-tree-part-i.html

// Return #nodes that matches P or Q in the subtree.
int countMatchesPQ(Node *root, Node *p, Node *q) {
  if (!root) return 0;
  int matches = countMatchesPQ(root->left, p, q) + countMatchesPQ(root->right, p, q);
  if (root == p || root == q)
    return 1 + matches;
  else
    return matches;
}

Node *LCA(Node *root, Node *p, Node *q) {
  if (!root || !p || !q) return NULL;
  if (root == p || root == q) return root;
  int totalMatches = countMatchesPQ(root->left, p, q);
  if (totalMatches == 1)
    return root;
  else if (totalMatches == 2)
    return LCA(root->left, p, q);
  else /* totalMatches == 0 */
    return LCA(root->right, p, q);
}

Но мне было интересно, как вычислить временную сложность алгоритма, может ли кто-нибудь мне помочь?

4
bigpotato 7 Июн 2014 в 16:19
Fwiw: Наименьший общий предок также можно найти за O(1), если вы можете выполнить предварительные вычисления для всех узлов (за время O(n)) en.wikipedia.org/wiki/…
 – 
Asad Iqbal
18 Сен 2016 в 00:22

2 ответа

Лучший ответ

Наихудший случай для этого алгоритма был бы, если бы узлы были выходными узлами одного и того же уровня.

Node *LCA(Node *root, Node *p, Node *q)
{
  for root call countMatchesPQ;
  for(root->left_or_right_child) call countMatchesPQ; /* Recursive call */
  for(root->left_or_right_child->left_or_right_child) call countMatchesPQ;
  ...
  for(parent of leave nodes of p and q) call countMatchesPQ;
}

countMatchesPQ вызывается для height of tree times - 1. Назовем высоту дерева как h.

Теперь проверьте сложность вспомогательной функции

int countMatchesPQ(Node *root, Node *p, Node *q) {
  Search p and q in left sub tree recursively
  Search p and q in right sub tree recursively
}

Итак, это обширный поиск, и последняя сложность составляет N, где N - количество узлов в дереве.

Сложив оба наблюдения, общая сложность алгоритма составит

O(h * N)

Если дерево сбалансировано, h = log N (дерево RB, treap и т. Д.) Если дерево неуравновешено, в худшем случае h may be up to N

Таким образом, сложность с точки зрения N может быть задана как

Для сбалансированного двоичного дерева: O (N logN)
Точнее, это действительно h (N + N / 2 + N / 4 ...) для сбалансированного дерева и, следовательно, должно получиться 2hN
Для несбалансированного двоичного дерева: O (N 2 )
Точнее, это действительно h (N + N-1 + N-2 ...) для сбалансированного дерева и, следовательно, должно быть h x N x (N + 1) / 2

2

Ваш алгоритм не использует память. Используя некоторую память для сохранения пути, вы можете значительно улучшить свой алгоритм.

3
Mohit Jain 25 Июн 2014 в 13:37
Точнее, h(N + N/2 + N/4...) должно быть h * logN, верно?
 – 
bigpotato
25 Июн 2014 в 13:18
Для бесконечного ряда (N + N/2 + N/4...) сумма членов равна 2N.
 – 
Mohit Jain
25 Июн 2014 в 13:35

Сложность LCA составляет O(h), где h - высота дерева. Верхняя граница высоты дерева равна O(n), где n обозначает количество вершин / узлов в дереве.

Если ваше дерево сбалансировано (см. AVL, красно-черное дерево) высота порядка log(n), следовательно, общая сложность алгоритма равна O(log(n)).

4
Community 23 Май 2017 в 15:00
Вы посчитали стоимость LCA. Но в его реализации OP проходит поддерево при каждом вызове (h раз) в функции countMatchesPQ. Таким образом, общая сложность должна быть больше, чем h. Не так ли?
 – 
Mohit Jain
7 Июн 2014 в 17:11
Ты прав. Вот почему у нас есть ваш ответ для :)
 – 
0x90
7 Июн 2014 в 18:31