You are on page 1of 52

UNIDAD IV Estructuras no lineales

145

Introduccin Los rboles representan las estructuras no lineales y dinmicas de datos ms importantes en computacin. Dinmicas, puesto que la estructuras rbol puede cambiar durante la ejecucin de un programa. No lineales, puesto que a cada elemento del rbol puede seguirle varios elementos. Los rboles balanceado o AVL representan la estructura da de datos mas eficiente para trabajar con la memoria principal, interna, del procesador. 4.1.- Definicin Un rbol es una estructura jerrquica aplicada sobre una coleccin de elementos u objetos llamados nodos; uno de los cuales es conocido como nodo raz. Adems se crea una relacin o parentesco entre los nodos dado lugar a trminos como padre, hijo, hermano, antecesor, sucesor, ancestro, etctera. Como forma para completar esta definicin podemos decir tambin que un rbol o estructura arborescente, es una estructura no lineal en la que cada elemento esta relacionado con dos o ms elementos. 4.1.2.- Representacin en memoria de rboles Los rboles son una estructura en el cual cada nodo hijos (ver grafico 4.1). puede tener hasta n

Los rboles pueden representarse en memoria utilizando dos tipos de datos: a).- Apuntadores (Estructura dinmicas de datos). b).- Arreglos (Estructuras estticos). La ventaja de la representacin de las estructuras dinmicas de datos es que se puede expandir o contraer en tiempo de compilacin, sin importar la cantidad de elementos al que puede tener el rbol, mientras que las estructura estticas (arreglos) permanecen fijos con la cantidad de elementos declarados. Ms adelante hablaremos ms de estas estructuras. 4.1.2.1.- rboles generales Un rbol general consta de un conjunto de nodos (unidades de informacin) en las que adems de contener su propia informacin contiene la direccin de

146 otros nodos de condiciones: menor importancia o jerrquica, y cumple las siguientes

1.- Existe un nodo raz. 2.- El resto de los nodos se distribuyen en un nmero finito de subconjuntos distintos. 3.- Cada uno de estos subconjuntos es un subrbol del rbol nodo raz. Como ejemplo podemos mencionar el rbol siguiente: Empleado Estado civil Nombre Nacimiento Fecha Calle Lugar Direccin Estudios Cursos

Nmero Ciudad Provincia Titulo

Figura 4.1.- rboles de datos de un empleado

Existe un nodo raz (empleado) y 3 subrboles: Estado civil, Direccin y Estudios. Los elementos de esta estructura como ya ha dicho se le llama nodos. Aplicaciones de los rboles. Los rboles tienen una gran variedad de aplicaciones. Por Ejemplo, se pueden utilizar para representar formulas matemticas, para organizar adecuadamente la informacin, para registrar la historia de un campeonato de tenis, para construir un rbol genealgico, para el anlisis de circuitos elctricos y para enumerar los captulos y secciones de un libro. A (B (D (I), E,F(J,K))), C(G,H(L))) b) A B E F I J K H L C G

a)

147 1.A,1.1.B,1.1.1.D,1.1.1.1.I,1.1.2.E,1.1.3.F,1.1.3.J,1.1.3.2.K,1.2.C,1.2.1.G,1.2.2. H,1.2.2.1.L c) A A B a B D b I C A E a F E F D G H a a J K C G H L d)


4.2.- Diferentes formas de representar una estructura de rbol. a).-Diagrama de Venn, b).- Anidacin parntesis, c).- Notacin decimal de Dewey, d).-Notacin indentada, e).- Grafo.

J e)

Grficamente una estructura rbol se puede representar de diferentes formas y todas ellas equivalentes. En la figura 4.2 se representan cinco diagramas correspondientes a una estructura de rbol. En la figura 4.2a se representa por medio del diagrama de Venn; en la figura 4.1b se representa por medio de anidacin de parntesis; en la figura 4.2c se representa por medio de la notacin decimal de Dewey; en la figura 4.2d por medio de la notacin indentada, y en la figura 4.2e por medio de grafos. Esta ltima representacin es la que comnmente se utiliza, y ha originado el trmino rbol por su parecido abstracto con el vegetal (ramas, raz, hojas), aunque la raz de nuestro rbol est hacia arriba. Caractersticas y propiedades de los rboles Las siguientes propiedades son las caractersticas ms importantes de los rboles generales: a).- Todo rbol que no es vaco, tiene un nico nodo. b).- Un nodo X es descendiente directo de un nodo Y, si el nodo X es apuntado por el nodo Y. En este caso es comn utilizar la expresin X es hijo de Y. c).- Un nodo X es antecesor directo de un nodo Y, si el nodo X apunta al nodo Y. En este caso es comn utilizar la expresin X es padre de Y.

148 d).- Se dice que todos los descendientes directos de un mismo nodo padre son hermanos. e).- Todo nodo que no tiene ramificaciones se conoce como nodo terminal u hoja. f).- Todo nodo que no es raz, y no es hoja ni terminal se conoce como nodo interior. g).- Grado es el numero de descendiente directos de un determinado nodo. Grado del rbol es el mximo grado de todos los nodos del rbol. i).- Nivel es el numero de arcos o aristas que debe recorrerse para llegar a un determinado nodo. Por definicin la raz tiene nivel 1. j).- Altura del rbol es el numero mximo de niveles de todos los nodos del rbol.
A

Figura 4.3 rbol general.

Dado el ejemplo de la figura 4.3 se dan los siguientes ejemplos: 1.-A es el nodo raz. 2.- B y C son hijos de A, por lo tanto B y C son hermanos. 3.- D y E son hijos de B, tambin D y E son hermanos. 4.- A es padre de B. 5.- B es padre de D. 6.- I, E, J, K, G y L son nodos terminales u hojas. 7.- B, D, F, C y H son nodos interiores. 8.- El grado del nodo A es 2. 9.- El grado del nodo B es 3. 10.-El nivel del nodo A es 1, B es 2. 4.1.2.2.- rboles binarios Un rbol ordenado es aquel en el cual las ramas de los nodos del rbol estn ordenadas. Los rboles ordenados de grado 2 son muy importantes en computacin, ya que son un tipo especial de rbol llamados comnmente como rboles binarios. Normalmente cuando hablamos de un rbol, sabemos que es una estructura en el cual cada nodo puede tener como mximo dos subrboles, los cuales son el subrbol izquierdo y el subrbol derecho.

149

Podemos definir un rbol binario como un rbol de tipo T como una estructura homognea que es la concatenacin de un elemento de tipo T, llamada raz, con dos rboles binarios disjuntos llamados subrbol izquierdo y subrbol derecho de la raz. Aplicaciones de los rboles binarios Los rboles binarios tienen mltiples aplicaciones. Se les puede utilizar para: Toma de decisiones con dos opciones de un proceso. Representar rbol genealgicos (construido en forma ascendente y donde se muestran los ancestros de un individuo dado). Representar la historia de un campeonato de tenis (construido en forma ascendente en donde existe un ganador, 2 ganadores, 4 semifinalistas y as sucesivamente). Representar expresiones algebraicas construidas con operadores binarios. 2 7 1 4 7 a) 1 1 +

4 7 3 2 5 0 5 9 7 2

b)

150
Figura 4.4.-Distintas aplicaciones de los rboles binarios. A).- rboles binarios de bsqueda. B).- Representacin de una expresin algebraica. C).- rbol genealgico.

Osvaldo Miguel Ros

Jos Miguel Rama

Maria Ros Snchez

Antonio Miguel Rito

Manuela Rama U.

Roberto Ros Lpez

Rosala Snchez P.

Figura 4.4.- (CONTINUACIN)

rboles binarios completos Podemos definir a los rboles binarios completos como un rbol en el que todos sus nodos, excepto los del ltimo nivel, tiene dos hijos: el subrbol izquierdo y el subrbol derecho. A continuacin mostramos algunos rboles completos. A ^

D a)

E A ^ B

151 b)
Figura 4.5.- rboles binarios completos. a).- De altura 3. b).- De altura 4.

