04 curso-prope-py ed-arboles
TRANSCRIPT
Árboles
Cursos Propedéuticos 2006
Programación y Estructuras de Datos
Manuel Montes ([email protected])Claudia Feregrino ([email protected])
Contenido de la sección
• Árbol genérico– Definición y representación
• Árboles binarios– Definición, implementación y recorridos
• Árboles binarios de Búsqueda– Definición y principales operaciones (insertar,
eliminar, buscar)
• Árboles AVL (balanceados)
¿qué es un árbol?
• Un árbol puede definirse de forma recursiva
como:
– Una colección de nodos que puede ser vacía, o que
en su defecto consiste de un nodo raíz R y un
número finito de estructuras tipo árbol T1,…,Tk,
llamados subárboles, los cuales son disjuntos y sus
respectivos nodos raíz están conectados a R.
• Por tanto, un árbol es una estructura no
secuencial.
Ejemplo de un árbol
A
B D
G H I
C
E F
Nomenclatura básica
• Todo nodo nj, exceptuando el raíz, está conectado
exclusivamente a otro nodo nk donde:
– nj es el padre de nk (e.g., B es el padre de E)
– nk es uno de los hijos de nj (e.g., E es un hijo de B)
– Nodos con el mismo padre son “hermanos”
– Nodos sin hijos son llamados “hojas”
• Si existe una trayectoria del nodo nj al nodo nk entonces:
– nj es antecesor de nk (e.g., A es antecesor de E)
– nk es descendiente de nj (e.g., E es descendiente de E)
Más nomenclatura
• La trayectoria del nodo n1 a nk se define como la
secuencia de nodos n1,n2,…,nk, tal que ni es el
padre de ni+1. Entonces:
– La longitud de una trayectoria es el número de ramas recorridas, es decir, K-1.
– Nivel o profundidad del nodo ni es la longitud de la
trayectoria que va del nodo raíz a ni.
• C tiene profundidad 1, mientras que I tiene profundidad 2.
– La altura del nodo ni es longitud de la trayectoria más
larga de ni a una hoja.
• G tiene altura 0, B altura 1 y A (raíz) altura 2.
Implementación
typedef struct { TipoDato dato; struct NodoArbol *hijo1; struct NodoArbol *hijo2; : struct NodoArbol *hijoN;} NodoArbol;
typedef struct { TipoDato dato; struct NodoArbol *hijo; struct NodoArbol *hermano;} NodoArbol;
• Dos formas de implementar:
– Tener un apuntador a cada uno
de los hijos. Problema cuando
NO sabemos el número de hijos.
– Mantener los hijos de un nodo en
una lista ligada. No hay ninguna
restricción sobre número de hijos.
• Así, un nodo del árbol consiste en
un dato, un apuntador al primer
hijo y un apuntador a la lista de
hermanos.
Ejemplo de representación
• ¿cómo lo podemos recorrer?– ¿cómo hacemos búsqueda en anchura?
– ¿cómo hacemos búsqueda en profundidad?
A
B D
G H I
C
E F
Árboles binarios
• Un árbol binario en un árbol en el cual cada nodo puede tener como máximo dos hijos.
• Recursivamente un árbol binario puede definirse como: un árbol vacío, o un nodo raíz con un subárbol izquierdo y un subárbol derecho.
Raíz
Árbolizquierdo
Árbolderecho
Implementación
typedef struct NodoArbol *Arbol;
struct NodoArbol { TipoDatol dato; struct NodoArbol *izq; struct NodoArbol *der;};
• Cada nodo del árbol consiste en:
– Un dato (cualquier cosa)
– Un apuntador al hijo izquierdo
– Un apuntador al hijo derecho
• Inicialmente el nodo raíz apunta
a NULL.
• En las hojas del árbol, los
apuntadores hacia los hijos
izquierdo y derecho son NULL.
Recorridos estándar
• Preorder: – Procesar nodo– Procesar árbol izquierdo– Procesar árbol derecho
• Inorder:– Procesar árbol izquierdo– Procesar nodo– Procesar árbol derecho
• Postorder:– Procesar árbol izquierdo– Procesar árbol derecho– Procesar nodo
void inorder(Arbol *nodo) { if (nodo != NULL) { inorder(nodo->izq); visitar(nodo); inorder(nodo->der); } }
void postorder(Arbol *nodo) { if (nodo != NULL) { postorder(nodo->izq); postorder(nodo->der); visitar(nodo); } }
Ejemplo de recorridos
Preorden: A, B, D, E, C, F, G
Inorden: D, B, E, A, F, C, G
Postorden: D, E, B, F, G, C, A
A
B C
F GD E
Árbol binario de búsqueda
• Es una árbol:– Una colección de nodos que puede ser vacía, o que en su defecto
consiste de un nodo raíz R y un número finito de estructuras tipo árbol T1,…,Tk, llamados subárboles, los cuales son disjuntos y sus
respectivos nodos raíz están conectados a R.
• Es binario:– Cada nodo puede tener como máximo dos hijos, en otras palabras,
cada nodo sólo puede tener dos subárboles.
• Es de búsqueda porqué:– Los nodos están ordenados de manera conveniente para la búsqueda.
– Todas las datos del subárbol izquierdo son menores que el dato del nodo raíz, y todas los datos del subárbol derecho son mayores.
Ejemplos
• ¿son todos árboles binarios de búsqueda?
6
2 8
3
1 4
6
2 8
3
1 4
7
2
7
1 8
6
5
4
Operación BUSCAR
boolean buscar(Arbol *nodo, int elem) { if (nodo == NULL) return FALSE; else if (nodo->dato < elem) return buscar(nodo->izq, elem); else if (nodo->dato > elem) return buscar(nodo->der,
elem); else return TRUE; }
6
2 8
3
1 4
Buscando 4: VERDADEROBuscando 7: FALSO
Operación INSERTAR
void insertar(Arbol *nodo, int elem){ if (nodo == NULL) { nodo = (Arbol *)
malloc(sizeof(Arbol)); nodo->dato = elem; nodo->izq = nodo->der = NULL; } else if (elem < nodo->dato) nodo-izq = insertar(nodo->izq,
elem); else if (elem > nodo->dato) nodo->der = insertar(nodo->der,
elem);
return nodo; }
6
2 8
3
1 4
5
Insertando 5
Operación ELIMINAR (1)
• Existen cuatro distintos escenarios:1. Intentar eliminar un nodo que no existe.
– No se hace nada, simplemente se regresa FALSE.
2. Eliminar un nodo hoja.– Caso sencillo; se borra el nodo y se actualiza el
apuntador del nodo padre a NULL.
3. Eliminar un nodo con un solo hijo.– Caso sencillo; el nodo padre del nodo a borrar se
convierte en el padre del único nodo hijo.
4. Eliminar un nodo con dos hijos.– Caso complejo, es necesario mover más de un
apuntador.
ELIMINAR (casos sencillos)
6
2 8
3
1 4
6
2 8
3
1 4
Eliminar nodo hojaEliminar 3
Eliminar nodo con un hijoEliminar 4
ELIMINAR (Caso complejo)
6
2 8
3
1 4
5
6
3 8
3
1 4
5
Eliminar nodo con dos hijosEliminar 2
eliminar
copiarvalor
• Remplazar el dato del nodo que se desea eliminar con el dato del nodo más pequeño del subárbol derecho
• Después, eliminar el nodo más pequeño del subárbol derecho (caso fácil)
Otro ejemplo (caso complejo)
6
2 8
3
1 4
5
3.5
Eliminar nodo con dos hijosEliminar 2
6
3 8
3
1 4
5
3.5
Implementación ELIMINAR
void eliminar(Arbol *nodo, int elem) { Arbol *aux, * hijo; if (nodo == NULL) return; /* no existe
nodo */ /* recorrer árbol hasta encontrar elem */ else if (elem < nodo->dato) nodo-izq = eliminar(nodo->izq, elem); else if (elem > nodo->dato) nodo->der = eliminar(nodo->der,
elem);
else /* encontramos el elemento */ /* tiene dos hijos */
if (nodo->izq && nodo->derecho){ aux = enontrar_min(nodo->der); nodo->dato = aux->dato; nodo->der = eliminar(
nodo->der; nodo->dato);
} /* un solo hijo */ else { aux = nodo; if (nodo->izq == NULL) hijo = nodo-
>der; if (nodo->der == NULL) hijo = nodo-
>izq; free(aux); return hijo; } } return nodo;}
Árboles desbalanceados
• La figura muestra un árbol binario de búsqueda, sin embargo éste NO facilita la búsqueda de elementos.
• El problema es que está muy desbalanceado.
• La solución es balancearlo, y con ello asegurar que asegurar que tiempo promedio de búsqueda sea de O(log2N).
2
7
1 8
6
5
4
Árboles AVL
• Propuestos por Adelson-Velskii and Landis.
• Árboles binarios con una condición de balance.
• Esta condición es usada para asegurar que en todo momento la altura del árbol es O(log2N).
• Condición de balance: para cada nodo del árbol, las alturas de sus subárboles izquierdo y derecho sólo pueden diferir como máximo en 1.
• La condición de balance debe mantenerse después de cada operación de inserción o eliminación.
¿son árboles AVL?
0,0
1,00,0
1,2
3,2
1,0
0,0
5
6
2 8
3
1 4
6
2 8
3
1 4 7
0,0
1,10,0
1,2
3,1
0,0
0,0
Construcción de un árbol AVL
• Cuando se inserta un nodo
se modifican las condiciones
de balance en la trayectoria
hacia la raíz.
• Si se presenta una
desbalance, entonces es
necesario hacer algunas
modificaciones al árbol.
• Dos tipos de modificaciones:
– Rotación simple
– Rotación doble
5
6
2 8
1 4
3,1
Rotación simple
X
K1
K2
Y
Z X
K1
K2
Y Z
Z
K1
K2
Y
X
K2
K1
Y
Z
X
Ejemplo rotación simple
4
2 5
61 3
2
1 4
3
6
5
NO siempre es suficiente
6
2 8
3
1 4
2
1 6
3
4 8
¡tenemos un caso de “pata de perro”!
Rotación doble
K1
K3
D
AK2
B C
K1
K3
A
DK2
B C
K1K3
DA
K2
B C
K3K1
DA
K2
B C
Ejemplo doble rotación
2 6
1 3 5
4
14
7 15
k3
k1
k2
13
2 7
1 3 6
4
14
5 1513