//----------------------------------------------------------------------------- // Copyright © 2003 - Philip Howard - All rights reserved // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. //----------------------------------------------------------------------------- // package libh/avl // homepage http://libh.slashusr.org/ //----------------------------------------------------------------------------- // author Philip Howard // email libh at ipal dot org // homepage http://phil.ipal.org/ //----------------------------------------------------------------------------- // This file is best viewed using a fixed spaced font such as Courier // and in a display at least 120 columns wide. //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // G. M. Adelson-Velskii and E. M. Landis are the inventors of the algorithm // bearing their initials, AVL. Their algorithm for height-balanced binary // search trees is the basis of the code in this package. Some modification // is made to the balancing operations, and features are added for practical // purposes. //----------------------------------------------------------------------------- #include "avl_lib.h" __PROTO_BEGIN__ //----------------------------------------------------------------------------- // function avl_delete // // purpose Delete a specified node or the current node from a given // binary tree. // // arguments 1 (AVL *) pointer to root of the binary tree // 2 (void *) pointer to node to delete, or NULL to delete the // currently selected node // // returns (void *) pointer to deleted node. // (void *) NULL if no node deleted. // // note The caller is responsible for returning the memory space // occupied by the deleted node if it was dynamically allocated. //----------------------------------------------------------------------------- void * avl_delete ( AVL * arg_tree , void * arg_node ) __PROTO_END__ { avl_link * link_p ; avl_link * bal_p ; avl_link * rep_p ; avl_link * pa_p ; #if AVL_CHECK_ARGS //--------------------------------- // Make sure we have a binary tree. //--------------------------------- if ( ! arg_tree ) return NULL; #endif //-------------------------- // Make sure we have a node. //-------------------------- if ( arg_node ) { link_p = avl_node_to_link( arg_tree, arg_node ); } else { if ( ! ( link_p = arg_tree->select ) ) return NULL; } #if AVL_PARANOID_LOGIC #if AVL_COUNT_ENABLE //------------------------------------------------------ // Make sure there are supposed to be nodes in the tree. //------------------------------------------------------ if ( avl_count_get_nodes( arg_tree ) < 1 ) return NULL; #endif /* AVL_COUNT_ENABLE */ #endif /* AVL_PARANOID_LOGIC */ //----------------------------------------------- // If this node is the selected one, unselect it. //----------------------------------------------- if ( link_p == arg_tree->select ) { arg_tree->select = NULL; } //------------------------------- // Get the deleted node's parent. //------------------------------- pa_p = link_p->pa; bal_p = pa_p; //------------------------------------------------------------------- // Decide which of several scenarios exists and handle appropriately. // In the midst of these changes, the tree structure is incomplete. // Thus calls to check or print the tree cannot be made here. //------------------------------------------------------------------- if ( ! link_p->lo ) { if ( ! link_p->hi ) { //---------------------------------------------------------------- // Since the node to be deleted is a leaf node, having no lo link // nor hi link, deletion involves finding which side the parent // links to the deleted node and making it nil. That side becomes // shorter. Re-balance from the parent. // // _P_ -> _P _P_ -> _P_ <-- balance changed here // O D O -or- D O O //---------------------------------------------------------------- if ( bal_p->lo == link_p ) bal_p->lo = NULL; #if AVL_PARANOID_LOGIC else if ( bal_p->hi != link_p ) { avl_abort( arg_tree, link_p, "avl_delete: orphan node being deleted\n" ); return NULL; } #endif /* AVL_PARANOID_LOGIC */ else bal_p->hi = NULL; } else { //------------------------------------------------------------- // Since the node to be deleted has a hi link but no lo link, // deletion involves pulling up the hi link as the replacement. // The side of the parent where the deleted node was linked is // now shorter. Re-balance from the parent. // // _P_ _P_ _P_ _P_ <-- balance changed here // O D -> O R -or- D O -> R O // R R //------------------------------------------------------------- ( rep_p = link_p->hi )->pa = bal_p; //-- Link the parent to the replacement on the same side. if ( bal_p->lo == link_p ) bal_p->lo = rep_p; #if AVL_PARANOID_LOGIC else if ( bal_p->hi != link_p ) { avl_abort( arg_tree, link_p, "avl_delete: orphan node being deleted\n" ); return NULL; } #endif /* AVL_PARANOID_LOGIC */ else bal_p->hi = rep_p; } } else { if ( ! link_p->hi ) { //------------------------------------------------------------- // Since the node to be deleted has a lo link but no hi link, // deletion involves pulling up the lo link as the replacement. // The side of the parent where the deleted node was linked is // now shorter. Re-balance from the parent. // // _P_ _P_ _P_ _P_ <-- balance changed here // O D -> O R -or- D O -> R O // R R //------------------------------------------------------------- ( rep_p = link_p->lo )->pa = bal_p; //-- Link the parent to the replacement on the same side. if ( bal_p->hi == link_p ) bal_p->hi = rep_p; #if AVL_PARANOID_LOGIC else if ( bal_p->lo != link_p ) { avl_abort( arg_tree, link_p, "avl_delete: orphan node being deleted\n" ); return NULL; } #endif /* AVL_PARANOID_LOGIC */ else bal_p->lo = rep_p; } else { //---------------------------------------------------------------- // Since the node to be deleted has both a lo link and a hi link, // deletion may or may not be simple, depending on whether one of // the links can still be pulled up. Such a link can be pulled up // if it preserves the order of the tree. That is only the case // if its link on the nearer side (hi->lo or lo->hi) is nil. // // If the simple case does not exist, the nearest order node must // be found to replace the deleted node. That node will be either // a leaf or a simple case itself, so extraction will be easy. // Finding the nearest node involves stepping down the links on // the side in the direction of the deleted node until one is nil. // // The simple case does not optimize the algorithm. Instead, it // may be an optimization to choose the side which is currently // longer, as that may reduce the number of re-shuffles done to // maintain balance. The tree order can be preserved either way. //---------------------------------------------------------------- if ( link_p->lo->depth < link_p->hi->depth ) { //-------------------------------------------------- // This way is a choice to use the hi branch. Check // to see if it has a lo link or is the simple case. //-------------------------------------------------- if ( ( rep_p = link_p->hi )->lo ) { //--------------------------------------------------------- // Since the hi branch has a lo link, we must follow down // the lo links until we find a node with no lo link. This // is the lowest keyed node in the hi branch and thus will // preserve the order of the tree when it replaces the // deleted node. Extract it by pulling up its hi branch. // // _D__________ _R__________ // Y ____O_ -> Y ____O_ // ___B_ _B_ <---balance changed here // R_ X // X // // The above illustration shows only the case where the // replacement is found 2 levels down. Real cases may // involve any number of levels. //--------------------------------------------------------- while ( rep_p->lo ) rep_p = rep_p->lo; bal_p = rep_p->pa; bal_p->lo = rep_p->hi; if ( rep_p->hi ) { rep_p->hi->pa = bal_p; } //-- Replacement inherits hi link, which is linked back. ( rep_p->hi = link_p->hi )->pa = rep_p; } else { //------------------------------------------------------- // Since the hi branch has no lo link, its apex is the // lowest keyed node in the hi branch and thus will // preserve the order of the tree when it replaces the // deleted node. Extract it by pulling up its hi branch. // // _D___ _R_ <---balance changed here // Y R_ -> Y X // X //------------------------------------------------------- bal_p = rep_p; //-- Replacement already has hi link. } //-- Replacement inherits lo link, which is linked back. ( rep_p->lo = link_p->lo )->pa = rep_p; } else { //-------------------------------------------------- // This way is a choice to use the lo branch. Check // to see if it has a hi link or is the simple case. //-------------------------------------------------- if ( ( rep_p = link_p->lo )->hi ) { //--------------------------------------------------------- // Since the lo branch has a hi link, we must follow down // the hi links until we find a node with no hi link. This // is the highest keyed node in the lo branch and thus will // preserve the order of the tree when it replaces the // deleted node. Extract it by pulling up its lo branch. // // __________D_ __________R_ // _O____ Y -> _O____ Y // _O___ _O_ <---balance changed here // _R X // X // // The above illustration shows only the case where the // replacement is found 2 levels down. Real cases may // involve any number of levels. //--------------------------------------------------------- while ( rep_p->hi ) rep_p = rep_p->hi; bal_p = rep_p->pa; bal_p->hi = rep_p->lo; if ( rep_p->lo ) { rep_p->lo->pa = bal_p; } //-- Replacement inherits lo link, which is linked back. ( rep_p->lo = link_p->lo )->pa = rep_p; } else { //------------------------------------------------------- // Since the lo branch has no hi link, its apex is the // highest keyed node in the lo branch and thus will // preserve the order of the tree when it replaces the // deleted node. Extract it by pulling up its lo branch. // // ___D_ _R_ <---balance changed here // _R Y -> X Y // X //------------------------------------------------------- bal_p = rep_p; //-- Replacement already has lo link. } //-- Replacement inherits hi link, which is linked back. ( rep_p->hi = link_p->hi )->pa = rep_p; } //-- Replacement inherits pa link, which is linked back (lo or hi). rep_p->pa = pa_p; if ( pa_p->lo == link_p ) pa_p->lo = rep_p; #if AVL_PARANOID_LOGIC else if ( pa_p->hi != link_p ) { avl_abort( arg_tree, link_p, "avl_delete: orphan node being deleted\n" ); return NULL; } #endif /* AVL_PARANOID_LOGIC */ else pa_p->hi = rep_p; //-- Replacement inherits depth. rep_p->depth = link_p->depth; } } //-- Balance the tree from the determined point of possible imbalance. avl_balance( arg_tree, bal_p ); #if AVL_CLEAN_NODE //-- Clean the deleted node to detect misuse. link_p->pa = NULL; link_p->lo = NULL; link_p->hi = NULL; link_p->depth = 0; #endif //-- Keep statistical counts. avl_count_dec_nodes( arg_tree ); //-- Return deleted node. return avl_link_to_node( arg_tree, link_p ); }