Los rboles completos tienen en la altura 1, un nodo, en altura 2, tres nodos , en la altura 3, siete nodos. Un rbol completo de altura 5 tendr 31 nodos, de altura 9 tendr 511 nodos, y un rbol de altura 17 tendr 131,071 nodos. Los arboles generales tambin se pueden convertir a rboles binarios aplicando algunos mecanismos necesarios para hacerlo. Los criterios bsicos que se tienen que seguir para convertir un rbol general a un rbol binario son los siguientes: 1.- Enlazar los hijos de cada nodo en forma horizontal (los hermanos). 2.- Enlazar en forma vertical el nodo padre con el hijo que se encuentra ms a la izquierda. Adems, debe eliminarse el vnculo de ese padre con resto de sus hijos. 3.- Rotar el diagrama resultante, aproximadamente 45 grados hacia a la izquierda, as se obtendr el rbol binario correspondiente. Representacin de los rboles binarios en memoria Existen bsicamente dos tipos de formas para representar los rboles en memoria: 1. Por medio de apuntadores, esta forma es por medio de estructura dinmicas de datos. 2. Por medio de arreglos, que son las estructuras estticas de datos. Por la naturaleza de los apuntadores, es conveniente la utilizacin de las estructura dinmicas de datos para almacenar rboles, ya que proporciona la ventaja de que el rbol puede expandirse o contraerse en tiempo de compilacin, sin tener que preocuparnos por la memoria cuando se usan arreglos. Cuando almacenamos los nodos de los rboles, tenemos que pensar en que cada nodo es un objeto que consta de tres campos, un campo de informacin y dos campos punteros, estos campos punteros sirven de enlace de ese nodo con sus hijos, es decir, con los subrbol izquierdo y el subrbol derecho. Podemos mostrar en forma grfica la representacin de un nodo as: IZQ INFORMA CIN INFO DER

152 Subrbol izq


4.6.- Representacin en memoria del nodo de un rbol.

Subrbol der

IZQ: Campo donde se almacena la direccin del subrbol izquierdo del nodo. INFO: Campo donde almacena la informacin de inters del nodo. DER: Campo donde se almacena la direccin del subrbol derecho del nodo. La definicin de la clase para implementar un rbol binario es: class arbol{ private: int num; arbol *izq; arbol *der; arbol *raiz; public: arbol(){raiz=izq=der=NULL;} void insertar(int n); void eliminar(int); int buscar(int n); void inorder(); void preorder(); }; Lo que define aqu es una clase rbol que tiene como atributos privados a num (Informacin de inters del nodo), campos *izq, *der, *raiz. Con el carcter * definimos una variable dinmico en C++, esto indica que estas variables *izq, *der y *raiz almacenarn exclusivamente direcciones de memoria. Los campos izq y der apuntarn el subrbol izquierdo y subrbol derecho respectivamente y el campo raiz almacenar la raz del rbol. Vase el rbol binario de la figura 4.7a que representa la expresin (A*B) + (C/D)^3, esta misma estructura se representa en la figura 4.7b. + +

* A B /

a)

153

+ * ^

A NULL NULL

B NULL NULL C NULL NULL

/ NULL D NULL NULL

3 NULL

Figura 4.7.- Representacin de una rbol binario en memoria.

rboles Binarios de Bsqueda (ABB) Un rbol binario de bsqueda es una estructura sobre el cual se pueden realizar eficientemente las operaciones de bsqueda, insercin y eliminacin. Comparando esta estructura con otras se pueden apreciar varias ventajas. Por ejemplo, en un arreglo es posible localizar datos eficientemente si estos se encuentran ordenados, pero las operaciones de eliminacin son costosas. En las listas estas operaciones se pueden realizar con bastante facilidad, sin embargo, la operacin de bsqueda es bastante costosa, que a veces nos puede llevar a recorrer todo la lista para localizar un elemento. La ventaja de los rboles binarios de bsqueda frente a una lista enlazada es la capacidad para hacer una bsqueda. La realizacin de una bsqueda binaria sobre un rbol implica simplemente mover el puntero a la izquierda o a la derecha hasta que se encuentra el valor deseado. Un rbol binario de bsqueda es una estructura especial de datos, en cual el hijo de la izquierda, si es que existe, de cualquier nodo contiene un valor ms pequeo que el nodo padre, y el hijo de la derecha, si es que existe, contiene un valor ms grande que el nodo padre. A continuacin presentamos un ejemplo de rbol binario de bsqueda con 10 elementos:

154

Raiz 5 +

3 2 4 6

1 +

10

Figura 4.8.- Ejemplo de un rbol binario de bsqueda

En base al concepto y el ejemplo de la figura 4.8 cuando se construye un rbol binario de bsqueda, se puede notar que una vez que se ha comenzado el rbol y ha decidido qu llave debe insertar primero, no hay duda para saber donde colocar la siguiente. Las propiedades que definen un rbol binario de bsqueda dictan las relaciones entre nodos y determinan donde debe residir el siguiente nodo. La estructura de un rbol binario de bsqueda particular est determinada por el orden en el cual los nodos son colocados en el rbol. Al colocar un nuevo nodo, siempre ser insertado primero como un nodo hoja. Insercin en un rbol binario de bsqueda Para crear y mantener la informacin almacenada en un rbol binario de bsqueda, debemos tener una rutina que inserte nuevos nodos dentro del rbol. Un nuevo nodo siempre se insertar en la posicin deseada en el rbol como un nodo hoja. La figura 4.9 muestra una serie de inserciones en un rbol binario de bsqueda.

155

a).- Raiz = NULL; b).- Insertar 5 Raiz 5 e).- Insertar 3 Raiz 5 3 7 g).- Insertar 4 Raiz 5 3 4 7 9 9

c).- Insertar 9 Raiz 5 9 f).- Insertar 12 Raiz

d).- Insertar 7 Raiz 5 9 7

5 3 7 h).- Insertar 8 Raiz 1 2 5 3 4 7 8 9 1 2 9 1 2

Figura 4.9.- Inserciones en un rbol binario de bsqueda

Como se puede notar, cada nodo se inserta en el lugar que le corresponde de acuerdo al criterio de los rboles binarios de bsqueda. Cabe recordar tambin que antes de insertar un nodo en un rbol binario de bsqueda, se debe buscar el lugar adecuado de ese nodo, para ello tenemos primeramente que hacer una bsqueda en rbol, es decir, recorrer el camino del nodo que va entrar. Entendemos como camino del nodo, como el conjunto de aristas o ramas que se tiene que recorrer para llegar a ese nodo.

156

El algoritmo para la operacin de insercin sobre un rbol binario de bsqueda especifica tres tareas que deben ser realizadas: Crear un nodo para contener los nuevos datos. Encontrar el lugar de insercin. Cambiar los punteros para insertar el nuevo nodo dentro del rbol.

Crear el nodo El fragmento de cdigo para crea el montculo para un nuevo nodo que va entrar al rbol. nuevo=new arbol; nuevo->izq=NULL; nuevo->der=NULL; nuevo->num=n; Encontrar el lugar de insercin El algoritmo siguiente busca el lugar de un nodo antes de insertarlo. Encontrado = 0; ptr=raiz; anterior=NULL; while ((ptr) && (encontrado==0)) { anterior=ptr; if (ptr->num==n) encontrado=1; else if (ptr->num<n) ptr=ptr->der; else ptr=ptr->izq; } Este algoritmo realiza lo siguiente, usamos una bandera encontrado que inicializamos 0, y dos punteros anterior y ptr. Anterior es un puntero que seguir la pista de ptr. Ptr es igual a la Raiz, anterior igual a NULL, distinto de NULL, y la bandera sea cero. Anterior toma la direccin de ptr. iteramos mientras ptr sea

Comparamos el valor de los nodos con el valor que buscamos n, si el valor n es igual al valor del nodo habremos encontrado el nodo y la bandera se asigna a 1, pero si el valor del nodo es menor que el valor buscado n, entonces ptr toma direccin de su derecha, en todo caso si el valor del nodo es mayor que el valor de n buscado, ptr toma la direccin de su izquierda y

