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); }