Files
snake2025/MLV/tree_set.c
2025-03-26 09:05:12 +01:00

528 lines
13 KiB
C

/*
* This file is part of the MLV Library.
*
* Copyright (C) 2012 Adrien Boussicault, Marc Zipstein
*
*
* This Library 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 3 of the License, or
* (at your option) any later version.
*
* This Library 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 Library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "tree_set.h"
#include "warning_error.h"
#include "memory_management.h"
#include "mathematics.h"
/*
* Renvoie la hauteur d'un arbre
*/
int MLV_get_height_tree_set( MLV_TreeSet* tree_set ){
if( tree_set==NULL ){
return 0;
}
return tree_set->height;
}
/*
* Cette fonction substitue node par new_node
* dans le père de node.
*
* Avant :
* + <-- father
* / \
* + <-- node
*
* Après :
* + <-- father
* / \
* + <-- new_node
*
* Cette fonction ne modifie pas le contenu de new_node.
* Cette fonction renvoie un pointeur vers le père.
* Cette fonction n'accepte pas de node NULL.
*
*/
MLV_TreeSet* substitute_in_father_tree_Set(
MLV_TreeSet* node, MLV_TreeSet* new_node
){
if( node ){
ERROR_FULL("Node can't be NULL !");
}
MLV_TreeSet* father = node->father;
if( father ){
if( father->left_son == node ){
father->left_son = new_node;
}else{
father->right_son = new_node;
}
}
new_node->father = father;
return father;
}
/*
* Renvoie le noeud de l'arbre de valeur de clé maximale.
*/
MLV_TreeSet* MLV_get_maximal_node_of_tree_set( MLV_TreeSet* tree_set ){
MLV_TreeSet* result = NULL;
while( tree_set ){
result = tree_set;
tree_set = tree_set->right_son;
}
return result;
}
/*
* TODO
*
* Cette fonction retire un noeud de l'arbre.
* Cette focntion renvoi un pointeur vers le nouvel arbre
* obtenu.
*
*/
MLV_TreeSet* remove_node_of_tree_set( MLV_TreeSet* tree_set ){
NOT_IMPLEMENTED;
return NULL;
}
/*
* Cette fonction détruit les données contenues dans un noeud de l'arbre.
*
*/
void destroy_node_data_of_tree_set( MLV_TreeSet* tree_set ){
if( tree_set && tree_set->data_destroying_function ){
tree_set->data_destroying_function( tree_set->data );
}
}
/*
* Cette fonction suprime le noeud (en libérant la mémoire), mais
* ne suprrime pas la donnée qu'elle contient.
*
* Cette fonction renvoie un pointeur vers le nouvel arbre obtenu.
*/
MLV_TreeSet* destroy_node_of_tree_set( MLV_TreeSet* tree_set ){
MLV_TreeSet* new_tree;
new_tree = remove_node_of_tree_set( tree_set );
MLV_FREE( tree_set, MLV_TreeSet );
return new_tree;
}
/*
* Cette fonction détruit les données contenues dans un noeud, puis suprime le noeud
* de l'arbre et libère la mémoire associée à tree_set.
*
* Cette fonction renvoie un pointeur vers le nouvel arbre obtenu.
*
*/
MLV_TreeSet* destroy_node_and_his_data_of_tree_set( MLV_TreeSet* tree_set ){
MLV_TreeSet* new_tree;
destroy_node_data_of_tree_set( tree_set );
new_tree = remove_node_of_tree_set( tree_set );
MLV_FREE( tree_set, MLV_TreeSet );
return new_tree;
}
/*
* Cette fonction cherche un noeud ayant une donnée particulière.
* Cette fonction rencoie le noeud associé à cette donnée.
*/
MLV_TreeSet* MLV_find_tree_set( void* data, MLV_TreeSet* tree_set ){
if( tree_set == NULL ){
return NULL;
}
int comparaison = tree_set->sorting_function( data, tree_set->data );
if( comparaison == 0 ){
return tree_set;
}else if( comparaison < 0 ){
return MLV_find_tree_set( data, tree_set->left_son );
}else{
return MLV_find_tree_set( data, tree_set->right_son );
}
}
/*
* Supprime les données conetenu dans un tree_set et
* les remplaces par une autre donnée.
* La mémoire de l'ancienne donnée est suprimée.
*
*/
void change_data_of_node_tree_set(
void* data,
void (* data_destroying_function )( void* data ),
MLV_TreeSet * tree_set
){
destroy_node_data_of_tree_set( tree_set );
tree_set->data = data;
tree_set->data_destroying_function = data_destroying_function;
}
/*
* Créer un nouveau noeud.
*/
MLV_TreeSet* create_node_tree_set(
MLV_TreeSet* father, MLV_TreeSet* left_son, MLV_TreeSet* right_son,
void* data,
void (* data_destroying_function )( void* data ),
int (* sorting_function )( void* data1, void* data2 ),
int height
){
MLV_TreeSet* result = MLV_MALLOC( 1, MLV_TreeSet );
result->father = father;
result->left_son = left_son;
result->right_son = right_son;
result->data = data;
result->data_destroying_function = data_destroying_function;
result->sorting_function = sorting_function;
result->height = height;
return result;
}
/*
* Cette fonction équilibre localement le sous-arbre tree_set en équilibrant
* que sa racine.
*
* A l'issue de l'équilibrage, elle met à jour les hauteurs de tous les noeuds
* du sous-arbre associée à tree_set.
* Si les fils à tree_set étaient équilibrés, alors, tree_set devient lui aussi
* équilibré.
*
*/
MLV_TreeSet* equilibrate_localy_the_tree_set( MLV_TreeSet* tree_set ){
if( ! tree_set ) return NULL;
MLV_TreeSet *A, *B, *C, *D, *E, *F, *G;
MLV_TreeSet* father = tree_set->father;
int height_left = MLV_get_height_tree_set( tree_set->left_son );
int height_right = MLV_get_height_tree_set( tree_set->right_son );
int comparaison = height_left-height_right;
// Si les fils ont des tailles qui diffèrent d'au plus 1, alors le sous-arbre
// est déjà équilibré et il y a rien à faire.
if( abs( comparaison ) <= 1 ) return tree_set;
// Le sous arbres n'est pas équilibré.
// 4 type d'arbres non équilibrés peuvent se présenter
if( comparaison < 0 ){
/* B
* / \
* A \
* +
*/
B = tree_set;
A = B->left_son;
int height_right_left = MLV_get_height_tree_set( B->right_son->left_son );
int height_right_right = MLV_get_height_tree_set( B->right_son->right_son );
if( height_right_left > height_right_right ){
/* 1er type :
*
* B
* / \
* A F
* / \
* D G
* / \
* C E
*/
F = B->right_son;
D = F->left_son;
G = F->right_son;
C = D->left_son;
E = D->right_son;
}else{
/* 2ème type :
*
* B
* / \
* A D
* / \
* C F
* / \
* E G
*/
D = B->right_son;
C = D->left_son;
F = D->right_son;
E = F->left_son;
G = F->right_son;
}
}else{
/* F
* / \
* / G
* +
*/
F = tree_set;
G = F->right_son;
int height_left_left = MLV_get_height_tree_set( F->left_son->left_son );
int height_left_right = MLV_get_height_tree_set( F->left_son->right_son );
if( height_left_right > height_left_left ){
/* 3ème type :
*
* F
* / \
* B G
* / \
* A D
* / \
* C E
*/
B = F->left_son;
A = B->left_son;
D = B->right_son;
C = D->left_son;
E = D->right_son;
}else{
/* 4ème type :
*
* F
* / \
* D G
* / \
* B E
* / \
* A C
*/
D = F->left_son;
B = D->left_son;
E = D->right_son;
A = B->left_son;
C = B->right_son;
}
}
/*
* On réordonne l'arbre de façon à obtenir
*
* D
* / \
* B F
* / \ / \
* A C E G
*/
A->father = B;
C->father = B;
E->father = F;
G->father = F;
B->left_son = A;
B->right_son = C;
B->father = D;
B->height = max( A->height, C->height ) + 1;
F->left_son = E;
F->right_son = G;
F->father = D;
F->height = max( E->height, G->height ) + 1;
D->left_son = B;
D->right_son = F;
D->father = father;
D->height = max( B->height, F->height ) + 1;
// On met à jour le père du sous-arbre d'origine
if( father ){
if( father->left_son == tree_set ){
father->left_son = D;
}else{
father->right_son = D;
}
}
return D;
}
/*
* Algorithme d'insertion d'une nouvelle (cle, data) dans un arbre.
*/
MLV_TreeSet* MLV_add_data_in_tree_set(
void* data,
void (* data_destroying_function )( void* data ),
int (* sorting_function )( void* data1, void* data2 ),
MLV_TreeSet * tree_set
){
if( tree_set==NULL ){
return create_node_tree_set(
NULL, NULL, NULL,
data,
data_destroying_function, sorting_function,
1
);
}
// On cherche l'endroit où il faut insérer le noeud dans l'arbre.
MLV_TreeSet* current = tree_set;
MLV_TreeSet* father_of_insertion_point = NULL;
int comparaison;
while( current ){
comparaison = current->sorting_function( data, current->data );
if( comparaison == 0 ){
change_data_of_node_tree_set(
data, data_destroying_function, current
);
return tree_set;
}else if( comparaison < 0 ){
father_of_insertion_point = current;
current = current->left_son;
}else{
father_of_insertion_point = current;
current = current->right_son;
}
}
// on créé un nouveau noeud
current = create_node_tree_set(
father_of_insertion_point, NULL, NULL,
data,
data_destroying_function, sorting_function,
1
);
// On insère le nouveau noeud dans l'arbre.
// Ici, comme tree_set ne peut pas valoir NULL,
// father_of_insertion_point ne peut pas être NULL et
// comparaison contient une valeur.
// Le point d'insertion à donc toujours un père.
if( comparaison<0 ){
father_of_insertion_point->left_son = current;
}else{
father_of_insertion_point->right_son = current;
}
// On équilibre l'arbre.
// Pour cela on remonte l'arbre en paratn du sommet ajouté jusqu'à la
// racine et on rééqulibre localement chaque noeud rencontré.
current = father_of_insertion_point; // On part du père car le nouveau
// noeud est toujours équilibré.
MLV_TreeSet* new_root; // La nouvelle racine issue du rééquilibrage local.
while( current ){
new_root = equilibrate_localy_the_tree_set( current );
current = new_root->father;
}
// Comme father_of_inserion_point n'est pas NULL (voir explication
// précédente), current n'est pas NULL. Donc, new_root contient un valeur.
// D'après equilibrate_localy_the_tree_set, new_root est un arbre équilibré.
return new_root;
}
/*
* Execute un fonction sur chaque clé et donné de l'arbre.
*/
void MLV_foreach_data_tree_set(
void (* data_function )( void* data, void* data_user ),
void* data_user,
MLV_TreeSet* tree_set
){
if( tree_set ){
if( tree_set->left_son ){
MLV_foreach_data_tree_set( data_function, data_user, tree_set->left_son );
}
if( tree_set->right_son ){
MLV_foreach_data_tree_set( data_function, data_user, tree_set->right_son );
}
data_function( tree_set->data, data_user );
}
}
/*
* Execute un fonction sur chaque noeud de l'arbre.
*/
void foreach_node_tree_set(
void (* node_function)(MLV_TreeSet* tree_set, void* data_user),
void* data_user,
MLV_TreeSet* tree_set
){
if( tree_set ){
if( tree_set->left_son ){
foreach_node_tree_set( node_function, data_user, tree_set->left_son );
}
if( tree_set->right_son ){
foreach_node_tree_set( node_function, data_user, tree_set->right_son );
}
node_function( tree_set, data_user );
}
}
/*
* Un wrapper à utiliser avec foreach_node_tree_set.
*/
void wrapper_destroy_node_data_of_tree_set( MLV_TreeSet* tree_set, void* useless_data ){
destroy_node_data_of_tree_set( tree_set );
}
/*
* Un wrapper à utiliser avec foreach_node_tree_set.
*/
void wrapper_remove_node_of_tree_set( MLV_TreeSet* tree_set, void* useless_data ){
remove_node_of_tree_set( tree_set );
}
/*
* Un wrapper à utiliser avec foreach_node_tree_set.
*/
void wrapper_destroy_node_and_his_data_of_tree_set( MLV_TreeSet* tree_set, void* useless_data ){
destroy_node_and_his_data_of_tree_set( tree_set );
}
/*
* Un wrapper à utiliser avec foreach_node_tree_set.
*/
void wrapper_destroy_node_of_tree_set( MLV_TreeSet* tree_set, void* useless_data ){
destroy_node_of_tree_set( tree_set );
}
void free_tree_set( MLV_TreeSet* tree_set, void* useless_data ){
MLV_FREE( tree_set, MLV_TreeSet );
}
/*
* Cette fonction supprime tous les noeuds de l'abre sans détruire les données.
*/
void MLV_init_tree_set( MLV_TreeSet* tree_set ){
foreach_node_tree_set( free_tree_set, NULL, tree_set );
}
void deep_free_tree_set( MLV_TreeSet* tree_set, void* useless_data ){
destroy_node_data_of_tree_set( tree_set );
free_tree_set( tree_set, useless_data );
}
/*
* Cette fonction supprime tous les noeuds de l'abre et détruit les données
* associé à l'arbre.
*/
void MLV_clear_tree_set( MLV_TreeSet* tree_set ){
foreach_node_tree_set( deep_free_tree_set, NULL, tree_set );
}
/*
* Enlève une donnée de l'esemble.
* La donnée n'est pas supprimée.
*/
MLV_TreeSet* MLV_remove_data_from_tree_set( void* data, MLV_TreeSet* tree_set ){
MLV_TreeSet* node = MLV_find_tree_set( data, tree_set );
return destroy_node_of_tree_set( node );
}
/*
* Supprime une donnée de l'ensemble.
* La donnée est supprimée.
*/
MLV_TreeSet* MLV_remove_data_and_data_from_tree_set( void* data, MLV_TreeSet* tree_set ){
MLV_TreeSet* node = MLV_find_tree_set( data, tree_set );
destroy_node_data_of_tree_set( node );
return destroy_node_and_his_data_of_tree_set( node );
}
/*
* Supprime une donnée de l'ensemble.
* La donnée est supprimée.
*/
int MLV_is_in_tree_set( void* data, MLV_TreeSet* tree_set ){
return MLV_find_tree_set( data, tree_set )!=NULL ;
}