04 curso-prope-py ed-arboles

30
Árboles Cursos Propedéuticos 2006 Programación y Estructuras de Datos Manuel Montes ([email protected]) Claudia Feregrino ([email protected])

Transcript of 04 curso-prope-py ed-arboles

Page 1: 04 curso-prope-py ed-arboles

Árboles

Cursos Propedéuticos 2006

Programación y Estructuras de Datos

Manuel Montes ([email protected])Claudia Feregrino ([email protected])

Page 2: 04 curso-prope-py ed-arboles

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)

Page 3: 04 curso-prope-py ed-arboles

¿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.

Page 4: 04 curso-prope-py ed-arboles

Ejemplo de un árbol

A

B D

G H I

C

E F

Page 5: 04 curso-prope-py ed-arboles

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)

Page 6: 04 curso-prope-py ed-arboles

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.

Page 7: 04 curso-prope-py ed-arboles

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.

Page 8: 04 curso-prope-py ed-arboles

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

Page 9: 04 curso-prope-py ed-arboles

Á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

Page 10: 04 curso-prope-py ed-arboles

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.

Page 11: 04 curso-prope-py ed-arboles

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); } }

Page 12: 04 curso-prope-py ed-arboles

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

Page 13: 04 curso-prope-py ed-arboles

Á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.

Page 14: 04 curso-prope-py ed-arboles

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

Page 15: 04 curso-prope-py ed-arboles

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

Page 16: 04 curso-prope-py ed-arboles

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

Page 17: 04 curso-prope-py ed-arboles

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.

Page 18: 04 curso-prope-py ed-arboles

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

Page 19: 04 curso-prope-py ed-arboles

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)

Page 20: 04 curso-prope-py ed-arboles

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

Page 21: 04 curso-prope-py ed-arboles

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;}

Page 22: 04 curso-prope-py ed-arboles

Á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

Page 23: 04 curso-prope-py ed-arboles

Á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.

Page 24: 04 curso-prope-py ed-arboles

¿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

Page 25: 04 curso-prope-py ed-arboles

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

Page 26: 04 curso-prope-py ed-arboles

Rotación simple

X

K1

K2

Y

Z X

K1

K2

Y Z

Z

K1

K2

Y

X

K2

K1

Y

Z

X

Page 27: 04 curso-prope-py ed-arboles

Ejemplo rotación simple

4

2 5

61 3

2

1 4

3

6

5

Page 28: 04 curso-prope-py ed-arboles

NO siempre es suficiente

6

2 8

3

1 4

2

1 6

3

4 8

¡tenemos un caso de “pata de perro”!

Page 29: 04 curso-prope-py ed-arboles

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

Page 30: 04 curso-prope-py ed-arboles

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