157 reinicia el ciclo mientras hasta que ptr se igual a NULL o encontrado sea igual a 1. Cuando el bucle termine ptr es igual a NULL, anterior apunta al nodo al que queremos enlazar el nuevo nodo. Insertar un nodo cuando ya existen elementos en el rbol. Cuando ya existen elementos en el rbol, raiz apuntar a una direccin vlida. if (raiz) { //busca primero lugar //enlaza el nuevo nodo con el nodo que apunta anterior if (anterior->num<=n) anterior->der=nuevo; else anterior->izq=nuevo; } Si anterior ->num es menor que n, entonces el nuevo nodo se enlaza a la derecha de anterior, en caso contrario se enlaza a la izquierda. Insertar el primer nodo del rbol. Cuando el rbol est vaco, raiz apunta a NULL, si esto ocurre, raiz toma automticamente la direccin del nuevo nodo. El algoritmo para insertar el primer nodo del rbol es: If (raiz==NULL) raiz = nuevo; La funcin para insertar un nuevo nodo en un rbol binario de bsqueda es la siguiente: arbol *nuevo; arbol *ptr,*anterior; nuevo=new arbol; nuevo->izq=NULL; nuevo->der=NULL; nuevo->num=n; if (raiz) { ptr=raiz; anterior=NULL; while (ptr!=NULL) { anterior = ptr; if (ptr->num<=n)

158 ptr=ptr->der; else ptr=ptr->izq; } if (anterior->num<=n) anterior->der=nuevo; else anterior->izq=nuevo; } else { raiz=nuevo; } Supresin de nodos en un rbol binario de bsqueda Adems de la insercin de un nuevo nodo, otra operacin necesaria para mantener un rbol binario de bsqueda es la operacin de supresin de un nodo especfico del rbol. Las especificaciones para nuestra operacin de supresin toman como entrada la raiz del rbol y una clave a buscar. Encuentra y suprime el nodo que tiene esa clave del rbol. Estas especificaciones siguieren una operacin en dos partes: Encontrar el nodo que contiene el valor a suprimir. Borrar el nodo del rbol.

Habiendo desarrollado ya la operacin de bsqueda, estamos familiarizados con lo que se necesita para encontrar una clave dada en el rbol binario de bsqueda. La segunda parte de la operacin es suprimir el nodo. Esta operacin varia dependiendo de la posicin del nodo en el rbol. Obviamente, es ms simple suprimir un nodo hoja que suprimir la raiz del rbol. Para poder realizar de una manera efectiva esta operacin es importante dividirla en varias partes: Supresin de una hoja (sin hijos). Supresin de un nodo con un solo hijo. Supresin de un nodo con dos hijos. Supresin de una hoja (sin hijos). La supresin de un nodo hoja, es simplemente cuestin de colocar el enlace adecuado de su padre a NULL, y deshacerse de nodo innecesario. Un fragmento de cdigo que realiza esto es: //Caso de Supresin de una hoja if ((ptr->der==NULL) && (ptr->izq==NULL))

159 if (anterior==NULL) raiz=NULL; else if (anterior->der==ptr) anterior->der=NULL; else anterior->izq=NULL; La eliminacin de un nodo hoja es relativamente sencilla. Supresin de un nodo con un solo hijo. Para eliminar un nodo con un hijo, se debe primero determinar cual de los hijos tiene dicho nodo. El siguiente algoritmo elimina un nodo con un hijo derecho como un nodo con un hijo izquierdo. if (ptr->der!=NULL) //Hay un hijo derecho if (anterior==NULL) raiz=ptr->der; else if (anterior->der==ptr) anterior->der=ptr->der; else anterior->izq=ptr->der; else //hay un hijo izquierdo if (anterior==NULL) raiz=ptr->izq; else if (anterior->der==ptr) anterior->der=ptr->izq; else anterior->izq=ptr->izq; La eliminacin de un nodo con un hijo izquierdo o derecho, si es que no es la raiz, anterior ->izq o anterior ->der toma la direccin del derecho o la izquierdo de ptr. Supresin de un nodo con dos hijos Este caso es el ms complicado, ya que no podemos hacer que el padre del nodo suprimido apunte a ambos hijos del nodo suprimido. Existen varias formas de realizar esta supresin. Uno de los mtodos ms frecuentes es reemplazar el nodo que deseamos suprimir con el nodo de valor ms prximo al valor del nodo suprimido. Este nodo puede proceder del lado izquierdo o del lado derecho. En este ejemplo utilizaremos este mtodo el nodo a suprimir ser reemplazado por el valor ms prximo o ms cercano del subrbol izquierdo. Este valor procede del nodo ms prximo del nodo que estamos suprimiendo. Para hacerlo, nos movemos a la izquierda una vez y luego un tanto a la derecha. Si el hijo izquierdo del nodo que queremos

160 suprimir no tiene hijo derecho, entonces el hijo izquierdo reemplazo, despus eliminamos el nodo hijo. 2 6 1 4 5 6 8 7 8 8 5 7 8 6 8 1 2 6 7 4 9 8 9 se usa como

3 4 8

Suprimir el nodo que contiene 7.

Figura 4.10.- Supresin de un nodo con dos hijos

En la siguiente figura mostramos una serie de supresiones de todo tipo. rbol inicial 2 6 1 4 5 6 8 6 7 8 8 5 7 8 1 Suprimir 5 2 6 7 4 9 8 9

3 4 8 Suprimir

3 Suprimir 8 2

4 2 6

1 3 5 6 8

6 7 8 8

6 7 3 9 8

9
Figura 4.11.- Supresiones en un rbol binario de bsqueda

161

