/* Binary Tree for sorting things ** ============================== ** Author: Arthur Secret ** ** 4 March 94: Bug fixed in the balancing procedure ** */ #include #include #define MAXIMUM(a,b) ((a)>(b)?(a):(b)) #include PUBLIC HTBTree * HTBTree_new ARGS1(HTComparer, comp) /********************************************************* ** This function returns an HTBTree with memory allocated ** for it when given a mean to compare things */ { HTBTree * tree = (HTBTree *)malloc(sizeof(HTBTree)); if (tree==NULL) outofmem(__FILE__, "HTBTree_new"); tree->compare = comp; tree->top = NULL; return tree; } PRIVATE void HTBTElement_free ARGS1(HTBTElement*, element) /********************************************************** ** This void will free the memory allocated for one element */ { if (element) { if (element->left != NULL) HTBTElement_free(element->left); if (element->right != NULL) HTBTElement_free(element->right); FREE(element); } } PUBLIC void HTBTree_free ARGS1(HTBTree*, tree) /************************************************************** ** This void will free the memory allocated for the whole tree */ { HTBTElement_free(tree->top); FREE(tree); } PRIVATE void HTBTElementAndObject_free ARGS1(HTBTElement*, element) /********************************************************** ** This void will free the memory allocated for one element */ { if (element) { /* Just in case nothing was in the tree anyway */ if (element->left != NULL) HTBTElementAndObject_free(element->left); if (element->right != NULL) HTBTElementAndObject_free(element->right); FREE(element->object); FREE(element); } } PUBLIC void HTBTreeAndObject_free ARGS1(HTBTree*, tree) /************************************************************** ** This void will free the memory allocated for the whole tree */ { HTBTElementAndObject_free(tree->top); FREE(tree); } PUBLIC void HTBTree_add ARGS2( HTBTree*, tree, void*, object) /********************************************************************** ** This void is the core of HTBTree.c . It will ** 1/ add a new element to the tree at the right place ** so that the tree remains sorted ** 2/ balance the tree to be as fast as possible when reading it */ { HTBTElement * father_of_element; HTBTElement * added_element; HTBTElement * forefather_of_element; HTBTElement * father_of_forefather; BOOL father_found,top_found; int depth,depth2,corrections; /* father_of_element is a pointer to the structure that is the father of the ** new object "object". ** added_element is a pointer to the structure that contains or will contain ** the new object "object". ** father_of_forefather and forefather_of_element are pointers that are used ** to modify the depths of upper elements, when needed. ** ** father_found indicates by a value NO when the future father of "object" ** is found. ** top_found indicates by a value NO when, in case of a difference of depths ** < 2, the top of the tree is encountered and forbids any further try to ** balance the tree. ** corrections is an integer used to avoid infinite loops in cases ** such as: ** ** 3 3 ** 4 4 ** 5 5 ** ** 3 is used here to show that it need not be the top of the tree. */ /* ** 1/ Adding of the element to the binary tree */ if (tree->top == NULL) { tree->top = (HTBTElement *)malloc(sizeof(HTBTElement)); if (tree->top == NULL) outofmem(__FILE__, "HTBTree_add"); tree->top->up = NULL; tree->top->object = object; tree->top->left = NULL; tree->top->left_depth = 0; tree->top->right = NULL; tree->top->right_depth = 0; } else { father_found = YES; father_of_element = tree->top; added_element = NULL; father_of_forefather = NULL; forefather_of_element = NULL; while (father_found) { if (tree->compare(object,father_of_element->object)<0) { if (father_of_element->left != NULL) father_of_element = father_of_element->left; else { father_found = NO; father_of_element->left = (HTBTElement *)malloc(sizeof(HTBTElement)); if (father_of_element->left==NULL) outofmem(__FILE__, "HTBTree_add"); added_element = father_of_element->left; added_element->up = father_of_element; added_element->object = object; added_element->left = NULL; added_element->left_depth = 0; added_element->right = NULL; added_element->right_depth = 0; } } if (tree->compare(object,father_of_element->object)>=0) { if (father_of_element->right != NULL) father_of_element = father_of_element->right; else { father_found = NO; father_of_element->right = (HTBTElement *)malloc(sizeof(HTBTElement)); if (father_of_element->right==NULL) outofmem(__FILE__, "HTBTree_add"); added_element = father_of_element->right; added_element->up = father_of_element; added_element->object = object; added_element->left = NULL; added_element->left_depth = 0; added_element->right = NULL; added_element->right_depth = 0; } } } /* ** Changing of all depths that need to be changed */ father_of_forefather = father_of_element; forefather_of_element = added_element; do { if (father_of_forefather->left == forefather_of_element) { depth = father_of_forefather->left_depth; father_of_forefather->left_depth = 1 + MAXIMUM(forefather_of_element->right_depth, forefather_of_element->left_depth); depth2 = father_of_forefather->left_depth; } else { depth = father_of_forefather->right_depth; father_of_forefather->right_depth = 1 + MAXIMUM(forefather_of_element->right_depth, forefather_of_element->left_depth); depth2 = father_of_forefather->right_depth; } forefather_of_element = father_of_forefather; father_of_forefather = father_of_forefather->up; } while ((depth != depth2) && (father_of_forefather != NULL)); /* ** 2/ Balancing the binary tree, if necessary */ top_found = YES; corrections = 0; while ((top_found) && (corrections < 7)) { if ((abs(father_of_element->left_depth - father_of_element->right_depth)) < 2) { if (father_of_element->up != NULL) father_of_element = father_of_element->up; else top_found = NO; } else { /* We start the process of balancing */ corrections = corrections + 1; /* ** corrections is an integer used to avoid infinite ** loops in cases such as: ** ** 3 3 ** 4 4 ** 5 5 ** ** 3 is used to show that it need not be the top of the tree ** But let's avoid these two exceptions anyhow ** with the two following conditions (4 March 94 - AS) */ if ((father_of_element->left == NULL) && (father_of_element->right->right == NULL) && (father_of_element->right->left->left == NULL) && (father_of_element->right->left->right == NULL)) corrections = 7; if ((father_of_element->right == NULL) && (father_of_element->left->left == NULL) && (father_of_element->left->right->right == NULL) && (father_of_element->left->right->left == NULL)) corrections = 7; if (father_of_element->left_depth > father_of_element->right_depth) { added_element = father_of_element->left; father_of_element->left_depth = added_element->right_depth; added_element->right_depth = 1 + MAXIMUM(father_of_element->right_depth, father_of_element->left_depth); if (father_of_element->up != NULL) { /* Bug fixed in March 94 - AS */ BOOL first_time; father_of_forefather = father_of_element->up; forefather_of_element = added_element; first_time = YES; do { if (father_of_forefather->left == forefather_of_element->up) { depth = father_of_forefather->left_depth; if (first_time) { father_of_forefather->left_depth = 1 + MAXIMUM(forefather_of_element->left_depth, forefather_of_element->right_depth); first_time = NO; } else father_of_forefather->left_depth = 1 + MAXIMUM(forefather_of_element->up->left_depth, forefather_of_element->up->right_depth); depth2 = father_of_forefather->left_depth; } else { depth = father_of_forefather->right_depth; if (first_time) { father_of_forefather->right_depth = 1 + MAXIMUM(forefather_of_element->left_depth, forefather_of_element->right_depth); first_time = NO; } else father_of_forefather->right_depth = 1 + MAXIMUM(forefather_of_element->up->left_depth, forefather_of_element->up->right_depth); depth2 = father_of_forefather->right_depth; } forefather_of_element = forefather_of_element->up; father_of_forefather = father_of_forefather->up; } while ((depth != depth2) && (father_of_forefather != NULL)); father_of_forefather = father_of_element->up; if (father_of_forefather->left == father_of_element) { /* ** 3 3 ** 4 5 ** When tree 5 6 becomes 7 4 ** 7 8 8 6 ** ** 3 is used to show that it may not be the top of the ** tree. */ father_of_forefather->left = added_element; father_of_element->left = added_element->right; added_element->right = father_of_element; } if (father_of_forefather->right == father_of_element) { /* ** 3 3 ** 4 5 ** When tree 5 6 becomes 7 4 ** 7 8 8 6 ** ** 3 is used to show that it may not be the top of the ** tree */ father_of_forefather->right = added_element; father_of_element->left = added_element->right; added_element->right = father_of_element; } added_element->up = father_of_forefather; } else { /* ** ** 1 2 ** When tree 2 3 becomes 4 1 ** 4 5 5 3 ** ** 1 is used to show that it is the top of the tree */ added_element->up = NULL; father_of_element->left = added_element->right; added_element->right = father_of_element; } father_of_element->up = added_element; if (father_of_element->left != NULL) father_of_element->left->up = father_of_element; } else { added_element = father_of_element->right; father_of_element->right_depth = added_element->left_depth; added_element->left_depth = 1 + MAXIMUM(father_of_element->right_depth, father_of_element->left_depth); if (father_of_element->up != NULL) /* Bug fixed in March 94 - AS */ { BOOL first_time; father_of_forefather = father_of_element->up; forefather_of_element = added_element; first_time = YES; do { if (father_of_forefather->left == forefather_of_element->up) { depth = father_of_forefather->left_depth; if (first_time) { father_of_forefather->left_depth = 1 + MAXIMUM(forefather_of_element->left_depth, forefather_of_element->right_depth); first_time = NO; } else father_of_forefather->left_depth = 1 + MAXIMUM(forefather_of_element->up->left_depth, forefather_of_element->up->right_depth); depth2 = father_of_forefather->left_depth; } else { depth = father_of_forefather->right_depth; if (first_time) { father_of_forefather->right_depth = 1 + MAXIMUM(forefather_of_element->left_depth, forefather_of_element->right_depth); first_time = NO; } else father_of_forefather->right_depth = 1 + MAXIMUM(forefather_of_element->up->left_depth, forefather_of_element->up->right_depth); depth2 = father_of_forefather->right_depth; } father_of_forefather = father_of_forefather->up; forefather_of_element = forefather_of_element->up; } while ((depth != depth2) && (father_of_forefather != NULL)); father_of_forefather = father_of_element->up; if (father_of_forefather->left == father_of_element) { /* ** 3 3 ** 4 6 ** When tree 5 6 becomes 4 8 ** 7 8 5 7 ** ** 3 is used to show that it may not be the top of the ** tree. */ father_of_forefather->left = added_element; father_of_element->right = added_element->left; added_element->left = father_of_element; } if (father_of_forefather->right == father_of_element) { /* ** 3 3 ** 4 6 ** When tree 5 6 becomes 4 8 ** 7 8 5 7 ** ** 3 is used to show that it may not be the top of the ** tree */ father_of_forefather->right = added_element; father_of_element->right = added_element->left; added_element->left = father_of_element; } added_element->up = father_of_forefather; } else { /* ** ** 1 3 ** When tree 2 3 becomes 1 5 ** 4 5 2 4 ** ** 1 is used to show that it is the top of the tree. */ added_element->up = NULL; father_of_element->right = added_element->left; added_element->left = father_of_element; } father_of_element->up = added_element; if (father_of_element->right != NULL) father_of_element->right->up = father_of_element; } } } while (father_of_element->up != NULL) { father_of_element = father_of_element->up; } tree->top = father_of_element; } } PUBLIC HTBTElement * HTBTree_next ARGS2( HTBTree*, tree, HTBTElement*, ele) /************************************************************************** ** this function returns a pointer to the leftmost element if ele is NULL, ** and to the next object to the right otherwise. ** If no elements left, returns a pointer to NULL. */ { HTBTElement * father_of_element; HTBTElement * father_of_forefather; if (ele == NULL) { father_of_element = tree->top; if (father_of_element != NULL) while (father_of_element->left != NULL) father_of_element = father_of_element->left; } else { father_of_element = ele; if (father_of_element->right != NULL) { father_of_element = father_of_element->right; while (father_of_element->left != NULL) father_of_element = father_of_element->left; } else { father_of_forefather = father_of_element->up; while (father_of_forefather && (father_of_forefather->right == father_of_element)) { father_of_element = father_of_forefather; father_of_forefather = father_of_element->up; } father_of_element = father_of_forefather; } } #ifdef BTREE_TRACE /* The option -DBTREE_TRACE will give much more information ** about the way the process is running, for debugging matters */ if (father_of_element != NULL) { printf("\nObject = %s\t",(char *)father_of_element->object); if (father_of_element->up != NULL) printf("Objet du pere = %s\n", (char *)father_of_element->up->object); else printf("Pas de Pere\n"); if (father_of_element->left != NULL) printf("Objet du fils gauche = %s\t", (char *)father_of_element->left->object); else printf("Pas de fils gauche\t"); if (father_of_element->right != NULL) printf("Objet du fils droit = %s\n", (char *)father_of_element->right->object); else printf("Pas de fils droit\n"); printf("Profondeur gauche = %d\t",father_of_element->left_depth); printf("Profondeur droite = %d\n",father_of_element->right_depth); printf(" **************\n"); } #endif return father_of_element; } #ifdef TEST main () /****************************************************** ** This is just a test to show how to handle HTBTree.c */ { HTBTree * tree; HTBTElement * next_element; tree = HTBTree_new((HTComparer)strcasecomp); HTBTree_add(tree,"hypertext"); HTBTree_add(tree,"Addressing"); HTBTree_add(tree,"X11"); HTBTree_add(tree,"Tools"); HTBTree_add(tree,"Proposal.wn"); HTBTree_add(tree,"Protocols"); HTBTree_add(tree,"NeXT"); HTBTree_add(tree,"Daemon"); HTBTree_add(tree,"Test"); HTBTree_add(tree,"Administration"); HTBTree_add(tree,"LineMode"); HTBTree_add(tree,"DesignIssues"); HTBTree_add(tree,"MarkUp"); HTBTree_add(tree,"Macintosh"); HTBTree_add(tree,"Proposal.rtf.wn"); HTBTree_add(tree,"FIND"); HTBTree_add(tree,"Paper"); HTBTree_add(tree,"Tcl"); HTBTree_add(tree,"Talks"); HTBTree_add(tree,"Architecture"); HTBTree_add(tree,"VMSHelp"); HTBTree_add(tree,"Provider"); HTBTree_add(tree,"Archive"); HTBTree_add(tree,"SLAC"); HTBTree_add(tree,"Project"); HTBTree_add(tree,"News"); HTBTree_add(tree,"Viola"); HTBTree_add(tree,"Users"); HTBTree_add(tree,"FAQ"); HTBTree_add(tree,"WorkingNotes"); HTBTree_add(tree,"Windows"); HTBTree_add(tree,"FineWWW"); HTBTree_add(tree,"Frame"); HTBTree_add(tree,"XMosaic"); HTBTree_add(tree,"People"); HTBTree_add(tree,"All"); HTBTree_add(tree,"Curses"); HTBTree_add(tree,"Erwise"); HTBTree_add(tree,"Carl"); HTBTree_add(tree,"MidasWWW"); HTBTree_add(tree,"XPM"); HTBTree_add(tree,"MailRobot"); HTBTree_add(tree,"Illustrations"); HTBTree_add(tree,"VMClient"); HTBTree_add(tree,"XPA"); HTBTree_add(tree,"Clients.html"); HTBTree_add(tree,"Library"); HTBTree_add(tree,"CERNLIB_Distribution"); HTBTree_add(tree,"libHTML"); HTBTree_add(tree,"WindowsPC"); HTBTree_add(tree,"tkWWW"); HTBTree_add(tree,"tk2.3"); HTBTree_add(tree,"CVS-RCS"); HTBTree_add(tree,"DecnetSockets"); HTBTree_add(tree,"SGMLStream"); HTBTree_add(tree,"NextStep"); HTBTree_add(tree,"CVSRepository_old"); HTBTree_add(tree,"ArthurSecret"); HTBTree_add(tree,"CVSROOT"); HTBTree_add(tree,"HytelnetGate"); HTBTree_add(tree,"cern.www.new.src"); HTBTree_add(tree,"Conditions"); HTBTree_add(tree,"HTMLGate"); HTBTree_add(tree,"Makefile"); HTBTree_add(tree,"Newsgroups.html"); HTBTree_add(tree,"People.html"); HTBTree_add(tree,"Bugs.html"); HTBTree_add(tree,"Summary.html"); HTBTree_add(tree,"zDesignIssues.wn"); HTBTree_add(tree,"HT.draw"); HTBTree_add(tree,"HTandCERN.wn"); HTBTree_add(tree,"Ideas.wn"); HTBTree_add(tree,"MarkUp.wn"); HTBTree_add(tree,"Proposal.html"); HTBTree_add(tree,"SearchPanel.draw"); HTBTree_add(tree,"Comments.wn"); HTBTree_add(tree,"Xanadu.html"); HTBTree_add(tree,"Storinglinks.html"); HTBTree_add(tree,"TheW3Book.html"); HTBTree_add(tree,"Talk_Feb-91.html"); HTBTree_add(tree,"JFosterEntry.txt"); HTBTree_add(tree,"Summary.txt"); HTBTree_add(tree,"Bibliography.html"); HTBTree_add(tree,"HTandCern.txt"); HTBTree_add(tree,"Talk.draw"); HTBTree_add(tree,"zDesignNotes.html"); HTBTree_add(tree,"Link.html"); HTBTree_add(tree,"Status.html"); HTBTree_add(tree,"http.txt"); HTBTree_add(tree,"People.html~"); HTBTree_add(tree,"TAGS"); HTBTree_add(tree,"summary.txt"); HTBTree_add(tree,"Technical.html"); HTBTree_add(tree,"Terms.html"); HTBTree_add(tree,"JANETAccess.html"); HTBTree_add(tree,"People.txt"); HTBTree_add(tree,"README.txt"); HTBTree_add(tree,"CodingStandards.html"); HTBTree_add(tree,"Copyright.txt"); HTBTree_add(tree,"Status_old.html"); HTBTree_add(tree,"patches~"); HTBTree_add(tree,"RelatedProducts.html"); HTBTree_add(tree,"Implementation"); HTBTree_add(tree,"History.html"); HTBTree_add(tree,"Makefile.bak"); HTBTree_add(tree,"Makefile.old"); HTBTree_add(tree,"Policy.html"); HTBTree_add(tree,"WhatIs.html"); HTBTree_add(tree,"TheProject.html"); HTBTree_add(tree,"Notation.html"); HTBTree_add(tree,"Helping.html"); HTBTree_add(tree,"Cyber-WWW.sit.Hqx"); HTBTree_add(tree,"Glossary.html"); HTBTree_add(tree,"maketags.html"); HTBTree_add(tree,"IntroCS.html"); HTBTree_add(tree,"Contrib"); HTBTree_add(tree,"Help.html"); HTBTree_add(tree,"CodeManagExec"); HTBTree_add(tree,"HT-0.1draz"); HTBTree_add(tree,"Cello"); HTBTree_add(tree,"TOPUB"); HTBTree_add(tree,"BUILD"); HTBTree_add(tree,"BUILDALL"); HTBTree_add(tree,"Lynx"); HTBTree_add(tree,"ArthurLibrary"); HTBTree_add(tree,"RashtyClient"); HTBTree_add(tree,"#History.html#"); HTBTree_add(tree,"PerlServers"); HTBTree_add(tree,"modules"); HTBTree_add(tree,"NCSA_httpd"); HTBTree_add(tree,"MAIL2HTML"); HTBTree_add(tree,"core"); HTBTree_add(tree,"EmacsWWW"); #ifdef BTREE_TRACE printf("\nTreeTopObject=%s\n\n",tree->top->object); #endif next_element = HTBTree_next(tree,NULL); while (next_element != NULL) { #ifndef BTREE_TRACE printf("The next element is %s\n",next_element->object); #endif next_element = HTBTree_next(tree,next_element); } HTBTree_free(tree); } #endif