Ing. En Sistemas Computacionales Estructura de Datos Unidad V Estructuras no lineales estáticas y dinámicas. (Árboles y grafos) Ing. Néstor Alejandro Carrillo López Arboles Un árbol es un conjunto finito de nodos: 1. Si la colección es vacía, se dice que el árbol es vacio 2. En caso contrario, un árbol A consiste de un nodo especial llamado raíz y n (sub)arboles no vacios T1,T2,…,Tn. La raíz de A se conecta con la raíz de cada Ti por un arco dirigido. Relaciones entre nodos Todo nodo nj, exceptuando el raíz, esta conectado exclusivamente a otro nodo nk donde: • nj es el padre de nk (Ej., B es el padre de E) • nk es uno de los hijos de nj (Ej., E es un hijo de B) • Nodos con el mismo padre son “hermanos” (Ej., B , C y D) • Nodos sin hijos son llamados “hojas” (Ej., G) Árboles binarios • Un árbol binario es un árbol en el cual cada nodo puede tener como máximo dos hijos • Un árbol binario se define como: – un árbol vacio, o – un nodo raíz con un subárbol izquierdo y un subárbol derecho Representación en Memoria Hay dos formas tradicionales de representar un árbol binario en memoria: • Por medio de datos tipo punteros también conocidos como variables dinámicas o listas. • Por medio de arreglos(estáticas). Sin embargo, la más utilizada es la primera, puesto que es la más natural para tratar este tipo de estructuras. Representación en Memoria • Los nodos del árbol binario serán representados como registros que contendrán como mínimo tres campos. En un campo se almacenará la información del nodo. Los dos restantes se utilizarán para apuntar al subárbol izquierdo y derecho del subárbol en cuestión. • Cada nodo se representa gráficamente de la siguiente manera: Declaración de árbol binario • Se definirá el árbol con un dato de tipo objecto (puede ser cualquier otra tipo de datos) y dos hijos: izquierdo (izq) y derecho (der). Para representar los enlaces con los hijos se utilizan punteros. El árbol vacío se representará con un puntero nulo. • La clase nodo binario puede declarar de la siguiente manera: Class NodoB{ Object dato; NodoB izq; NodoB der; NodoB (){ this (null);} NodoB ( Object o ) { this(o,null,null);} NodoB(Object o, NodoB i, NodoB d) {dato=0; izq=i; der=d;} } Declaración de árbol binario • Se definirá el árbol con un dato de tipo objecto (puede ser cualquier otra tipo de datos) y dos hijos: izquierdo (izq) y derecho (der). Para representar los enlaces con los hijos se utilizan punteros. El árbol vacío se representará con un puntero nulo. • La clase nodo binario puede declarar de la siguiente manera: public class NodoBinario { //atributos Object dato; NodoBinario izquierdo; NodoBinario derecho; //constructores public NodoBinario() { this(null); } public NodoBinario(Object elDato) { this( elDato, null, null); } public NodoBinario(Object elDato, NodoBinario menor, NodoBinario mayor) { dato=elDato; izquierdo=menor; derecho=mayor; } Operaciones básicas en arboles • • • • • Crear un árbol vacio Verificar si el árbol esta vacio Insertar un nodo Eliminar un nodo Recorrer un árbol Recorridos de arboles • Procedimientos que visitan todos los nodos de un árbol efectuando una acción sobre cada uno. • Existen dos formas posibles de recorrer un árbol no vacio Amplitud: el proceso se realiza horizontalmente desde la raíz a todos sus hijos, luego a los hijos de sus hijos y así sucesivamente Profundidad: se sigue un camino desde la raíz a través de un hijo antes de proseguir al siguiente hijo Ejemplo de recorrido en amplitud Recorridos en profundidad • Existen tres formas posibles de recorrer en profundidad un árbol no vacio, según cuando la raíz sea visitada Recorrido Características Pre orden se visita la raíz; se recorre el subárbol izquierdo; se recorre el subárbol derecho; inorden se recorre el subárbol izquierdo; se visita la raíz; se recorre el subárbol derecho; Post-orden se recorre el subárbol izquierdo; se recorre el subárbol derecho; se visita la raíz; Recorridos en profundidad • Pre orden se visita la raíz; se recorre el subárbol izquierdo; se recorre el subárbol derecho; Recorridos en profundidad • Recorrido inorden se recorre el subárbol izquierdo; se visita la raíz; se recorre el subárbol derecho; Recorridos en profundidad • Post-orden se recorre el subárbol izquierdo; se recorre el subárbol derecho; se visita la raíz; Carga (Nodo) • • • • • • • • • • • • • • • • • • • • • • • • • 1. leer información (INFO) 2. Hacer NODO^.INFO <- INFO 3. Escribir “Existe nodo por la izquierda?” 4. Leer respuesta 5. Si respuesta es afirmativa entonces CREA (OTRO) {Crear un nuevo nodo} Hacer NODO^.IZQ<-OTRO Regresar a CARGA con NODO^.IZQ {Llamada recursiva} si no Hacer NODO^.IZQ<-Null 6. {Fin del condicional del paso 5} 7. Escribir “Existe nodo por derecha?” 8. Leer respuesta 9. Si respuesta es afirmativa entonces CREA (OTRO) {Crea un nuevo nodo} Hacer NODO^.DER <-OTRO Regresar a CARGAR con NODO^.DER {Llamada recursiva} si no Hacer NODO^.DER<- Null 10. {Fin de condicional del paso 9} Algoritmo de preorden PREORDEN(NODO) {El algoritmo realiza el recorrido preorden en un árbol binario. NODO es un dato de tipo puntero } {INFO, IZQ Y DER son campos del registro nodo. INFO es una variable de tipo carácter. IZQ y DER son variables de tipo puntero} 1. Si NODO <> Null entonces visitar el NODO {Escribir NODO^.INFO} Regresar a PREORDEN con NODO^.IZQ {Llamada recursiva a PREORDEN con la rama izquierda del nodo en cuestión} Regresar a PREORDEN con NODO^.DER {Llamada recursiva a PREORDEN con la rama derecha del nodo en cuestión} 2. {Fin de condicional del paso 1} INORDEN (NODO) {El algoritmo realiza el recorrido inorden en un árbol binario. NODO es un registro de tipo puntero} {INFO, IZQ y DER son campos del registro nodo. INFO es una variable de tipo carácter. IZQ y DER son variables de tipo puntero} 1. Si NODO <> Null entonces Regresar a INORDEN con NODO^.IZQ {Llamada recursiva a INORDEN con la rama izquierda del nodo en cuestion} Visitar el NODO {Escribir NODO^.INFO} Regresar a INORDEN con NODO^.DER {Llamada recursiva a INORDEN con la rama derecha del nodo en cuestión} 2. {Fin de condicional del paso 1} POSTORDEN(NODO) {El algoritmo realiza el recorrido postorden en un árbol binario. NODO es un dato de tipo puntero} {INFO, IZQ y DER son campos del registro nodo. INFO es una variable de tipo carácter IZQ y DER son variables de tipo puntero} 1. Si NODO<>Null entonces Regresa POSTORDEN con NODO^.IZQ {Llamada recursiva a POSTORDEN con la rama izquierda del nodo en cuestión} Regresar a POSTORDEN con NODO^.DER {Llamada recursiva a POSTORDEN con la rama derecha del nodo en cuestion} Visitar el NODO {Escribir NODO^.INFO} 2. {Fin de condicional del paso 1} Grafos • Un grafo G es un par (V,E), tal que: V: conjunto de nodos E: conjunto de arcos conectando a los nodos en V Un arco e = (u,v) es un par de nodos Grafos dirigidos y no dirigidos • Dirigido (o Digrafo) Cada linea tiene una direcciona su sucesor Note que <vi, vj> ≠ <vj, vi> • No dirigido Las lineas no tienen direccion Note <vi, vj> = <vj, vi>。 Operaciones en grafos • Considere un grafo G y dos nodos x y y -añade(G, x,y): añade en G un arco de x a y, si no existe - borra(G, x,y): suprime un arco de x a y, si existe - adyacente(G, x,y): verifica si hay un arco de x a y en G - vecinos(G, x): lista todos los nodos y tal que hay un arco entre x y y Operaciones en grafos • En los grafos que tienen valores asociados a sus arcos, también se tienen: - asigna_valor(G, x,y,v): asigna el valor asociado al arco (x,y) a v. -obtener_valor(G, x,y): regresa el valor asociado al arco (x,y).