Loading AI tools
tipo di struttura di dati Da Wikipedia, l'enciclopedia libera
In informatica, un albero o struttura ad albero (tree in inglese) è la struttura dati che si riconduce al concetto di albero con radice presente nella teoria dei grafi. Un albero si compone di due tipi di sottostrutture fondamentali: il nodo, che in genere contiene informazioni, e l'arco, che stabilisce un collegamento gerarchico fra due nodi: si parla allora di un nodo padre dal quale esce un arco orientato che lo collega a un nodo figlio.
Si chiede inoltre che ogni nodo possa avere al massimo un unico arco entrante, mentre dai diversi nodi possono uscire diversi numeri di archi uscenti. Si chiede infine che l'albero possegga un unico nodo privo di arco entrante: questo nodo viene detto radice (root) dell'albero. Ogni nodo che non presenta archi uscenti è detto foglia (leaf node) e in ogni albero finito, cioè con un numero finito di nodi, si trova almeno un nodo foglia. Ovviamente, un nodo può essere contemporaneamente padre (se ha archi uscenti) e figlio (se ha un arco entrante, ovvero se è diverso dalla radice).
Solitamente ogni nodo porta con sé delle informazioni e molto spesso anche una chiave con cui è possibile identificarlo univocamente all'interno dell'albero. L'altezza o profondità dell'albero è il massimo delle lunghezze dei suoi cammini massimali, cammini che vanno dalla radice alle sue foglie.
Principalmente gli alberi si dividono in alberi non ordinati e alberi ordinati. I primi non seguono alcuna regola per quanto riguarda le relazioni padre-figlio, mentre i secondi impongono che tra il nodo padre e i nodi figli ci sia un ben preciso ordinamento. I più utilizzati in ambito informatico sono sicuramente gli alberi ordinati. Un'altra classificazione può essere fatta in base al numero massimo di figli che un nodo può avere. Si può parlare dunque di Alberi binari in cui ogni nodo può avere al massimo due figli, oppure di Alberi n-ari in cui non vi è un limite al numero massimo di nodi figlio.
Un'ulteriore caratterizzazione è quella che si basa sul cosiddetto bilanciamento: un albero è perfettamente bilanciato se ha tutte le foglie al medesimo livello, ovvero se ogni foglia dell'albero ha la medesima distanza dalla radice. Un albero è -bilanciato se, per ogni nodo , detto l'insieme dei massimi livelli raggiungibili seguendo tutte le foglie di , la differenza tra il massimo e il minimo di non è maggiore di .
L'insieme di nodi al di sopra un nodo compone l'insieme dei predecessori, quelli che seguono sono i discendenti.
I tipi di albero più diffusi sono i seguenti:
L'implementazione più diffusa degli alberi si basa su liste concatenate, ovvero da oggetti (i nodi) che referenziano altri oggetti.
Un'interfaccia tipica di un nodo di un albero binario in Java può essere la seguente:
public class Nodo {
public Nodo figlioSinistro;
public Nodo figlioDestro;
//le informazioni contenute dal nodo, di tipo Object per generalizzare
public Object informazioni;
//una chiave univoca per identificare il nodo, ad esempio un intero
public int chiaveUnivoca;
}
Notoriamente, gli alberi heap sono implementabili anche tramite array o vettori
typedef int TKey;
typedef int TSat;
struct SInfo{
TKey key;
TSat satellite;
};
typedef struct SInfo TInfo;
struct SNode {
TInfo info;
struct SNode *left;
struct SNode *right;
};
typedef struct SNode TNode;
typedef TNode* TTree;
TTree tree_create(){
return NULL;
}
TTree tree_destroy(TTree tree) {
if(tree_empty(tree)==true)
return NULL;
else if((tree->left==NULL) && (tree->right==NULL)) {
free(tree);
return NULL;
} else {
tree->left = tree_destroy(tree->left);
tree->right = tree_destroy(tree->right);
free(tree);
return NULL;
}
}
TNode* tree_search_recursive(TTree tree, TKey key){
if((tree_empty(tree)==true) || equal(tree->info.key, key))
return tree;
else {
if(greater(key, tree->info.key))
return tree_search_recursive(tree->right, key);
else
return tree_search_recursive(tree->left, key);
}
}
TTree tree_insert_recursive(TTree tree, TInfo info){
if(tree_empty(tree)==true){
TNode* newnode;
newnode=(TNode*) malloc(sizeof(TNode));
if(newnode==NULL){
printf("Errore di allocazione");
exit(1);
}
newnode->right=newnode->left=NULL;
newnode->info=info;
return newnode;
} else if(!greater(info.key,tree->info.key)){
tree->left=tree_insert_recursive(tree->left,info);
return tree;
} else {
tree->right=tree_insert_recursive(tree->right,info);
return tree;
}
}
TTree tree_delete_ver2(TTree tree, TKey key){
if(tree_empty(tree)==true) /* Albero vuoto */
return NULL;
else if(greater(tree->info.key, key)) {
/* Cancellazione nel Ramo di Sinistra */
tree->left=tree_delete_ver2(tree->left, key);
return tree;
}
else if(greater(key, tree->info.key)) {
/* Cancellazione nel Ramo di Destra */
tree->right=tree_delete_ver2(tree->right, key);
return tree;
} else { /*tree->info.key==key */
/*Cancellazione della Radice */
TNode* min_right, *alias;
if(tree_empty(tree->right)==true)
alias=tree->left;
else {
min_right=tree_min(tree->right);
min_right->left=tree->left;
alias=tree->right;
}
free(tree);
return alias;
}
}
Molti algoritmi che operano sugli alberi richiedono di visitare tutti i nodi dell'albero, ovvero di definire un (sotto) algoritmo che dato un albero costruisce permutazione dell'insieme dei suoi nodi. I metodi di visita in profondità sono i seguenti.
void visitapreordine(Nodepointer start)
{
if (start == NULL) return;
start->markup = 1;
printf(”%d %d\n”, start->valore, start->markup);
visitapreordine(start->son_sx);
visitapreordine(start->son_dx);
}
Nodepointer
è un puntatore al nodo radice da cui parte la ricerca in pre-ordine. son_sx
e son_dx
sono rispettivamente i figli sinistro e destro del nodo da cui parte la ricerca. L'algoritmo descritto si limita a stampare a video il valore e il markup del nodo considerato.
void visitainordine(Nodepointer start)
{
if (start == NULL) return;
visitainordine(start->son_sx);
start->markup = 1;
printf(”%d %d\n”, start->valore, start->markup);
visitainordine(start->son_dx);
}
Nodepointer
è un puntatore al nodo radice da cui parte la ricerca in ordine. son_sx
e son_dx
sono rispettivamente i figli sinistro e destro del nodo da cui parte la ricerca. L'algoritmo descritto si limita a stampare a video il valore e il markup del nodo considerato.
void visitapostordine(Nodepointer start)
{
if (start == NULL) return;
visitapostordine(start->son_sx);
visitapostordine(start->son_dx);
start->markup = 1;
printf(”%d %d\n”, start->valore, start->markup);
}
Nodepointer
è un puntatore al nodo radice da cui parte la ricerca in post-ordine. son_sx
e son_dx
sono rispettivamente i figli sinistro e destro del nodo da cui parte la ricerca. L'algoritmo descritto si limita a stampare a video il valore e il markup del nodo considerato.
Ciascun metodo può essere applicato in modo ricorsivo, ovvero per "visita di un sottoalbero" si intenderà l'applicazione dello stesso algoritmo di visita al nodo radice di tale sottoalbero. In alternativa è possibile implementare la visita in profondità facendo uso di una pila.
Un esempio di metodo esclusivamente non ricorsivo di visita di un albero è invece dato dalla visita in ampiezza.
Seamless Wikipedia browsing. On steroids.
Every time you click a link to Wikipedia, Wiktionary or Wikiquote in your browser's search results, it will show the modern Wikiwand interface.
Wikiwand extension is a five stars, simple, with minimum permission required to keep your browsing private, safe and transparent.