Автор Тема: Какой код лучше?  (Прочитано 7358 раз)

ilovb

  • Hero Member
  • *****
  • Сообщений: 2538
  • just another nazi test
    • Просмотр профиля
    • Oberon systems
Какой код лучше?
« : Ноябрь 15, 2013, 06:29:37 pm »
PROCEDURE RotateLeft (tree: Tree; node: Node);
   VAR
      temp: Node;
BEGIN

   temp := node.right;
   node.right := temp.left;
   
   IF temp.left # NIL THEN
      temp.left.parent := node;
   END;
   
   temp.parent := node.parent;
   
   IF node.parent = NIL THEN
      tree.root := temp;
   ELSIF node = node.parent.left THEN
      node.parent.left := temp;
   ELSE
      node.parent.right := temp;
   END;
   
   temp.left := node;
   node.parent := temp;
   
END RotateLeft;

PROCEDURE RotateLeft (tree: Tree; a: Node);
   VAR
      b, c, p: Node;
     
BEGIN
   
   p := a.parent;
   c := a.right;
   b := c.left;
   
   (* before:
      p
      a
          c
        b
     
   *)
   
   c.parent := p;
   a.parent := c;
   
   IF p = NIL THEN (* a = tree.root *)
      tree.root := c;
   ELSE
      IF p.right = a THEN
         p.right := c;
      ELSE
         p.left := c;
      END;
   END;
   
   c.left  := a;
   a.right := b;
   
   IF b # NIL THEN
      b.parent := a;
   END;
         
   (* after:
          p
          c
      a
        b
     
   *)
   
END RotateLeft;
« Последнее редактирование: Ноябрь 15, 2013, 06:34:07 pm от ilovb »

Valery Solovey

  • Hero Member
  • *****
  • Сообщений: 509
    • Просмотр профиля
Re: Какой код лучше?
« Ответ #1 : Ноябрь 15, 2013, 09:02:39 pm »
Зависит от того, что надо было сделать. Если я правильно понимаю, то они делают разные вещи.

ilovb

  • Hero Member
  • *****
  • Сообщений: 2538
  • just another nazi test
    • Просмотр профиля
    • Oberon systems
Re: Какой код лучше?
« Ответ #2 : Ноябрь 15, 2013, 09:16:15 pm »
Может быть я где накололся... Но вообще это один и тот же алгоритм

Valery Solovey

  • Hero Member
  • *****
  • Сообщений: 509
    • Просмотр профиля
Re: Какой код лучше?
« Ответ #3 : Ноябрь 15, 2013, 09:17:24 pm »
Делают, вроде, одно и то же, но оба читать тяжело.

Я бы предложил выкинуть оба варианта и написать третий, где в именах переменных отражался бы уровень дерева:

PROCEDURE RotateLeft (tree: Tree; n1: Node);

n2Right
n3Left
n0

Или типа того.

Valery Solovey

  • Hero Member
  • *****
  • Сообщений: 509
    • Просмотр профиля
Re: Какой код лучше?
« Ответ #4 : Ноябрь 15, 2013, 09:17:53 pm »
Может быть я где накололся... Но вообще это один и тот же алгоритм
Да, это я ошибся

ilovb

  • Hero Member
  • *****
  • Сообщений: 2538
  • just another nazi test
    • Просмотр профиля
    • Oberon systems
Re: Какой код лучше?
« Ответ #5 : Ноябрь 15, 2013, 09:19:47 pm »
А во втором варианте комменты "before" и "after" ясности не добавляют?

Valery Solovey

  • Hero Member
  • *****
  • Сообщений: 509
    • Просмотр профиля
Re: Какой код лучше?
« Ответ #6 : Ноябрь 15, 2013, 09:34:28 pm »
Добавляют.

ilovb

  • Hero Member
  • *****
  • Сообщений: 2538
  • just another nazi test
    • Просмотр профиля
    • Oberon systems
Re: Какой код лучше?
« Ответ #7 : Ноябрь 15, 2013, 09:37:02 pm »
Еще вопрос:
А если бы код читался после статьи: http://www.rsdn.ru/article/alg/binstree.xml#EAJAC
и разглядывания картинки:

Какой вариант было бы легче понять?

ps Второй вариант я написал в попытках сделать код яснее. Лично мне комменты дают всю необходимую информацию для понимания. Но беда в том, что автор этих комментов я... Вот хотелось узнать мнение со стороны.
« Последнее редактирование: Ноябрь 15, 2013, 09:39:28 pm от ilovb »

Valery Solovey

  • Hero Member
  • *****
  • Сообщений: 509
    • Просмотр профиля
