Как сложность времени Morris Traversal o (n)? - PullRequest
17 голосов
/ 25 июня 2011

http://geeksforgeeks.org/?p=6358 Может кто-нибудь объяснить, почему у Морриса Траверсала временная сложность o(n)?В обходе, когда у узла есть левый потомок, его копия делается правому потомку его предшественника.Поэтому наихудший случай - найти предшественника для каждого узла

 while(pre->right != NULL && pre->right != current)
        pre = pre->right;

Что увеличит временную сложность?Я что-то здесь упускаю?

Ответы [ 4 ]

10 голосов
/ 24 июня 2013

Еще один способ взглянуть на это - узнать, сколько раз будет проходить узел дерева.Как это постоянно (3 раза для двоичного дерева).Мы смотрим на O (N).

8 голосов
/ 22 февраля 2016

Оригинальная статья для обхода Морриса - Простой и дешевый обход бинарных деревьев .В разделе «Введение» утверждается, что сложность по времени равна O (n):

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

Полный текст статьи должен содержать анализ сложности времени.Но полный текст не может быть доступен бесплатно.

Morris Traversal 非 遍历 二叉树 (非 递归 , 不用 栈 , , O (1) 空间) делает некоторый анализ.Я перевел некоторую соответствующую часть и сделал некоторые исправления, основанные на моем понимании.Вот мое понимание:

Двоичное дерево с n-узлами имеет n-1 ребер.При прохождении Морриса один край посещается максимум 3 раза.1-й визит для определения местоположения узла.Второе посещение - поиск предшественника какого-либо узла.И 3-й / финал для восстановления права ребенка предшественника на ноль.В следующем двоичном дереве красная пунктирная линия предназначена для определения местоположения узла (1-е посещение).Черная пунктирная линия предназначена для поиска узла-предшественника (пройдено два раза: и для 2-го, и для 3-го посещения).Узел F будет посещен, когда находится.Он также будет посещен, когда узел H ищет своего предшественника.Наконец, он будет посещен при восстановлении правого дочернего узла узла G (предшественника H) в ноль.

enter image description here

7 голосов
/ 30 июня 2011

это не увеличит сложность, так как алгоритм просто перестраивает дерево только в одном направлении (перестройка требует только O (n), после чего их только O (n) снова печатает их ... но они объединили обафункциональность в той же функции и дал специальное имя для алгоритма вот и все ...

1 голос
/ 04 декабря 2018

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

При обходе, когда у узла есть левый потомок, его копия делается правому потомку егоПредшественник

Копия узла [current] имеет значение , а не , сделанное правому дочернему элементу предшественника [текущего узла] - правый дочерний элемент предшественника текущего узла равен указал на текущий узел - указатель не делает никакой копии;вместо этого он просто указывает на текущий узел, который уже существует .

Теперь, чтобы ответить на ваш вопрос о вашем фрагменте кода:

while(pre->right != NULL && pre->right != current)
        pre = pre->right;
  • Этот цикл добавляет добавление времени к выполнению алгоритма [по сравнению с тем циклом, которого не было в коде]
  • Но в худшем случае время, которое он добавляет, равно n (принимая время выполнения от n до 2n ).Этот наихудший случай случается, когда каждый отдельный узел должен посещаться в дополнительное время, чтобы найти всех предшественников;кстати, каждое такое дополнительное посещение данного узла - это только дополнительное время, которое он посещает при поиске предшественников (это потому, что обнаружение любого другого предшественника никогда не будет проходить через те же узлы, которыепрошли через, чтобы найти любого другого предшественника) - это объясняет, почему дополнительное время способствует переходу от n -> 2n [линейный], но не n -> n ^ 2 [квадратичный]
  • Даже если 2n > n , когда рассматривается [Big-O] сложность , O (2n) = O (n)
  • Другими словами, более длительное время, равное 2n по сравнению с n , не является действительно дополнительным сложность : n & 2n время выполнения оба имеют сложности одинаковых O (n) [они оба "линейны"]

Теперь, хотя это могло звучать так, как я уже говорил выше, что весь алгоритм время выполнения равно 2n , это не так - это на самом деле 3n .

  • Цикл, который находится в только фрагмент кода сам вносит n время
  • Но алгоритм в целом работает в 3n потому что каждый узел посещается не более 3 раз (один раз / сначала, чтобы «нанизывать» его обратно на узел, для которого он является предшественником (конечная цель, которой помогает фрагмент кода);2-й раз, когда он достигается в обычном обходе [в отличие от чего-либо, что связано с потоковой обработкой предшественника];и затем 3-й / последний раз, когда он снова обнаруживается как предшественник [который сам на 2-й / последний раз] снова и его правый дочерний указатель / поток удаляется из указания на правый дочерний элемент текущего узла [непосредственно перед печатью текущего узла]}
  • И снова [точно так же, как сложность O (2n) = O (n)], сложность O (3n) = O (n)
  • Итак, подведем итог: Да, ваш цикл фрагмента кода вносит время , но НЕ дополнительное время сложность

Между прочим, я не думаю, что эта строка (из полного алгоритма) для удаления старой ссылки на «нить» предшественника является строго необходимой (хотя это не повредит, и может считаться хорошим исправлением):

pre->right = NULL; 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...