Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
Website: http://yalma.fime.uanl.mx/~romeo/ED/2011/
Sesiones: 48
Material de apoyo:
Estructura de Datos con C y C++.
Yedidyah Langsam, Moshe J. Augenstein, Aaron M. Tenenbaum, Brooklyn College
Segunda Edición, Prentice-Hall.
Estructura de Datos.
Román Martínez, Elda Quiroga.
Thomson Learning.
Software:
Compiladores GCC (GNU Compiler Collection)
Temario:
◦ Árboles en general
◦ Árboles binarios
◦ Árboles balanceados
◦ Árboles multicaminos
Árboles
En Informática, los árboles son abstracciones matemáticas que
juegan un rol central en el diseño y análisis de algoritmos, porque:
◦ Los usamos para describir propiedades dinámicas de los algoritmos
◦ Construimos estructuras de datos que son realizaciones concretas de
árboles.
El resto de los nodos se les conoce como rama, ya que tienen padre y
uno o varios hijos.
Árboles
En computación, usualmente se usa el término árbol para referirse a un
árbol con raíz. Mientras que se asume el término árbol libre para
referirse a la estructura más general.
Un recorrido es una sucesión de nodos del árbol de tal forma que entre
cada dos nodos consecutivos de la sucesión hay una relación de
parentesco.
Árboles: Definición General
Existen dos recorridos típicos para listar los nodos de un árbol: en
profundidad y en anchura.
descendiente hermanos
hoja
Árboles Binarios
Nivel de un árbol binario: La raíz del árbol tiene el nivel 0, y el nivel de
cualquier otro nodo en el árbol es uno más el nivel de su padre.
Nivel = 0
Nivel = 1
Nivel = 2
Nivel = 3
Profundidad
Null
father(p) : Retorna un info(p) : Retorna el contenido del
apuntador al padre del nodo nodo, en este ejemplo es a
p
a right(p) : Retorna un apuntador
left(p) : Retorna un apuntador
al hijo izquierdo del nodo b al hijo derecho del nodo
c
d e f g
isLeft(d) = true isLeft(e) = false isRight(g) = true
Note que si no existe un nodo que satisfaga cualquiera de las funciones anteriores, se
retorna un nulo (null) entonces.
return right(father(p))
return left(father(p))
Operaciones adicionales:
-makeTree(p) : Crea un árbol binario con un nodo único (raíz)
- setLeft(p, x) : Establece un nodo x como hijo izquierdo de otro nodo p,
siempre y cuando p no tenga un hijo del lado izquierdo ya establecido.
- setRight(p, x) : Similar a la función anterior.
Árboles Binarios: Aplicación de Ejemplo
Los árboles binarios son útiles cuando se toman decisiones en dos
sentidos en cada punto del proceso.
Ejemplo: Encontrar todos los duplicados en una lista de números:
{15,4,8, 7, 4, 3, 19, 5, 7, 9, 16, 5,17}
Algoritmo: Primer elemento es la raíz, subsecuentes elementos se
colocan a la izquierda si son menores o a la derecha si son mayores.
Si son duplicados no se insertan pero se reportan.
15 15 15 15 15
4 4 4 4 4
8 8 3 8
7 7
Árboles Binarios: Aplicación de Ejemplo
{15,4,8, 7, 4, 3, 19, 5, 7, 9, 16, 5,17}
15 15 15
4 19 4 19 4 19
3 8 3 8 3 8 16
7 7 7 9 17
…
5 5
Árboles Binarios: Aplicación de Ejemplo
Pseudocódigo:
15
int numbers[13] = {15,4,8, 7, 4, 3, 19, 5, 7, 9, 16, 5,17};
tree = makeTree(numbers[0]);
for(int i=1;i<length(numbers);i++){
4 19
p = q = tree;
while(numbers[i] !=info(p) && q!=NULL){
p = q;
if(numbers[i]<info(p)) 3 8 16
q = left(p);
else
q = right(p); 7 9 17
}
if(numbers[i] == info(p))
cout<<“Numero repetido”; 5
else if(numbers[i] < info(p))
setleft(p,numbers[i]);
else
setright(p, numbers[i]);
}
Ejemplo 2: Expresiones
La raíz del árbol binario contiene un operador que se aplicará a la
evaluación de las expresiones representadas por sus subárboles
izquierdo y derecho.
Los operandos son únicamente hojas en el árbol
+
$
A *
+ *
A+B*C B C
A * + C
B C A B
+ C
(A+B*C)$((A+B)*C)
A B (A+B)*C
Representación básica de un árbol binario
struct tnode {
int info;
struct tnode * father; //No necesario
struct tnode * left;
struct tnode * right; Info L F R
};
p
typedef struct tnode * TNODEPTR;
TNODEPTR createNode() {
TNODEPTR p = (TNODEPTR)
malloc(sizeof(struct tnode));
return p;
}
void freeNode(TNODEPTR P) {
free( p);
}
Representación básica de un árbol binario
TNODEPTR makeTree(int x) {
TNODEPTR root = createNode();
root->info = x;
root->father = NULL;
root->left = NULL; X NULL NULL NULL
root->right = NULL;
return root;
}
TNODEPTR father(TNODEPTR pNode) {
return pNode->father;
}
X N N N X N N
pNode->left=…
father
a
b c sibling(c) ?
d e f g
Árbol binario de búsqueda u ordenado
El ejemplo anterior introdujo el árbol binario de búsqueda o
árbol binario ordenado
Este tipo de árbol tiene todos sus nodos en orden, para cada
nodo X:
◦ Todos los elementos de su árbol izquierdo son menores o iguales a X,
◦ Mientras los nodos en su árbol derecho son mayores a X.
4 19
//Dado un árbol binario, inserta un
//nuevo nodo en el lugar correcto del arbol.
TNODEPTR insert(TNODEPTR pNode, int data){ 3 8 16
//1: Si el arbol esta vacio retorna
//un nodo unico
if(pNode==NULL){
return makeTree(data); 1 7 9 17
} else{
//Recursa hacia abajo del arbol
//Para encontrar el lugar correcto 5
if(data<=pNode->info){
pNode->left = insert(pNode->left, data);
}else{
pNode->right = insert(pNode->right, data);
}
//Retorna el nodo original sin cambiar
return(pNode);
}
}
Árbol binarios: Ejercicio simple
Escribe código que implemente el siguiente árbol binario:
a) Llamando a makeTree tres veces
2 y usando tres variables puntero.
TNODEPTR build123(){
TNODEPTR one, two three;
one = makeTree(1);
1 3
two = makeTree(2);
three = makeTree(3);
two->left = one;
two->right = three;
b) Llamando a makeTree tres veces return two
y usando una variable puntero. }
3 D 6 E F 9
ABDGCEHIF
4 G H I
7 8
Recorrido de árboles binarios
Orden Simétrico/Inorden:
1. Recorrer el subárbol izquierdo en orden simétrico
2. Recorrer la raíz
3. Recorrer el subárbol derecho en orden simétrico
1 D 6 E F 9
DGBAHEICF
2 G H I
5 7
Recorrido de árboles binarios
Orden Posterior:
1. Recorrer el subárbol izquierdo en orden posterior
2. Recorrer el subárbol derecho en orden posterior
3. Recorrer la raíz
2 D 6 E F 7
GDBHIEFCA
1 G H I
4 5
Remoción en un árbol binario ordenado
15 15
3 8 16 8 16
15 15
15
24
Remoción en un árbol binario ordenado
void deleteNode(TNODEPTR tree, int x) 15 15
{
TNODEPTR p = tree, q = NULL, rp;
4 19 4 19
while (p != NULL && p->info != x) {
q = p;
3 8 16 8 16
p = (x < p->info) ? p->left :
p-> right;
15 15
}
if (p == NULL) return;
4 19 8 19
if (p->left == NULL)
rp = p-> right;
8 16 16
else if (p-> right == NULL)
rp = p->left; 15
else { 4 23
19
TNODEPTR f = p;
rp = p-> right; 3 8 16 25
TNODEPTR s = rp->left;
while (s != NULL) {
18 23
24
f = rp;
rp = s; 24
s = rp->left;
Árboles Balanceados
La altura (profundidad) de un árbol binario es el nivel
máximo de sus hojas
0
1
0 1 -1
0
0 0 0
0 0 0
0 0
0 0
Por ejemplo:
P = {data=10, left=7, right=15, FE=2}
p
10
7 15
5 8
2 6
Árbol AVL: Rotaciones
7
5 10 2,5,6,7,8,10,15
2 6 8 15
10
7 15
2 7
5 8
6 10 2,5,6,7,8,10,15
Recorrido Inorder
8 15 2 6
l r l lr’
5 10 5 8
lr’ r’
2 6 8 15
2 6
Rotación izquierda
p r’
Si la inserción se produce en el hijo derecho (lr’) del 7 15
12
Rotación a Rotación a
la izquierda la derecha 9
9 15
7 12
7 10
5 8 10 15
5 8
2 6
2 6
Casos de Inserción
La inserción funciona como si fuera un árbol de búsqueda
binario desequilibrado, retrocediendo hacia la raíz y rotando
sobre cualquier nodo no balanceado.
Nuevo nodo
Nuevo nodo
Solución: Rotación Izquierda
X X X
B C D E
6 10 37 60 70 80 100 120 150
F G H
25 62 65 69 110
Árboles Multicaminos: Operaciones Básicas
numTrees(p): Dado un nodo multicamino p, retorna el número de hijos
(subárboles) de p (0<=numTrees(p)<=n). Donde n es el orden o grado del árbol.
child(p,i): Retorna el i_ésimo hijo del nodo p. Donde 0<=i<numTrees(p)-1.
key(p,j): Retorna la j_ésima llave del nodo p. Donde 0<=j<numTrees(p)-2 son las
llaves en orden ascendente
numTrees(A) => 4
A key(A,2)
key(A,0)
12 50 85
child(A,0) child(A,3)
B C D E
B C D E
60 70 80 100 120 150
6 10 37
traverse(T node){
if(node != NULL){
nt = numTrees(node); nt = 4
for(i = 0; i<nt-1; i++){ 0<=i<3
traverse(son(node,i));
key(A,0), Key(A,1), Key(A,2)
cout<<key(node, i); A
}
12 50 85
traverse(son(node,nt-1));
}
son(A,0) son(A,1) son(A,2) son(A,3)
}
B C D E
60 70 80 100 120 150
6 10 37
Árboles Multicaminos: Operaciones Básicas
Acceso secuencial directo: Accede a la siguiente llave partiendo de
otra que se le conoce su posición en el árbol. Asumimos que la llave
que conocemos se encuentra en Key(node, index)