El algoritmo para eliminar un nodo con hijos es el siguiente: if ((ptr->der!=NULL) && (ptr->izq!=NULL)) { //Busca el valor para remplazar. //Encontrar el nodo que contiene el valor //mas prximo menor que el valor que se //va a suprimir anterior = ptr; temp=ptr->izq; while (temp->der!=NULL) { anterior = temp; temp = temp->der; } //Copiar la informacin a reemplazar en //cuyo valor se va suprimir ptr->num = temp->num; //Suprimir el nodo desde el que se //copio la informacin if (anterior==ptr) anterior->izq=temp->izq; else anterior->der=temp->izq; //ptr toma valor de temp para ser eliminado ptr = temp; } 4.1.3 Recorridos en un rbol binario. Un proceso para desplazarse a travs de un rbol en forma tal que, cada nodo se visitado una y slo una vez se llama recorrido de rbol. Por lo tanto podemos que una de las operaciones ms importantes que se realizan sobre los rboles son precisamente los recorridos, esto es visitar cada uno de los nodos del rbol. Recorrer significa visitar los nodos del rbol en forma sistemtica, de tal manera que todos los nodos del mismo sean visitados una sola vez como ya lo dijimos anteriormente. Para recorrer una lista enlazada, ponemos un puntero al inicio de la lista y luego seguimos los enlaces desde un nodo a otro hasta alcanzar que el

162 puntero sea NULL. Para recorrer un rbol binario de bsqueda, iniciamos nuestro puntero a la raiz del rbol, y luego seguimos al subrbol izquierdo o derecho segn el tipo de recorrido que sea. Existen dos tipos de recorridos de los nodos de un rbol: el recorrido en anchura y el recorrido en profundidad. El recorrido en anchura se visita los nodos por niveles. Para ello se utiliza una estructura auxiliar tipo cola en la que despus de mostrar el contenido de un nodo, empezando por el nodo raiz, se almacena los punteros correspondientes a sus hijos izquierdo y derecho. De esta forma si recorremos los nodos de un nivel, mientras mostramos su contenido, almacenando en la cola los punteros a todos los nodos del nivel siguiente. Otro de los recorridos tambin importante es el recorrido en profundidad, este realiza en cualquiera de las tres formas bsicas de recorridos de rboles, todos ellas de naturaleza recursiva, pero tambin se pueden realizar con arreglos, estas son: preorden, inorden y posorden. El recorrido en preorden visita cada nodo antes que sus subrbol izquierdo y derecho. El recorrido inorden se llama as porque su nodo es visitado entre sus subrbol izquierdo y derecho. El recorrido postorden visita cada nodo despus de sus subrbol izquierdo y derecho 4.1.3.1 Preorden. Las operaciones importantes que se realizan en el recorrido preorden son: Visitar la raz. Recorrer el subrbol izquierdo en preorden. Recorrer el subrbol derecho en preorden.

4.1.3.2 Inorden. En este tipo de recorrido se realizan las operaciones bsicas siguientes: Recorrer subrbol izquierdo en inorden Visitar raiz. Recorrer subrbol derecho en inorden.

4.1.3.3 Postorden. En este ltimo tipo de recorrido se realizan las siguientes operaciones: Recorrer subrbol izquierdo en postorden. Recorrer subrbol derecho en postorden. Visitar raiz.

A continuacin mostramos el orden en que quedan los nodos de un rbol en los recorridos antes mencionados:

163

5 +

3 2 4 6

1 +

10

Figura 4.12.- Supresiones en un rbol binario de bsqueda.

Preorden : 5, 3, 2, 1, 4, 8, 6, 7, 9,10. Inorden: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10. Posorden: 1,2,4,3,7,6,10,9,8,5. Estos recorridos tienen orden establecidos en base al orden en que son visitados los nodos del rbol. Como lo hemos mencionado en el recorrido en inorden cada nodo es visitados entre sus subrbol izquierdo y derecho, mientras que el recorrido preorden el nodo es visitado antes que sus subrboles izquierdo y derecho, as mismo en el recorrido prosorden, el nodo es visitado despus de sus subrboles izquierdo y derecho. Cuando hablamos de las pilas mencionamos de los tipos de notacin de una expresin algebraica.. Un recorrido preorden produce una notacin prefija, un recorrido inorden una notacin infija y un recorrido posorden produce una notacin posfija. Las tcnicas de recorridos son importantes y tienen aplicaciones prcticas. La tcnica ms adecuada para una situacin particular est determinada por la manera en la cual la informacin se haya estructurado en el rbol. Programando los recorridos de rboles Para realizar en programacin estos recorridos podemos usar una de dos formas posibles: con apuntadores o con arreglos. La limitancia de arreglos en los recorridos es que si el rbol es extremadamente grande, se tiene que declarar un arreglo de igual tamao y, esto consume demasiando espacio en memoria. Usando apuntadores, puede que sea una decisin adecuada, pero si esto se usa con recursividad podemos tambin ocupar en exceso la memoria por las llamadas sucesivas a la funcin.

164

Recorridos de un rbol binario de bsqueda no recursivo usando arreglos y apuntadores. Los algoritmos no recursivos que usan explcitamente apilamiento y desapilamiento para recorrer un rbol binario de bsqueda son ms eficientes que los recursivos. Recorrido en Preorden Un algoritmo en C++ para recorrer en preorden un rbol binario de bsqueda usando pilas se escribe a continuacin: arbol *ptr; //ptr puntero a un objeto de tipo de rbol int n=-1,recorrido=1; arbol *pila=new arbol[20]; //Pila es un arreglo de 20 objetos de tipo de rbol if (raiz) { ptr=raiz; do { while (ptr!=NULL) { cout <<ptr->num<<" "; n++; pila[n].raiz=ptr; ptr=ptr->izq; } if (n>=0) { ptr=pila[n].raiz; n--; ptr=ptr->der; } else recorrido=0; }while (recorrido); delete puntero; } else { cout<<"No hay elementos en el arbol"; } En este algoritmo cada nodo es un objeto de tipo rbol, en el recorrido en preorden el nodo es visitado, posteriormente se mete en una pila mientras exista subrbol izquierdo, cuando ya no exista subrbol izquierdo, se saca de la pila el nodo para retornar al nivel anterior y avanzar al subrbol derecho, si existe subrbol izquierdo de ese nodo se visita y se mete el nodo en la pila y se recorre el subrbol izquierdo hasta no encontrar subrbol

165 izquierdo, si no existe subrbol derecho se saca el nodo de la pila y se regresa al nivel anterior, si no existe subrbol derecho se saca nuevamente el siguiente nodo de la pila, y as sucesivamente hasta terminar. El algoritmo anterior usa un arreglo para alojar el nodo temporalmente para poder regresar cuando ya no existe subrbol izquierdo y ni derecho de cada nodo. Recorrido en inorden En un recorrido inorden sucede lo mismo que en ejemplo anterior pero en diferente orden. En el ejemplo anterior, el nodo es visitado, despus se mete a la pila para seguir al subrbol izquierdo, en este caso sucede como lo marca el recorrido inorden. Primero, se recorre el subrbol izquierdo y se va metiendo los nodos a la pila mientras exista subrbol izquierdo, cuando se llega al final del subrbol izquierdo, se saca el nodo de la pila y se visita, esto es, se imprime ese nodo. Segundo, despus de imprimir, se va al subrbol derecho, si existe subrbol izquierdo de ese nodo, se guarda nuevamente en la pila el nodo y se va al paso uno. Tercero, si no existe subrbol derecho, se saca el nodo de pila para regresar al nivel anterior. El siguiente algoritmo realiza este recorrido de manera eficiente: arbol *ptr; int n=-1,recorrido=1; arbol *puntero=new arbol[50]; if (raiz) { ptr=raiz; do { while (ptr!=NULL) { n++; pila[n].raiz=ptr; ptr=ptr->izq; } if (n>=0) { ptr=pila[n].raiz; n--; cout <<ptr->num<<" "; ptr=ptr->der; } else recorrido=0; }while (recorrido);

166 delete puntero; } else cout<<"No hay elementos en el rbol"; Para recorrer un subrbol en posorden, solo hay que realizar ciertas modificaciones al algoritmo inorden. Se le deja al lector su construccin. A continuacin presentamos el programa insertar, eliminar y recorrer un rbol binario de bsqueda: #include <iostream.h> #include <conio.h> #include <stdlib.h> class arbol{ private: int num; arbol *izq; arbol *der; arbol *raiz; public: arbol(){raiz=izq=der=NULL;} void insertar(int n); void eliminar(int); int buscar(int n); void inorder(); void preorder(); }; void arbol::eliminar(int n) { arbol *ptr,*anterior,*temp; if (raiz) { ptr=raiz; anterior=NULL; while ((ptr->num!=n) && (ptr!=NULL)) { anterior=ptr; if (ptr->num<n) ptr=ptr->der; else ptr=ptr->izq; } if (ptr->num==n) { //Caso de Supresin de una hoja if ((ptr->der==NULL) && (ptr->izq==NULL)) if (anterior==NULL) raiz=NULL; else if (anterior->der==ptr) anterior->der=NULL;

167 else anterior->izq=NULL; else //Caso de supresion de un nodo con dos hijos if ((ptr->der!=NULL) && (ptr->izq!=NULL)) { //Busca el valor para remplazar. //Encontrar el nodo que contiene el valor //mas prximo menor que el valor que se //va a suprimir anterior=ptr; temp=ptr->izq; while (temp->der!=NULL) { anterior = temp; temp = temp->der; } //Copiar la informacion a reemplazar en //cuyo valor se va suprimir ptr->num=temp->num; //Suprimir el nodo desde el que se //copio la informacin if (anterior==ptr) anterior->izq=temp->izq; else anterior->der=temp->izq; //ptr toma valor de temp para ser eliminado ptr=temp; } else if (ptr->der!=NULL) //Hay un hijo derecho if (anterior==NULL) raiz=ptr->der; else if (anterior->der==ptr) anterior->der=ptr->der; else anterior->izq=ptr->der; else //hay un hijo izquierdo if (anterior==NULL) raiz=ptr->izq; else if (anterior->der==ptr) anterior->der=ptr->izq; else anterior->izq=ptr->izq; //suprime nodo innecesario

168 delete ptr; cout <<"Eliminado"; getch(); } else { cout <<"No encontr el elemento"; getch(); } } else { cout <<"arbol vacio"; getch(); } } void arbol::insertar(int n) { arbol *nuevo; arbol *ptr,*antes; nuevo=new arbol; nuevo->izq=NULL; nuevo->der=NULL; nuevo->num=n; if (raiz) { ptr=raiz; antes=NULL; while (ptr!=NULL) { antes=ptr; if (ptr->num<=n) ptr=ptr->der; else ptr=ptr->izq; } if (antes->num<=n) antes->der=nuevo; else antes->izq=nuevo; } else { raiz=nuevo; } } void arbol::preorder() { arbol *ptr; int n=-1,recorrido=1; arbol *pila=new arbol[20];

169 if (raiz) { ptr=raiz; do { while (ptr!=NULL) { n++; pila[n].raiz=ptr; cout <<ptr->num<<" "; ptr=ptr->izq; } if (n>=0) { ptr=pila[n].raiz; n--; ptr=ptr->der; } else recorrido=0; }while (recorrido); delete puntero; getch(); } else { cout<<"No hay elementos en el arbol"; getch(); } } void arbol::inorder() { arbol *ptr; int n=-1,recorrido=1; arbol *puntero=new arbol[50]; if (raiz) { ptr=raiz; do { while (ptr!=NULL) { n++; pila[n].raiz=ptr; ptr=ptr->izq; } if (n>=0) { ptr=pila[n].raiz; n--; cout <<ptr->num<<" "; ptr=ptr->der;

170 } else recorrido=0; }while (recorrido); delete puntero; getch(); } else { cout<<"No hay elementos en el arbol"; getch(); } } int arbol::buscar(int n) { arbol *ptr; int encontrado=0; if (raiz) { ptr=raiz; while ((ptr!=NULL) && (encontrado==0)) { if (ptr->num==n) encontrado=1; else if (ptr->num<n) ptr=ptr->der; else ptr=ptr->izq; } return encontrado; } else return encontrado; } void main() { int numero; arbol a; arbol *p; p=&a; char op; do{ clrscr(); cout <<"1.- Insertar\n"; cout <<"2.- Eliminar\n"; cout <<"3.- Consultar\n"; cout <<"4.- Recorrido inorder\n"; cout <<"5.- Recorrido preorder\n"; cout <<"6.- Terminar\n"; cout <<"Seleccione su opcin\n"; op=getch(); switch(op)

171 { case '1':{ clrscr(); cout<<"Insertar elementos"; cout <<"Deme el numero a insertar "; cin>>numero; p=&a; p->insertar(numero); } break; case '2':{ clrscr(); cout <<" Deme el numero a eliminar"; cin>>numero; a.eliminar(numero); } break; case '3':{ clrscr(); cout <<"Deme el numero a buscar "; cin>>numero; p=&a; if (a.buscar(numero)>0) cout <<"el numero "<<numero<<" si esta en el arbol"; else cout <<"el numero "<<numero<<" no esta en el arbol"; getch(); } break; case '4':{ p=&a; p->inorder(); } break; case '5':{ p=&a; p->preorder(); } break; } }while (op!='6');

4.1.4 Balanceo de rboles binarios. Los rboles binarios tienen una estructura que es eficiente para realizar operaciones de bsqueda, inserciones y eliminaciones. Pero cuando insertamos elementos ordenados de menor a mayor o de mayor a menor, se

172 produce un problema de encadenamiento hacia la derecha abajo o hacia la izquierda abajo, como lo muestra el grafico siguiente:
Raiz Raiz

2 3 4 5 1 + 5 7 7 5 + 6 b).

