Exemple #1
0
 public AVLTreeNode(T value, AVLTreeNode <T> parent, AVLTree <T> tree) // последний параметр будет использован только в методе Balance.
 {
     Value  = value;
     Parent = parent;
     _tree  = tree;
 }
        public bool Remove(T value)
        {
            // можно воспользоваться методом Find.
            AVLTreeNode <T> current = Find(value);          //вместо прохождения по всему дереву в этом методе - используем уже существующий метод Find, который делает то же самое.

            Count--;                                        // уменьшаем количество узлов в дереве на 1 из-за удаления.

            AVLTreeNode <T> treeToBalance = current.Parent; //Добавляем указатель для балансировки родительского элемента удаляемого элемента, т.к. родительски элемент может разбалансироваться после удаления его потомка. Относительно этого указателя позже будем проверять и проводиь балансировку.

            if (current == null)                            //если искомый узел не существует возвращаем false - и дальше вообще не смотрим - сразу выйти из метода и не делать длинной конструкции. Так короче, чем если проверять if (current != null) и внутрь этой конструкции ставить весь остальной код. Если false - то код дальше просто не пойдёт.
            {
                return(false);
            }

            // 1-й вариант: если удаляемый элемент не имеет правого элемента - то сместить на место удаляемого элемента его левого потомка.
            if (current.Right == null)        // если у него нет правого потомка
            {
                if (current.Parent == null)   // если удаляемый узел является корнем
                {
                    Head = current.Left;      // то левого потомка удаляемого элемента делаем корнем. А если при этом и левый равен null - получается нет ни правого, ни левого. Т.е. мы удаляем единственный элемент в дереве - корень. И в head автоматически записывается null.
                    if (current.Left != null) // и если у него есть левый потомок
                    {
                        Head.Parent = null;   // то, поскольку левый теперь становится корнем, удаляем в нём ссылку на родителя.
                    }
                }
                else // если удаляемый узел не является корнем
                {
                    int result = current.Parent.CompareTo(current.Value);   // здесь надо проверить: удаляемый элемент находился справа или слева от своего родителя. Для этого вводим буферную переменную, которая покажет: справа элемент или слева. // здесь можно было бы поставить в скобках и просто value - т.к. это будет значение именно удаляемого элемента. Но (и в примере так и указано) можно использовать и current.Value - что б уже как говориться "точно" указало на то что нужно.

                    if (result > 0)                         //родитель больше, а элемент - меньше (значит слева)
                    {
                        current.Parent.Left = current.Left; // родитель удаляемого элемента ссылается (по левой ссылке) на левого потомка удаляемого узла.
                        if (current.Left != null)
                        {
                            current.Left.Parent = current.Parent;    // А вот здесь - интересный момент: в примере урока в классе AVLTreeNode в свойствах элемента Left и Right было прописано, что если они не равны null, то в левом(или правом) потомке устанавливалась родительская ссылка на текущий элемент.  - Поэтому в примерах ссылка на новый родительский элемент устанавливалась автоматически, и текущей строки там нет. Здесь же я немного своим путём пошёл (не таким удобным:)). В этой строке - левый потомок теперь ссылается на родителя удаляемого элемента.
                        }
                    }
                    else if (result < 0)                     // если родитель меньше, значит элемент справа.     //лучше ставить else if, а не просто if - чтобы во второй if лишний раз не зашло.
                    {
                        current.Parent.Right = current.Left; // родитель удаляемого элемента ссылается (по правой ссылке) на левого потомка удаляемого узла.
                        if (current.Left != null)
                        {
                            current.Left.Parent = current.Parent;    // левый потомок теперь ссылается на родителя удаляемого элемента
                        }
                    }
                }
            }
            //2-й вариант: если удаляемый элемент имеет правого потомка, который не имеет левого потомка: тогда сделать правого потомка потомком родительского узла.
            else if (current.Right.Left == null) //проверка 2-го условия 2-го варианта
            {
                if (current.Parent == null)      // если удаляемый узел является корнем
                {
                    Head = current.Right;
                    current.Right.Left = current.Left;
                    if (current.Left != null)                // проверить существует ли левый потомок удаляемого узла, иначе будет NullReferenceException
                    {
                        current.Left.Parent = current.Right; //и дать ему ссылку на нового родителя.
                    }
                }
                else //если удаляемый корнем не является.
                {
                    int result = current.Parent.CompareTo(current.Value); //буферная переменная: отображает - слева текущий элемент или справа от родителя?

                    if (result > 0)
                    {
                        current.Parent.Left  = current.Right;
                        current.Right.Left   = current.Left;     //(повторяющийся код)вынесена вверх в примере урока
                        current.Right.Parent = current.Parent;   //(повторяющийся код)делается автоматически в примере урока
                        if (current.Left != null)                //- да действительно, лучше подобную операцию перенести в определение класса... - Это то, то в примере урока прописано в классе AVLTreeNode.
                        {
                            current.Left.Parent = current.Right; //если использовать определение класса AVLTreeNode как в примере урока, то эту строку необязательно писать - эта операция произойдёт автоматически.
                        }
                    }
                    else if (result < 0)
                    {
                        current.Parent.Right = current.Right;
                        current.Right.Left   = current.Left;     //(повторяющийся код)вынесена вверх в примере урока
                        current.Right.Parent = current.Parent;   //(повторяющийся код)делается автоматически в примере урока
                        if (current.Left != null)                //(повторяющийся код)делается автоматически в примере урока
                        {
                            current.Left.Parent = current.Right; //(повторяющийся код)делается автоматически в примере урока
                        }
                    }
                }
            }
            // 3-й вариант: если у правого потомка удаляемого элемента есть левый потомок: поместить крайнего левого потомка на место удаляемого узла (сделать родителя удаляемого узла родителем этого крайнего левого потомка)
            else
            {
                AVLTreeNode <T> leftMost = current.Right.Left; //добавляем ещё один указатель: на этот самый левый потомок правого потомка.

                while (leftMost.Left != null)                  //циклом перемещаем этот указатель на самого последнего из левых потомков.
                {
                    leftMost = leftMost.Left;
                }

                leftMost.Parent.Left  = leftMost.Right;  // Там, где убрали узел leftmost - меняем связи соседствовавших с ним элементов.
                leftMost.Right.Parent = leftMost.Parent; // Поскольку эти 4 строки будут независимо от того, становится ли leftMost корнем дерева или нет, то выносим их за пределы if.
                leftMost.Left         = current.Left;    //левый
                leftMost.Right        = current.Right;   //правый

                if (current.Parent == null)
                {
                    Head = leftMost;                 //смещаем элементы в соответствии с правилом и устанавливаем новые связи.
                    current.Right.Parent = leftMost; // (повторяющийся код - можно вынести вверх. но не буду, оставлю, чтоб понаблюдать можно было)
                    if (current.Left != null)        // (повторяющийся код - можно вынести вверх)
                    {
                        current.Left.Parent = leftMost;
                    }
                    leftMost.Parent = null; //убираем из leftMost ссылку на его бывшего родителя, т.к. leftMost теперь корень.
                }
                else
                {   //проверка нахождения leftMost слева или справа от его родителя.
                    int result = current.Parent.CompareTo(current.Value);

                    if (result > 0)
                    {
                        current.Parent.Left = leftMost;
                    }
                    else if (result < 0)
                    {
                        current.Parent.Right = leftMost;
                    }

                    leftMost.Parent      = current.Parent; //родитель
                    current.Right.Parent = leftMost;       //родитель правого   // (повторяющийся код - можно вынести вверх)
                    if (current.Left != null)              // (повторяющийся код - можно вынести вверх)
                    {
                        current.Left.Parent = leftMost;    //родитель левого
                    }
                }
            }
            treeToBalance.Balance();    //баласировка дерева.
            return(true);
        }