Re: Какой код лучше?
« Ответ #8 : Ноябрь 15, 2013, 10:18:59 pm »
Что показывают комментарии, я самостоятельно разобрался. Но далеко не сразу.

adva

  • Sr. Member
  • ****
  • Сообщений: 385
    • Просмотр профиля
Re: Какой код лучше?
« Ответ #9 : Ноябрь 15, 2013, 11:03:58 pm »
Обычно проще проверять соответствия кода ожиданиям, чем разбираться в его смысле, вроде как второй вариант более подходит для этого

ilovb

  • Hero Member
  • *****
  • Сообщений: 2538
  • just another nazi test
    • Просмотр профиля
    • Oberon systems
Re: Какой код лучше?
« Ответ #10 : Ноябрь 17, 2013, 11:12:08 am »
Еще пример переписывания:

Исходный вариант:
PROCEDURE InsertFixup (tree: Tree; node: Node);
   VAR
      temp: Node;
BEGIN
   
   node.color := red;
   
   WHILE (node # tree.root) & (node.parent.color = red) DO
      IF node.parent = node.parent.parent.left THEN
         temp := node.parent.parent.right;
         IF (temp # NIL) & (temp.color = red) THEN
            node.parent.color := black;
            temp.color := black;
            node.parent.parent.color := red;
            node := node.parent.parent;
         ELSE
            IF node = node.parent.right THEN
               node := node.parent;
               RotateLeft(tree, node);
            END;
            node.parent.color := black;
            node.parent.parent.color := red;
            RotateRight(tree, node.parent.parent);
         END;
         
      ELSE
         temp := node.parent.parent.left;
         IF (temp # NIL) & (temp.color = red) THEN
            node.parent.color := black;
            temp.color := black;
            node.parent.parent.color := red;
            node := node.parent.parent;
         ELSE
            IF node = node.parent.left THEN
               node := node.parent;
               RotateRight(tree, node);
            END;
            node.parent.color := black;
            node.parent.parent.color := red;
            RotateLeft(tree, node.parent.parent);
         END;
         
      END;
   END;
   
   tree.root.color := black;
   
END InsertFixup;
Модифицированный:
PROCEDURE InsertFixup (tree: Tree; node: Node);
   VAR
      parent, uncle, grandad: Node;
BEGIN
   
   node.color := red;

   WHILE (node # tree.root) & (node.parent.color = red) DO
     
      (* grandad # NIL *)
     
      parent  := node.parent;
      grandad := parent.parent;
     
      grandad.color := red;
     
      IF parent = grandad.left THEN
         
         uncle := grandad.right;
         
         IF (uncle # NIL) & (uncle.color = red) THEN
           
            parent.color := black;
            uncle.color  := black;
            node := grandad; (* move up *)

         ELSE (* uncle.color = black *)
           
            (* preparation for the right rotation *)
            IF node = parent.right THEN (* parent.right.color = red*)
               RotateLeft(tree, parent);
            ELSE
               node := parent; (* move up *)
            END;
            (* node = grandad.left *)
           
            RotateRight(tree, grandad); (* node = grandad.parent *)
           
            node.color := black;
           
         END;
         
      ELSE (* parent = grandad.right *)
         
         uncle := grandad.left;
         
         IF (uncle # NIL) & (uncle.color = red) THEN
           
            parent.color := black;
            uncle.color  := black;
            node := grandad; (* move up *)

         ELSE (* uncle.color = black *)
           
            (* preparation for the left rotation *)
            IF node = parent.left THEN (* parent.left.color = red *)
               RotateRight(tree, parent);
            ELSE
               node := parent; (* move up *)
            END;
            (* node = grandad.right *)
           
            RotateLeft(tree, grandad); (* node = grandad.parent *)
           
            node.color := black;
           
         END;
         
      END;
   END;
   
   tree.root.color := black;
   
END InsertFixup;

ilovb

  • Hero Member
  • *****
  • Сообщений: 2538
  • just another nazi test
    • Просмотр профиля
    • Oberon systems
Re: Какой код лучше?
« Ответ #11 : Ноябрь 17, 2013, 11:16:35 am »
Разбор случаев при вставке хорошо разобран тут: Красно-чёрное дерево

ilovb

  • Hero Member
  • *****
  • Сообщений: 2538
  • just another nazi test
    • Просмотр профиля
    • Oberon systems
Re: Какой код лучше?
« Ответ #12 : Ноябрь 17, 2013, 05:20:18 pm »
Лохонулся таки в InsertFixup.
В условии цикла нужно добавить еще одно ограничение (node.color = red):
PROCEDURE InsertFixup (tree: Tree; node: Node);
   VAR
      parent, uncle, grandad: Node;
BEGIN
   
   node.color := red;

   WHILE (node # tree.root) & (node.color = red) & (node.parent.color = red) DO
     
      parent  := node.parent;
      grandad := parent.parent;  (* grandad # NIL *)
     
      grandad.color := red;
     
      IF parent = grandad.left THEN
         
         uncle := grandad.right;
         
         IF (uncle # NIL) & (uncle.color = red) THEN
           
            parent.color := black;
            uncle.color  := black;
            node := grandad; (* move up *)

         ELSE (* uncle.color = black *)
           
            (* preparation for the right rotation *)
            IF node = parent.right THEN (* parent.right.color = red *)
               RotateLeft(tree, parent);
            ELSE
               node := parent; (* move up *)
            END;
            (* node = grandad.left *)
           
            RotateRight(tree, grandad); (* node = grandad.parent *)
           
            node.color := black;
           
         END;
         
      ELSE (* parent = grandad.right *)
         
         uncle := grandad.left;
         
         IF (uncle # NIL) & (uncle.color = red) THEN
           
            parent.color := black;
            uncle.color  := black;
            node := grandad; (* move up *)

         ELSE (* uncle.color = black *)
           
            (* preparation for the left rotation *)
            IF node = parent.left THEN (* parent.left.color = red *)
               RotateRight(tree, parent);
            ELSE
               node := parent; (* move up *)
            END;
            (* node = grandad.right *)
           
            RotateLeft(tree, grandad); (* node = grandad.parent *)
           
            node.color := black;
           
         END;
         
      END;
   END;
   
   tree.root.color := black;
   
END InsertFixup;

И еще одна вариация на тему (чтобы не менять условие цикла):
PROCEDURE InsertFixup (tree: Tree; node: Node);
   VAR
      parent, uncle, grandad: Node;
BEGIN
   
   node.color := red;

   WHILE (node # tree.root) & (node.parent.color = red) DO
     
      parent  := node.parent;
      grandad := parent.parent; (* grandad # NIL *)
     
      grandad.color := red;
     
      IF parent = grandad.left THEN
         
         uncle := grandad.right;
         
         IF (uncle # NIL) & (uncle.color = red) THEN
           
            parent.color := black;
            uncle.color := black;
            node := grandad; (* move up *)
            (* node.color = red *)
           
         ELSE (* uncle.color = black *)
           
            (* preparation for the right rotation *)
            IF node = parent.right THEN (* parent.right.color = red *)
               RotateLeft(tree, parent); (* parent = node.left *)
               node.color := black;
               node := parent; (* move down *)
            ELSE
               parent.color := black;
            END;
            (* node.color = red *)
            (* node.parent.color = black *)
           
            RotateRight(tree, grandad);
           
         END;
         
      ELSE (* parent = grandad.right *)
         
         uncle := grandad.left;
         
         IF (uncle # NIL) & (uncle.color = red) THEN
           
            parent.color := black;
            uncle.color  := black;
            node := grandad; (* move up *)
            (* node.color = red *)
           
         ELSE (* uncle.color = black *)
           
            (* preparation for the left rotation *)
            IF node = parent.left THEN (* parent.left.color = red *)
               RotateRight(tree, parent); (* parent = node.right *)
               node.color := black;
               node := parent; (* move down *)
            ELSE
               parent.color := black;
            END;
            (* node.color = red *)
            (* node.parent.color = black *)
           
            RotateLeft(tree, grandad);
           
         END;
         
      END;
   END;
   
   tree.root.color := black;
   
END InsertFixup;

ps Второй вариант ближе к исходному, чем первый.

ilovb

  • Hero Member
  • *****
  • Сообщений: 2538
  • just another nazi test
    • Просмотр профиля
    • Oberon systems
Re: Какой код лучше?
« Ответ #13 : Ноябрь 17, 2013, 05:40:28 pm »
Вообще, кстати, исходный вариант (который гуляет в интернетах) это 1:1 код из алгоритмов Кормена.

Еще предостережение, для тех, кто читает http://www.ozon.ru/context/detail/id/18319699/
Там описывается другой вариант красно-черного дерева (AA дерево), и код соответственно другой.

Еще можно встретить реализацию без поля parent. Это нормально, т.к. родитель нужен только для последовательного обхода дерева (в порядке возрастания/убывания ключей)
Если обход не нужен, то код для вставки/удаления/поиска можно написать через рекурсию.