a)

Figura 4.13.-Encadenamiento hacia abajo a).-Derecha b).- Izquierda

Cuando esto sucede, el rbol puede crecer de una manera descontrolado y decrece la eficiencia y el rendimiento de las operaciones sobre el rbol. Si la cantidad de comparaciones sobre el rbol es de N/2, es sinnimo de rendimiento muy pobre, por lo tanto se tiene que aplicar sobre l reacomodos de tal forma de que el nmero de comparaciones sea menor de N/2. Con el objeto de mejorar el rendimiento de las bsquedas sobre los rboles binarios de bsqueda surgen los rboles balanceados (AVL). La idea fundamental del balanceo es realizar reacomodos despus de cada insercin o eliminacin. Una rbol AVL es un rbol binario de bsqueda que trata de mantenerse lo ms balanceado posible, conforme se realizan operaciones de insercin y eliminacin. Estos tipos de rboles fueron propuestos en 1962 por los matemticos rusos Adelson - Velskii y Landis de donde surge su nombre. Su contribucin principal consisti en presentar algoritmos eficientes de insercin y eliminacin considerando un balanceo en el rbol que a su vez, repercute en la eficiencia de las bsquedas. En un rbol binario balanceado es un rbol binario de bsqueda en que cada nodo, en sus subrbol izquierdo y derecho no debe diferir en una unidad. Factor de balanceo Para poder determinar si un rbol est balanceado o no debe manejarse informacin relativa al equilibrio de cada nodo del rbol, surge as el concepto de factor de equilibrio de un nodo (fe) que se define como la altura del rbol subrbol derecho menos la altura del subrbol izquierdo. FE=Hsd Hsi

173 Los nodos de un rbol AVL guardan un valor entre 1 y -1, lo que se conoce como factor de balanceo (FB), y representa la diferencia entre las alturas de sus subrboles. Un FB igual a cero, en un nodo, significa que la alturas de sus subrboles son iguales; un FB positivo significa que el subrbol derecho es ms grande que el izquierdo, y FB negativo significa que el subrbol izquierdo es ms grande que el derecho. Si FE llegara a tomar los valores de -2 o 2 entonces debera reestructurar el rbol. En la figura siguiente se muestra un rbol con el correspondiente FE para cada nodo del rbol. -1 1 0 2 0 5 Observe que la altura del nodo 7 es -1 puesto que la altura del subrbol derecho es igual a 2, y la altura del subrbol izquierdo es 3. El del nodo 3 es igual a 1 puesto que altura del subrbol derecho es 2 y el de la izquierda es 1. Una clase para implementar un rbol balanceado es:
public class arbolAvl { arbolAvl izq, der; int info; int fe ; public arbolAvl() { } }

7 -1 9 -1

3 4 -1 6 8 0

Reestructuracin del rbol balanceado. Para insertar un elemento en el rbol, primero es debe seguirse el camino de bsqueda del rbol, hasta localizar el lugar donde hay que insertar el nodo. Luego se calcula su FE, que obviamente es 0, y regresamos por el camino de bsqueda calculando el FE de los distintos nodos. Si el alguno de los nodos se viola el criterio de equilibrio entonces debe reestructurarse el rbol. El proceso termina al llegar a la raiz del rbol o cuando se realiza la reestructuracin del mismo, o cuyo caso es necesario determinar el FE de los nodos restantes. Reestructurar el rbol significa rotar los nodos del mismo. La rotacin puede ser simple o compuesta. El primero caso involucra a dos nodos y en el segundo involucra a 3. Si la rotacin es simple puede realizar por las ramas derechas (DD), por las ramas izquierdas (II). Si la rotacin es compuesta

174 puede realizarse por las ramas derechas e izquierda (DI) o por las ramas izquierda y derecha (ID). DD Simple Rotacin Compuesta ID En la siguiente figura se muestran los distintos tipos de rotacin. Las lneas continuas delgadas muestran el estado de los nodos antes de realizar la insercin. Las lneas discontinuas (- - - - -) indican el nuevo elemento insertado. Las lneas (. . . . ) indican el camino de regreso hasta que se detecta el equilibrio del rbol. Las lneas continuas gruesas indican el movimiento de los nodos en la rotacin. I I 2 3 4 -1 Rotacin II 7 -2 2 0 3 4 0 7 0 II DI

2 2 1

D 3 0 0 2 Rotacin DD

0 3 9 D 7 7 0

7 2 7 -1 0 D 9 I 8 Rotacin DI 0 7 0 8 9 0

175

0 I 7 D 8 Ejemplo de la rotacin II Se insertan los elementos 8, 6, 4 , el rbol queda de la siguiente manera: ptr anterior 4 0 6 4 -1 8 -2 0 Rotacin ID 9 1 0 2 5

Al regresar por el camino del nodo calculando el factor de equilibrio, se detecta que en la clave 8 se viola el criterio de equilibrio, por lo tanto hay que reestructurarlo. Para ello se apunta con ptr al nodo 8 y con anterior la rama izquierda de dicha clave, se procede realizar el siguiente cdigo: ptr.izq=anterior.der; anterior.der=ptr; ptr=anterior; Se calcula el factor de equilibrio para obtener el siguiente rbol: ptr.fe=0; 6 0 anterior.fe=0; 4 4 0 8 0

Ejemplo de la rotacin DD: Se insertan las claves 9,11, el rbol se muestra as: 6 0 2 0 ptr
9

176 1
11

anterior 0

Al regresar por el camino del rbol se detecta la violacin al criterio de equilibrio y debe reestructurarse. Para ello se apunta con ptr al nodo 8 y con anterior a su nodo derecho y se procede con el siguiente cdigo: ptr.der=anterior.izq; anterior.izq=ptr; ptr=anterior; Y se calcula el factor de equilibrio de cada uno de los nodos del rbol: ptr.fe=0; anterior.fe=0; 6 1 4 0 8 0
9

0
11

Ejemplo de la Rotacin ID Se insertan los elementos 2, 3, el rbol se muestra as: 6 -2 ptr 1 anterior 2 3 0 8 0 1
9

0
11

0 temp Al regresar por el camino del rbol se detecta la violacin al criterio de equilibrio en el nodo 4 y debe reestructurarse. Para ello se apunta con ptr al nodo 4 y con anterior a su nodo izquierdo, y temp apunta al nodo derecho de anterior y se procede con el siguiente cdigo: anterior.der=temp.izq; temp.izq=anterior; ptr.izq=temp.der; temp.der=ptr; ptr=temp; 0 0 0 2 0 6 0 4 0 8

9 11

Este rbol es resultado de la aplicacin de la rotacin anterior.

177 Se calcula el factor de equilibrio de cada uno de los nodos ptr.fe=0; anterior.fe=0; temp.fe=0; Ejemplo de la rotacin DI: Se insertan los elementos 13 y 12, al rbol se muestra de la siguiente forma: 0 0 0 2 0 6 0 4 0 8

9 11

ptr 2
13

anterior Rotacin DI
12

-1

0 temp Al regresar por el camino del rbol se detecta la violacin al criterio de equilibrio en el nodo 11 y debe reestructurarse. Para ello se apunta con ptr al nodo 11 y con anterior a su nodo derecho, y temp apunta al nodo izquierdo de anterior y se procede con el siguiente cdigo: anteior.izq=temp.der; temp.der=anterior; ptr.der=temp.izq; temp.izq=ptr; ptr=temp; 0 1

3 2 0 0

0 4 8 0

1 1
12

0 0

11

13

Este rbol es resultado de la aplicacin de la rotacin anterior. Se calcula el factor de equilibrio de cada uno de los nodos ptr.fe=0; anterior.fe=0; temp.fe=0;
El algoritmo de insercin en um arbol balanceado es el siguinete: Insertarbalanceado(nodo, bo, infor) { if (nodo!=null) { if (info.<nodo.infor) {

178
insertarbalanceado(nodo.izq,bo,infor); if (bo==true) { switch(nodo.fe) { case 1: nodo.fe=0; bo=false; break; case 0: case -1: nodo.fe=-1; break; nodo1=nodo.izq; break;

} if (nodo.fe==0) //RII { nodo.izq=nodo1.der; nodo1.der=nodo; nodo.fe=0; nodo=nodo1; } else { nodo2=nodo1.der; nodo.izq=nodo2.der; nodo2.der=nodo; nodo1.der=nodo2.izq; nodo2.izq=nodo1; } if (nodo2.fe== -1) nodo.fe=1; else nodo.fe=0; if (nodo2.fe== 1) nodo1.fe== -1; else nodo1.fe=0; nodo=nodo2; nodo.fe=0; bo=false; } else {

if (info.>nodo.info) { insertarbalanceado(nodo.der.bo,infor); if (bo==true) { switch(nodo.fe) { case -1: nodo.fe=0; bo=false; break; case 0: case 1: nodo.fe=1; break; nodo1=nodo.der; break;

179
} if (nodo.fe==0) //RDD { nodo.der=nodo1.izq; nodo1.izq=nodo; nodo.fe=0; nodo=nodo1; } else { //RDI nodo2=nodo1.izq; nodo.der=nodo2.izq; nodo2.izq=nodo; nodo1.izq=nodo2.der; nodo2.der=nodo1; } if (nodo2.fe== 1) nodo.fe= -1; else nodo.fe=0; if (nodo2.fe== -1) nodo1.fe== 1; else nodo1.fe=0; nodo=nodo2; } nodo.fe=0; bo=false;

} else { } } else {

Escribir El dato ya est capturado;

crearnodo(); nodo.info=info.; nodo.izq=null; nodo.der=null; nodo.fe=0; bo=true;

4.1.5 Clases para la implementacin de rboles.


public class arbolAvl { arbolAvl izq, der; int info,infor; int fe ; public arbolAvl() { } public void insertarbalanceado(arbolAvl nodo,boolean bo, int infor) {

180
//codigo para inserter } public void eliminarbalanceado() { //codigo } }

4.2 Grafos. En los puntos anteriores se estudiaron tipos de rboles en las que cada nodo o elemento puede tener como mximo un nodo precedente o raiz. Sin embargo , existen situaciones en las que la informacin a representar no responden a la estructura de la informacin de este tipo, sino que necesita mayor flexibilidad para otro tipo de relaciones entre los datos componentes de la informacin. Por ello en este apartado se pretende dar un panorama sobre las graficas, las cuales son estructuras de datos que permiten representar diferentes tipos de relaciones entre los datos. 4.2.1 Definicin. Un grafo est formado por un conjunto de nodos (llamados tambin vrtices) y un conjunto de lneas llamados tambin aristas (o arcos) que conectan los diferentes nodos. El conjunto de nodos s especifican en una notacin conjunto (entre parntesis) y el conjunto de aristas se especifican listando una frecuencia de aristas. Una grafo se define como: G=(V,A) donde v(g)= es un conjunto finito de vrtices. A(G)= es un conjunto de aristas(pares de vrtices). 4.2.2 Tipos de grafos. Grafos dirigidos: Un grafo es dirigido cuando la direccin de la lnea es hincada por el nodo que se lista primero, dicho de otra manera, cada arista del grafo incluye una flecha para indicar la direccin. Ejemplo de grafos dirigidos:

A G1
S

J C M Q

181
G2 V(G2)=(A,B,C) A(2)=(A,B),(B,A),(A,C),(C,A),(B,C)

El grado interno de un nodo en un grafo es el nmero de aristas que terminan de ese nodo. El grado externo de un nodo es el nmero de aristas que salen de ese nodo. El grado de un nodo es la suma de sus grados internos y externos. Por ejemplo:
Grado interno(A)=2 Grado externo(A)=2 Grado (A)= 4 O Grado interno(B)=1 Grado externo(B)=2 J Grado (B)= 3 Grado interno(C)=2 Grado externo(C)=1 C M Grado (C)= 3

A G1
S Q U

G2 B C

-Grafos no dirigidos: Un grafo no dirigido es cuando la relacin entre los nodos es desordenada, es decir, ellos simplemente estn conectados. G3 V(G3)=(A,B,C,D,) A(G3)=(A,B),(B,C),(B,D),(A,D)

Un grafo completo, es aquel en el que todos sus nodos se conectan a todos los dems nodos. Por ejemplo: A B G4 Grafo dirigido completo C A D

G5 Grafo no dirigido completo

182

Si existe n nodos, existirn n(n-1) aristas en el grafo dirigido completo y (n(n1)1)/2) aristas en un grafo no dirigido completo. Par el Grado G4 tendremos (4(4-1) = 12 aristas. Ahora para el grafo G5 tendremos (5(5-1)/2)= 10 aristas. Un grafo con peso, es un grafo en el que cada arista transporta un valor. Este tipo de grafo es importante sobre todo en la situacin en las que el valor del peso es utilizado. Una aplicacin de este tipo de grafo es el siguiente:

SONORA

MONTERR EY

14.50
Guadalajara

17.30 11.20

13-50 4 15
PUEBLA

D.F

15
YUCATAN

En este ejemplo, las vrtices representan ciudades y las aristas indican las rutas de autobuses que conectan las ciudades. Los pesos aadidos a los aristas representan el precio del servicio en autobuses entre pares de ciudades. Para ir de D.F. a Yucatn, buscamos un camino entre ellas. Puede existir mltiple caminos entre dos ciudades. El costo de un viaje es la suma de los precios entre cada par de ciudades a lo largo del camino. 4.2.3 Representacin de grafos en memoria. Un grafo puede estar representado por una matriz de adyacencia o una lista adyacencia. Si hay n nodos un grafo, la matriz de adyacencia ser una tabla con n filas y n columnas. El valor en la posicin [i][j] en la tabla es generalmente 1 si (vi,vj) es una arista de A, y 0 en otro caso. Si el grafo es una grafo con peso, la celda (i,j) puede contener el peso sobre esta arista, si la arista esta en A y 0, si no. Las listas de adyacencia son listas enlazadas, una para cada nodo, conteniendo los nombres de los nodos, a los que el nodo est conectado. Los encabezados cada una de estas listas, se tienen

183 en un arreglo de punteros. A continuacin se tiene en la matriz G2 representado como una matriz de adyacencia y como lista de adyacencia.

A O B J C M Q S D U G E F C

O J S C M Q U

O 0 0 0 0 0 0 0

J 1 0 0 0 0 0 0

G2 S 1 0 0 0 0 0 0

G6 C 0 1 0 0 0 0 0 M 0 1 0 0 0 0 0 Q 0 0 1 0 0 0 0 U 0 0 1 0 0 0 0 Tabla de adyacencia del grafo g2.

REPRESENTACIN DE DIRECTORIOS DE NODOS COMO LISTAS DE ADYACENCIAS: La representacin de directorios de nodos incluye dos partes: un directorio y un conjunto de listas ligadas. Hay una entrada en el directorio para cada nodo del grafo. La entrada del directorio para el nodo i apunta a una lista ligada, que representa los nodos que estn conectados al nodo i. Cada registro de la lista ligada tiene dos campos: el primero es un identificador del nodo y el otro es una lista al siguiente nodo de la lista. El Directorio representa nodos y la lista ligada representa aristas. Ahora representamos las listas de adyacencias de los grafos G2 y G6. DIR INFORMACION
O J S C M Q U J C
Q

S M U

Grafo G6

DIR

INFORMACION
A B C D E F G B D C E

184

Grafo G6
G

ARISTAS PONDERADAS Para representar un grafo con aristas ponderadas, se requiere reservar espacio en la estructura de datos para el almacenamiento de los pesos. A continuacin presentaremos un grafo con aristas ponderadas. 4
2 6 4 1 2 3

2
3

5 3
7

7
2

Grafo G7

Este ejemplo es un ejemplo particular de grafos de actividades, cada nodo representa un evento y cada arista representa una tarea que al completarse ayuda a disparar el evento siguiente que inicia otras tareas. Cada peso de arista es el tiempo requerido para terminar la tarea. Esta clase de grafos es comnmente usado en sistemas de administracin de proyectos. Cada registro para representar este tipo de grafos tiene 3 campos: nodo, peso y siguiente.
nodo peso siguiente

Una representacin de directorio de nodos para este tipo de grafo se muestra a continuacin. DIRECTORIO INFORMACION DE ARISTA 1 2 3 4 5 6
8 2 7 7 3 2 2 4 6 6 2 9 8 1 3 5 3 4

76

185 8 Como en todas las estructuras de datos necesitamos una forma de buscar un elemento. Para acceder a cada elemento de un arreglo unidimensional, usamos un bucle controlado por un contador. Para una pila extraemos cada elemento hasta que la pila quede vaca, y para una cola quitamos cada elemento hasta que la cola quede tambin vaca. Para un rbol se usan comnmente tres tipos de recorridos ( inorder, preorder y posorder), cada uno de los cuales iba aun nivel de profundidad del rbol y luego suba. Esta estrategia de ir a una rama a su punto ms profundo y moverse hacia arriba se llama una estrategia primero en profundidad. Otras formas de visitar sistemticamente cada uno de los nodos se llama primero en anchura. Para los grafos ambos recorrido son utilizados y a continuacin veremos cada uno de ellos. Recorrido de grafos En muchas aplicaciones es necesario visitar todos los nodos de un grafo, por ejemplo, al imprimir codos los eventos de n grafo de actividades, como el grafo F7, o para determinar que ciudades estn influidas en un mapa de de distancias, figura 1, o determinar el total entre ciudades. Las dos tcnicas bsicas de de recorrido de grafos que representaremos aqu son: recorridos en amplitud y recorrido en profundidad. Los algoritmos de recorridos de grafos marcan cada nodo visitado para no volverlo a visitar, as mismo un algoritmo de recorrido de de grficos puede marcar aristas cuando este haya sido recorrido. Recorrido en amplitud: En un recorrido en amplitud de grafos un nodo se selecciona como posicin inicial, este se visita y se marca, despus, todos los nodos no visitados, adyacentes a ese nodo, se visitan y se marcan en algn orden secuencial. Por ultimo los nodos no visitados, inmediatamente adyacentes a esos nodos, se visitan y marcan, y as sucesivamente, hasta que todos los nodos del grafo se hayan sido visitados en el recorrido. El recorrido en amplitud del grafo G7 es el siguiente: 4
2 6 4 1 2 3

2
3

5 3
7

7
2

Grafo G7

Resulta en el orden siguiente: 1,2,3,4,5,6,7,8. la secuencia 1,3,2,6,5,4,7,8, tambien es una orden de visita valida para el recorrido en amplitud.

186 el algoritmo recorrido utiliza una para almacenar los nodos,de cada nivel que se visita. Estos nodos almacenados son tratados uno por uno luego los nudos adyacentes son visitados y asi sucesivamente hasta que todos los nodos hayan sido visitados. Esta condicin de terminacin se alcanza cuando la cola queda vaca. Los procedimientos de insercin y supresin en cola, fueron explicados en el capitulo cuarto. Recorrido en profundidad: Mientras que el recorrido en amplitud procede nivel por nivel, el recorrido en profundidad sigue primero una trayectoria desde el nodo inicial hasta un nodo terminal, despus una trayectoria desde el mismo punto inicial hasta alcanzar el otro nodo final, y as sucesivamente hasta que todos los nodos hayan sido visitados. El recorrido en profundidad del grafo siguiente: 4
2 6 4 1 2 3

2
3

5 3
7

7
2

Grafo G7

Resulta en una visita de los nodos en el orden siguiente 1,2,4,8,5,7,3,6. Una trayectoria se sigue hasta que todos los nodos no visitados hayan sido alcanzados, despus, el algoritmo regresa al ultimo nodo y visitarlo y que tiene un nodo adyacente no visitado. Una secuencia igualmente valida, como resultado del recorrido en profundidades el siguiente 1, 3, 6, 7, 8, 2, 5, 4. Rutas crticas El grafo tratado anteriormente, representa una grfica de actividades. Casi todos los proyectos se pueden representar por estos grafos. Existen tcnicas muy importantes que utilizan grafos para ayudar a la evacuacin y el anlisis de graficas de actividades. Estas tcnicas son: Mtodo de la ruta critica(MRT). Tcnica de de Evaluacin de desempeo y revisin(TEDR). Tcnica de Asignacin de recursos y planeacin de multi-proyectos (TARPM). en lo que respecta a este trabajo solo daremos una introduccin a los aspectos bsicos del anlisis de grafos de actividades.

187 La estructura de grafo de un proyecto puede mostrar que varias tareas pueden ejecutarse en paralelo. Para ejecutar esto abordaremos un ejemplo de grafo.

3 E 4

B
1
2

F C
3

G 3 2
7 8

H 2

D El inters del manejo de proyectos es el de determinar cuales eventos son crticos para terminar el proyecto oportunamente. Un evento es crtico si, el retrazo en su terminacin, retraza la terminacin en todo el proyecto. El tiempo mas corto de terminacin posible para el proyecto es la trayectoria ms larga a travs del grafo. A esta trayectoria ms larga se le llama ruta crtica; y los eventos crticos se encuentran a lo largo de esta ruta. A la longitud de esta ruta se le llama tiempo de la ruta crtica (TRC). Un sistema de informacin podra incluir programas para encontrar rutas crticas TRC, as como disear y modificar los grafos de actividades. Una forma de detectar la ruta critica y detectar los tiempos de corrimiento permisibles para los eventos que no estn sobre la ruta critica consiste en encontrar los tiempos de inicio ms temprano y los ms tardos de cada evento. El tiempo de inicio ms temprano de evento i(ESTj) es el tiempo de arranque ms prximo para dicho evento. Mostraremos el grafo G7 y su tiempo de inicio ms temprano de cada nodo: 4
2 6 4 1 2 3

2
3

5 3
7

7
2

Grafo G7

El ESTj se calcula a partir de la trayectoria mas larga desde el nodo de inicio hasta el nodo i. El nodo 5 y 6 se deben completar antes de que arranque el evento 7. La formula para calcular el tiempo de inicio mas temprano (ESTi) es: ESTi = mx{ ESTj + T(j,i)} Esto quiere decir que el EST de un nodo es la suma del EST del nodo anterior ms su tiempo.

188 Por ejemplo, el nodo 1 y su EST es 0, porque antes de l no existe ningn evento, por lo tanto 0+ 0=0; el EST del evento del nodo 2 es 6. Porque antes del evento 1 hay 0 evento, y el tiempo del nodo 2 es: 0+6=6. El EST del nodo 4 es 6, porque 6+2=8. El del 7 es 12+2=14, podra ser 3+9+2=14, y el del 8 es 16. El tiempo de inicio ms tardo del evento i, (ESTi) es el tiempo de ms tardo del evento i (LST) es el tiempo ms tardo posible en el cual el evento i puede ser arrancado para que el grafo se complete en un tiempo critico. El conjunto de LST se calcula hacia atrs a partir del evento ultimo. Para que el grafo G7 anterior tenemos la tabla: 1 2 3 4 5 6 7 8 Evento LST ---------0-0 ---------13 15 2 --------- 3 12 9 ---------15 16 1 ---------11 14 - 3 ---------12 14 2 ---------14 16 - 2 ---------15 16 - 1

La formula para obtener el tiempo de inicio ms tardo es la siguiente: LSTi)= min{LSTj T(j,i)} Esto es el tiempo ms tardo del nodo anterior menos su tiempo: por ejemplo, el tiempo ms tardo del evento 4, es el tiempo ms tardo del evento 8 menos 1, esto es, 16-1=15. Ahora para obtener los eventos crticos del proyecto, se resta del tiempo de inicio ms tardo (EST), esto es: LST - EST y los nodos que tengan como diferencia 0 sern los eventos crticos del proyecto, veremos: ----------------------------------------------------evento LST - ESt = resultado ----------------------------------------------------1----------0 - 0 = ------ 0 critico 2---------15 - 6 = ------ 7 3--------- 3 - 3 = ------ 0 Critico 4---------15 7 = ------ 7 5---------11 10=------ 1 6 --------12 - 12=--------0 Critico 7 --------14 - 14= -------0 Critico 8 --------16 - 16= -------0 Critico Los eventos crticos estn representados por los nodos 1,3,6,7,8, los cuales se encuentran sobre la trayectoria ms largo del grafo, y podremos representarlo as: 4
2 1 2

189
4 3

2
3

5 3
7

7
2

Grafo G7

Estos eventos crticos son los que necesitan controlarse para completar a tiempo el proyecto. Hasta dejaremos los grafos, y no nos profundizaremos ms en esto, si el lector desea conocer ms sobre este aspecto es importante estudiar libros sobre investigacin de operaciones. Solo me resta darle las gracias por estudiar este tutorial, y ojala haya cumplido mi objetivo de comunicar mis enseanzas a ustedes, y as gracias nuevamente y mucha suerte. 4.2.4 Clases para la implementacin de grafos Programa de rboles binarios de bsqueda #include <iostream.h> #include <conio.h> #include <stdlib.h> class arbol{ private: int num; arbol *izq; arbol *der; arbol *raiz; public: arbol(){raiz=izq=der=NULL;} void insertar(int n); void eliminar(int); int buscar(int n); void inorder(); void preorder(); }; void arbol::eliminar(int n) { arbol *ptr,*anterior,*temp; if (raiz) { ptr=raiz; anterior=NULL; while ((ptr->num!=n) && (ptr!=NULL)) { anterior=ptr; if (ptr->num<n) ptr=ptr->der; else ptr=ptr->izq;

190 } if (ptr->num==n) { //Caso de Supresin de una hoja if ((ptr->der==NULL) && (ptr->izq==NULL)) if (anterior==NULL) raiz=NULL; else if (anterior->der==ptr) anterior->der=NULL; else anterior->izq=NULL; else //Caso de supresion de un nodo con dos hijos if ((ptr->der!=NULL) && (ptr->izq!=NULL)) { //Busca el valor para remplazar. //Encontrar el nodo que contiene el valor //mas prximo menor que el valor que se //va a suprimir anterior=ptr; temp=ptr->izq; while (temp->der!=NULL) { anterior = temp; temp = temp->der; } //Copiar la informacion a reemplazar en //cuyo valor se va suprimir ptr->num=temp->num; //Suprimir el nodo desde el que se //copio la informacin if (anterior==ptr) anterior->izq=temp->izq; else anterior->der=temp->izq; //ptr toma valor de temp para ser eliminado ptr=temp; } else if (ptr->der!=NULL) //Hay un hijo derecho if (anterior==NULL) raiz=ptr->der; else if (anterior->der==ptr) anterior->der=ptr->der; else anterior->izq=ptr->der; else //hay un hijo izquierdo

191 if (anterior==NULL) raiz=ptr->izq; else if (anterior->der==ptr) anterior->der=ptr->izq; else anterior->izq=ptr->izq; //suprime nodo innecesario delete ptr; cout <<"Eliminado"; getch(); } else { cout <<"No encontr el elemento"; getch(); } } else { cout <<"arbol vacio"; getch(); } } void arbol::insertar(int n) { arbol *nuevo; arbol *ptr,*antes; nuevo=new arbol; nuevo->izq=NULL; nuevo->der=NULL; nuevo->num=n; if (raiz) { ptr=raiz; antes=NULL; while (ptr!=NULL) { antes=ptr; if (ptr->num<=n) ptr=ptr->der; else ptr=ptr->izq; } if (antes->num<=n) antes->der=nuevo; else antes->izq=nuevo; } else

192 { raiz=nuevo; } } void arbol::preorder() { arbol *ptr; int n=-1,recorrido=1; arbol *pila=new arbol[20]; if (raiz) { ptr=raiz; do { while (ptr!=NULL) { n++; pila[n].raiz=ptr; cout <<ptr->num<<" "; ptr=ptr->izq; } if (n>=0) { ptr=pila[n].raiz; n--; ptr=ptr->der; } else recorrido=0; }while (recorrido); delete puntero; getch(); } else { cout<<"No hay elementos en el arbol"; getch(); } } void arbol::inorder() { arbol *ptr; int n=-1,recorrido=1; arbol *puntero=new arbol[50]; if (raiz) { ptr=raiz; do { while (ptr!=NULL) { n++;

193 pila[n].raiz=ptr; ptr=ptr->izq; } if (n>=0) { ptr=pila[n].raiz; n--; cout <<ptr->num<<" ptr=ptr->der; } else recorrido=0; }while (recorrido); delete puntero; getch(); } else { cout<<"No hay elementos en el arbol"; getch(); } } int arbol::buscar(int n) { arbol *ptr; int encontrado=0; if (raiz) { ptr=raiz; while ((ptr!=NULL) && (encontrado==0)) { if (ptr->num==n) encontrado=1; else if (ptr->num<n) ptr=ptr->der; else ptr=ptr->izq; } return encontrado; } else return encontrado; } void main() { int numero; arbol a; arbol *p; p=&a; char op; do{ clrscr();

";

194 cout <<"1.- Insertar\n"; cout <<"2.- Eliminar\n"; cout <<"3.- Consultar\n"; cout <<"4.- Recorrido inorder\n"; cout <<"5.- Recorrido preorder\n"; cout <<"6.- Terminar\n"; cout <<"Seleccione su opcin\n"; op=getch(); switch(op) { case '1':{ clrscr(); cout<<"Insertar elementos"; cout <<"Deme el numero a insertar "; cin>>numero; p=&a; p->insertar(numero); } break; case '2':{ clrscr(); cout <<" Deme el numero a eliminar"; cin>>numero; a.eliminar(numero); } break; case '3':{ clrscr(); cout <<"Deme el numero a buscar "; cin>>numero; p=&a; if (a.buscar(numero)>0) cout <<"el numero "<<numero<<" si esta en el arbol"; else cout <<"el numero "<<numero<<" no esta en el arbol"; getch(); } break; case '4':{ p=&a; p->inorder(); } break; case '5':{ p=&a; p->preorder(); } break;

195 } }while (op!='6');

You might also like