manual de estructura de datos -...

49
1 MANUAL DE ESTRUCTURA DE DATOS Evelio Ramiro Arrieta Torres Manual de EStructura de Datos EVELIO ARRIETA Instituto Tecnológico De Soledad Atlántico ITSA (958-57393) ISBN 978-958-59173-9-2

Upload: vuongtu

Post on 21-Oct-2018

224 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Manual de EStructura de Datos - itsa.edu.coitsa.edu.co/docs/25-E-Arrieta-Manual-de-Estructura-de-Datos.pdf · LISTAS ENLAZADAS..... 18 3.1 Definición ... Empecemos recordando que

1

MANUAL DE ESTRUCTURA DE DATOS

Evelio Ramiro Arrieta Torres

Man

ual d

e ES

truc

tura

de

Dat

os

EVELIO ARRIETA Inst

ituto

Tecn

ológ

ico

De

Sole

dad

Atlá

ntic

o IT

SA (9

58-5

7393

)IS

BN 9

78-9

58-5

9173

-9-2

Page 2: Manual de EStructura de Datos - itsa.edu.coitsa.edu.co/docs/25-E-Arrieta-Manual-de-Estructura-de-Datos.pdf · LISTAS ENLAZADAS..... 18 3.1 Definición ... Empecemos recordando que

Introducción ......................................................................................................... 4

1. COLAS............................................................................................................... 5

1.1 Definición .................................................................................................. 5

1.2 Operaciones básicas con colas .................................................................. 6

1.2.1 Agregar un elemento ........................................................................ 6

1.2.2 Eliminar un elemento de una cola .................................................... 7

1.3 Aplicaciones del uso de Colas ................................................................. 10

1.4 Cola circular ............................................................................................ 10

1.5 Ejercicios Colas ....................................................................................... 12

2. PILAS .............................................................................................................. 13

2.1 Definición ................................................................................................ 13

2.2 Operaciones básicas con pilas ................................................................ 13

2.2.1 Inserción de elementos .................................................................. 14

2.2.1.1 Inserción en una pila vacía ..................................................... 14

2.2.2 Eliminación en una Pila ................................................................... 15

2.5 Aplicaciones del uso de Pilas .................................................................. 17

2.6 Ejercicios con Pilas .................................................................................. 17

3. LISTAS ENLAZADAS ......................................................................................... 18

3.1 Definición ................................................................................................ 18

3.2 Declaraciones de tipos para manejar listas............................................. 18

3.3 Operaciones básicas con listas ................................................................ 19

3.3.1 Insertar elementos en una lista abierta .......................................... 19

3.3.2 Eliminar elementos en una lista abierta ......................................... 19

3.4 LISTAS DOBLEMENTE ENLADAS (Listas dobles) ....................................... 24

3.4.1 Operaciones básicas con listas doblemente enlazadas .................. 24

3.5 Ejercicios con Listas enlazadas ................................................................ 28

4. Recursividad ................................................................................................... 28

4.1 Concepto de recursividad. ...................................................................... 28

4.2 Ejemplos ................................................................................................. 29

4.3 Uso de la recursividad. ............................................................................ 32

4.4 Ejercicios ................................................................................................. 34

5. Arboles ........................................................................................................... 35

5.1 Definición ................................................................................................ 35

5.2 Operaciones básicas con árboles ............................................................ 36

5.3 Recorridos por árboles............................................................................ 37

CON

TEN

IDO

Page 3: Manual de EStructura de Datos - itsa.edu.coitsa.edu.co/docs/25-E-Arrieta-Manual-de-Estructura-de-Datos.pdf · LISTAS ENLAZADAS..... 18 3.1 Definición ... Empecemos recordando que

5.3.1 Pre-orden .................................................................................................. 37

5.3.2 In-orden .................................................................................................... 38

5.3.3 Post-orden ................................................................................................ 38

5.4 Eliminar nodos en un árbol .......................................................................... 38

5.5 Arboles ordenados ....................................................................................... 39

5.6 Árboles binarios de búsqueda (ABB) ........................................................... 40

5.6.1 Definición .................................................................................................. 40

5.6.2 Operaciones en ABB ................................................................................. 40

5.6.3 Buscar un elemento .................................................................................. 41

5.6.4 Insertar un elemento ................................................................................ 41

5.6.5 Borrar un elemento .................................................................................. 42

Ejemplo 1: Borrar un nodo hoja .............................................................................. 43

Ejemplo 2: Borrar un nodo rama con intercambio de un nodo hoja. ............................ 43

Ejemplo 3: Borrar un nodo rama con intercambio de un nodo rama. .......................... 44

5.7 EJERCICIOS ................................................................................................... 48

6. Bibliografía de Referencia .............................................................................. 49

CON

TEN

IDO

Page 4: Manual de EStructura de Datos - itsa.edu.coitsa.edu.co/docs/25-E-Arrieta-Manual-de-Estructura-de-Datos.pdf · LISTAS ENLAZADAS..... 18 3.1 Definición ... Empecemos recordando que

4

Introducción

En la práctica, la mayor parte de información útil no aparece aislada en forma de datos simples, sino que lo hace de forma organizada y estructurada. Los diccionarios, guías, enciclopedias, etc., son colecciones de datos que serían inútiles si no estuvieran organizadas de acuerdo con unas determinadas reglas. Además, tener estructurada la información supone ventajas adicionales, al facilitar el acceso y el manejo de los datos. Por ello parece razonable desarrollar la idea de la agrupación de datos, que tengan un cierto tipo de estructura y organización interna.

La selección de una estructura de datos frente a otra, a la hora de programar es una decisión importante, ya que ello influye decisivamente en el algoritmo que vaya a usarse para resolver un determinado problema. El objetivo de este manual no es sólo la descripción de las distintas estructuras, sino también la comparación de las mismas en términos de utilidad para la programación.

Empecemos recordando que un dato de tipo simple, no está compuesto de otras estructuras, que no sean los bits, y que por tanto su representación sobre el ordenador es directa, sin embargo existen unas operaciones propias de cada tipo, que en cierta manera los caracterizan. Una estructura de datos es, a grandes rasgos, una colección de datos (normalmente de tipo simple) que se caracterizan por su organización y las operaciones que se definen en ellos. Por tanto, una estructura de datos vendrá caracterizada tanto por unas ciertas relaciones entre los datos que la constituyen (ej. el orden de las componentes de un vector de números reales), como por las operaciones posibles en ella. Esto supone que podamos expresar formalmente, mediante un conjunto de reglas, las relaciones y operaciones posibles.

Page 5: Manual de EStructura de Datos - itsa.edu.coitsa.edu.co/docs/25-E-Arrieta-Manual-de-Estructura-de-Datos.pdf · LISTAS ENLAZADAS..... 18 3.1 Definición ... Empecemos recordando que

5

1. COLAS1.1 Definición

Una cola es un tipo especial de lista abierta en la que sólo se puede insertar nodos en uno de los extremos de la lista y sólo se pueden eliminar nodos en el otro. Además, como sucede con las pilas, las escrituras de datos siempre son inserciones de nodos, y las lecturas siempre eliminan el nodo leído.

Este tipo de lista es conocido como lista FIFO (First In First Out), el primero en entrar es el primero en salir.

El símil cotidiano es una cola para comprar, por ejemplo, las entradas del cine. Los nuevos compradores sólo pueden colocarse al final de la cola, y sólo el primero de la cola puede comprar la entrada.

Las colas se representan por listas enlazadas (a) o por arrays (b). Se necesitan dos punteros o variables: frente y final, y la lista o array de n elementos.

Fuente: Propia

El nodo típico para construir colas es el mismo para la construcción de listas:

struct nodo { int dato; struct nodo *siguiente;}

Una cola, entonces, es una lista abierta. Así que sigue siendo muy importante que el programa nunca pierda el valor del puntero al primer elemento, igual que pasa con las listas abiertas. Además, debido al funcionamiento de las colas, también deberemos mantener un puntero para el último elemento de la cola, que será el punto donde insertemos nuevos nodos.

Teniendo en cuenta que las lecturas y escrituras en una cola se hacen siempre en extremos distintos, lo más fácil será insertar nodos por el final, a continuación del nodo que no tiene nodo siguiente, y leerlos desde el principio, hay que recordar que leer un nodo implica eliminarlo de la cola.

100 48264 119frente

frente final

final

a)

b)

Page 6: Manual de EStructura de Datos - itsa.edu.coitsa.edu.co/docs/25-E-Arrieta-Manual-de-Estructura-de-Datos.pdf · LISTAS ENLAZADAS..... 18 3.1 Definición ... Empecemos recordando que

6

1.2 Operaciones básicas con colas

De nuevo nos encontramos ante una estructura con muy pocas operaciones disponibles. Las colas sólo permiten agregar y leer elementos:

· Agregar: Inserta un elemento al final de la cola.

· Leer: Lee y elimina un elemento del principio de la cola.

1.2.1 Agregar un elemento

Las operaciones con colas son muy sencillas, prácticamente no hay casos especiales, salvo que la cola esté vacía.

1.2.1.1 Agregar elemento en una cola vacía

Cola vacía

Partiremos de que ya tenemos el nodo a insertar y, por supuesto un puntero que apunte a él, además los punteros que definen la cola, primero y último que valdrán NULO:

Fuente: Propia

Elemento encolado

El proceso es muy simple, bastará con que:

1. Hacer que nodo.siguiente apunte a NULO.

2. Que el puntero primero apunte a nodo.

3. Y que el puntero último también apunte a nodo.

1.2.1.2 Agregar elemento en una cola no vacía

Fuente: Propia

Page 7: Manual de EStructura de Datos - itsa.edu.coitsa.edu.co/docs/25-E-Arrieta-Manual-de-Estructura-de-Datos.pdf · LISTAS ENLAZADAS..... 18 3.1 Definición ... Empecemos recordando que

7

Cola no vacíaDe nuevo partiremos de un nodo a insertar, con un puntero que apunte a él, y de una cola, en este caso, al no estar vacía, los punteros primero y último no serán nulos:

Elemento insertado en la cola

El proceso sigue siendo muy sencillo:

1. Hacemos que nodo.siguiente apunte a NULO.

2. Después que ultimo.siguiente apunte a DATO (último.nodo).

3. Y actualizamos último, haciendo que apunte a nodo.

1.2.1.3 Agregar elemento en una cola, caso general

Para generalizar el caso anterior, sólo necesitamos agregar una operación:

1. Hacemos que nodo.siguiente apunte a NULO.

2. Si último no es NULO, hacemos que ultimo.siguiente apunte a nodo.

3. Y actualizamos último, haciendo que apunte a nodo.

4. Si primero es NULO, significa que la cola estaba vacía, así que haremos que primero apunte también a nodo.

1.2.2 Eliminar un elemento de una cola

Ahora también existen dos casos, que la cola tenga un solo elemento o que tenga más de uno.

1.2.2.1 Eliminar un elemento en una cola con más de un elemento

Usaremos un puntero a un nodo auxiliar:

Cola con más de un elemento

1. Hacemos que nodo apunte al primer elemento de la cola, es decir a primero.

2. Asignamos a primero la dirección del segundo nodo de la pila: primero.siguiente.

3. Guardamos el contenido del nodo para devolverlo como retorno, recuerda que la operación de lectura en colas implican también borrar.

4. Liberamos la memoria asignada al primer nodo, el que queremos eliminar.

Fuente: Propia

Page 8: Manual de EStructura de Datos - itsa.edu.coitsa.edu.co/docs/25-E-Arrieta-Manual-de-Estructura-de-Datos.pdf · LISTAS ENLAZADAS..... 18 3.1 Definición ... Empecemos recordando que

8

1.2.2.2 Eliminar un elemento en una cola con un solo elemento

Cola con un elemento

También necesitamos un puntero a un nodo auxiliar:

1. Hacemos que nodo apunte al primer elemento de la pila, es decir a primero.

Fuente: Propia

2. Asignamos NULO a primero, que es la dirección del segundo nodo teórico de la cola: primero.siguiente.

3. Guardamos el contenido del nodo para devolverlo como retorno, recuerda que la operación de lectura en colas implican también borrar.

4. Liberamos la memoria asignada al primer nodo, el que queremos eliminar.

5. Hacemos que último apunte a NULO, ya que la lectura ha dejado la cola vacía.

1.2.2.3 Eliminar un elemento en una cola caso general

1. Hacemos que nodo apunte al primer elemento de la pila, es decir a primero.

2. Asignamos a primero la dirección del segundo nodo de la pila: primero.siguiente.

3. Guardamos el contenido del nodo para devolverlo como retorno, recuerda que la operación de lectura en colas implican también borrar.

4. Liberamos la memoria asignada al primer nodo, el que queremos eliminar.

5. Si primero es NULO, hacemos que ultimo también apunte a NULO, ya que la lectura ha dejado la cola vacía.

Page 9: Manual de EStructura de Datos - itsa.edu.coitsa.edu.co/docs/25-E-Arrieta-Manual-de-Estructura-de-Datos.pdf · LISTAS ENLAZADAS..... 18 3.1 Definición ... Empecemos recordando que

9

Colas en java usando vectores. Ejemploimport javax.swing.*;public class Colas { public static void main (String args[]){ byte max=9,op=0; int i=0,fr=-1,fin=-1; int V []= new int[10]; while(op!=4){op=Byte.parseByte(JOptionPane.showInputDialog(“Digite un numero\n”+ “1.Insertar\n”+ “2.Eliminar\n”+ “3.Mostrar\n”+ “4.Salir”)); switch(op){ case 1: if(fin!=max){ if(fr==-1){ fin=fin+1; fr=fr+1; V[fin]= Byte.parseByte(JOptionPane.showInputDialog(“Digite el valor”)); }else{ fin=fin+1; V[fin]= Byte.parseByte(JOptionPane.showInputDialog(“Digite el valor”)); } }else{ JOptionPane.showMessageDialog(nulo, “Overflow”); } break; case 2: if(fin!=-1){ if(fin==fr){ fin=-1; fr=-1; }else{ fr=fr+1;} }else{ JOptionPane.showMessageDialog(nulo, “Underflow”); } break; case 3: if(fin!=-1){ for(i=fr;i<=fin;i=i+1){ JOptionPane.showMessageDialog(nulo,V[i]); } } else{ JOptionPane.showMessageDialog(nulo, “Cola vacia”); }

break; case 4: JOptionPane.showMessageDialog(nulo, “Gracias”); break;

} }

}}

Page 10: Manual de EStructura de Datos - itsa.edu.coitsa.edu.co/docs/25-E-Arrieta-Manual-de-Estructura-de-Datos.pdf · LISTAS ENLAZADAS..... 18 3.1 Definición ... Empecemos recordando que

10

1.3 Aplicaciones del uso de Colas

Las colas se pueden utilizar habitualmente en muchas áreas de la informática. Quizás la aplicación más común de las colas es la organización de tareas de un ordenador. En general, los trabajos enviados a un ordenador son “encolados” por éste, para ir procesando secuencialmente todos los trabajos en el mismo orden en que se reciben. Cuando el ordenador recibe el encargo de realizar una tarea, ésta es almacenada al final de la cola de trabajos. En el momento que la tarea que estaba realizando el procesador acaba, éste selecciona la tarea situada al principio de la cola para ser ejecutada a continuación. Todo esto suponiendo la ausencia de prioridades en los trabajos. En caso contrario, existirá una cola para cada prioridad.

Otra de los usos de una cola es, por ejemplo, a la hora de gestionar eficientemente los trabajos que deben ser enviados a una impresora (o a casi cualquier dispositivo conectado a un computador). De esta manera, la maquina controla el envío de trabajos al dispositivo, no enviando un trabajo hasta que la impresora no termine con el anterior

1.4 Cola circular

Una cola circular o anillo es una estructura de datos en la que los elementos están de forma circular y cada elemento tiene un sucesor y un predecesor. Los elementos pueden consultarse, agregarse y eliminarse únicamente desde la cabeza del anillo que es una posición distinguida. Esta avanza en el sentido de las agujas del reloj.

Fuente: Propia

En la figura mostrada muestra una cola circular con un solo dato almacenado. La variable final es la posición en donde se hizo la última inserción. Después que se ha producido una inserción, final se mueve circularmente a la derecha. La implementación del movimiento circular se realiza utilizando la “teda” de los restos:

Page 11: Manual de EStructura de Datos - itsa.edu.coitsa.edu.co/docs/25-E-Arrieta-Manual-de-Estructura-de-Datos.pdf · LISTAS ENLAZADAS..... 18 3.1 Definición ... Empecemos recordando que

11

Programa en Java cola circular

import javax.swing.*;public class Colas_Circulares {public static void main(String args[]){

int OP=0; int VH[]= new int [4]; int Max=3; int Last=-1, First=-1; String A= “\n”;

while(OP!=5){ OP=Integer.parseInt(JOptionPane.showInputDialog(“Menu de Operaciones\n”+”1.Insertar\n”+”2.Elimi nar\n”+”3.Imprimir\n”+”4.Imprimir First & Last\n”+”5.Salir\n”+”Escoja Opcion”)); switch(OP){ case 1: if((Last==Max && First==0)||(First==Last+1)){ JOptionPane.showMessageDialog(nulo, “La Cola esta Llena”);

}else if(First==-1 && Last==-1){ Last++; First++; VH[Last]= Integer.parseInt(JOptionPane.showInputDialog(“Ingrese Datos”)); }else if(First!=-1 && Last!=Max){ Last++; VH[Last]= Integer.parseInt(JOptionPane.showInputDialog(“Ingrese Datos”)); }else if(Last==Max && First!=0){ Last=0; } break; case 2: if(First==-1 && Last==-1){ JOptionPane.showMessageDialog(nulo, “La Cola Esta Vacia”); }else if(First==Last){ Last=-1; First=-1; JOptionPane.showMessageDialog(nulo, “Se ha Eliminado el Unico Elemento en la cola”); }else if(First!=Last){ First++; JOptionPane.showMessageDialog(nulo, “Se ha Eliminado el Primer Elemento de la Cola”); }else if(Last==Max){ Last=0;

} break; case 3: if(Last!=-1 && First!=-1){ for(int i=First;i<=Last;i++){ A=A+VH[i]+”,”;

} JOptionPane.showMessageDialog(nulo, “Los Elementos de la Cola son: “ + A); }else if(Last!=Max){ for(int j=Last;j<Last;j++){ A=A+VH[j]+”,”; }

Page 12: Manual de EStructura de Datos - itsa.edu.coitsa.edu.co/docs/25-E-Arrieta-Manual-de-Estructura-de-Datos.pdf · LISTAS ENLAZADAS..... 18 3.1 Definición ... Empecemos recordando que

12

for(int x=First;x<=Max;x++){ A=A+VH[x]+”,”; } JOptionPane.showMessageDialog(nulo, “Los Elemetos de la Cola son: “ + A);

}else{ JOptionPane.showMessageDialog(nulo, “La Cola Esta Vacia”); } break;

case 4: if(Last!=-1 && First!=-1){ JOptionPane.showMessageDialog(nulo, “El Primer Elemento de la Cola es: “ +VH[First]+ “ El Ultimo Elemento de la Cola es: “ +VH[Last]);

}else{ JOptionPane.showMessageDialog(nulo, “La Cola Esta Vacia”); } break; case 5: JOptionPane.showMessageDialog(nulo, “Gracias Por Utilizar Nuestro Programa, EXITOS!!!”);

break; default: JOptionPane.showMessageDialog(nulo, “Ha Digitado una Opcion Fuera de Rango”); } }}}

1.5 Ejercicios Colas

1. Escriba una rutina que reciba una Cola C de números enteros y mueva sus elementos a una nueva cola, pero manteniendo el orden de salida de los elementos. Al finalizar la Cola C no debe contener elementos.

2. Escriba una rutina que reciba una Cola C de números enteros y mueva sus elementos a una nueva cola, pero invirtiendo el orden de salida de los elementos. Al finalizar la Cola C no debe contener elementos.

3. Escriba una rutina que reciba dos Colas C1 y C2 de números enteros y devuelva una nueva Cola con los elementos concatenados en el orden C1 y C2. Es de destacar que las Colas recibidas no deben ser sufrir ningún tipo de cambio o alteración.

4. Escriba una rutina que reciba dos Colas C1 y C2 de números enteros y proceda a intercambiar sus elementos, pero manteniendo el orden de salida de los mismos. Al finalizar la rutina, la Cola C1 tendrá los elementos de la Cola C2 y esta a su vez tendrá los elementos de la Cola C1.

5. Escriba una rutina que reciba una Cola C de números flotantes y devuelva una nueva Cola pero con los elementos invertidos, es decir el último de la Cola C, pasará a ser el primero de la nueva Cola. Es de destacar que la Cola C no debe sufrir ningún tipo de cambio o alteración.

6. Escriba una rutina que reciba una Cola C de números flotantes y devuelva una Pila, manteniendo el orden de salida de los elementos. Es de destacar que la Cola C no debe sufrir ningún tipo de cambio o alteración.

Page 13: Manual de EStructura de Datos - itsa.edu.coitsa.edu.co/docs/25-E-Arrieta-Manual-de-Estructura-de-Datos.pdf · LISTAS ENLAZADAS..... 18 3.1 Definición ... Empecemos recordando que

13

7. Escribir un programa en el que generen 100 números aleatorios en el rango -25…. + 25 y se guarden en una cola. Una vez creada la cola, el usuario puede solicitar que se forme otra cola con los números negativos que tiene la cola original.

8. Escribir un programa que ingrese dos colas del mismo tipo e indique si las dos colas son idénticas.

9. Un pequeño supermercado dispone en la salida de tres cajas de pago. En el local hay 25 carritos de compra. Escribir un programa que simule el funcionamiento, siguiendo las siguientes reglas:

a. SI cuando llega un cliente no hay ningún carrito disponible, espera a que lo haya. b. Ningún cliente se impaciente y abandona el supermercado sin pasar por alguna de las colas de las cajas. c. Cuando un cliente finaliza su compra, se coloca en la cola de la caja que hay menos gente, y no se cambia de cola. d. En el momento en que un cliente paga en la caja, su carrito de compra queda disponible.

2. PILAS2.1 Definición

Una pila es un tipo especial de lista abierta en la que sólo se pueden insertar y eliminar nodos en uno de los extremos de la lista. Estas operaciones se conocen como “push” y “pop”, respectivamente insertar y eliminar. Además, las escrituras de datos siempre son inserciones de nodos, y las lecturas siempre eliminan el nodo leído.

Estas características implican un comportamiento de lista LIFO (Last In First Out), el último en entrar es el primero en salir.

El símil del que deriva el nombre de la estructura es una pila de platos. Sólo es posible agregar platos en la parte superior de la pila, y sólo pueden tomarse del mismo extremo.

Fuente: Propia

Una pila es una lista abierta. Así que sigue siendo muy importante que nuestro programa nunca pierda el valor del puntero al primer elemento, igual que pasa con las listas abiertas.

Teniendo en cuenta que las inserciones y borrados en una pila se hacen siempre en un extremo, lo que consideramos como el primer elemento de la lista es en realidad el último elemento de la pila.

2.2 Operaciones básicas con pilas

Las pilas tienen un conjunto de operaciones muy limitado, sólo permiten las operaciones de “push” y “pop”:

· Push (Insertar): Agregar un elemento al final de la pila.

· Pop (Eliminar): Leer y eliminar un elemento del final de la pila.

Page 14: Manual de EStructura de Datos - itsa.edu.coitsa.edu.co/docs/25-E-Arrieta-Manual-de-Estructura-de-Datos.pdf · LISTAS ENLAZADAS..... 18 3.1 Definición ... Empecemos recordando que

14

2.2.1 Inserción de elementos

Las operaciones con pilas son muy simples, no hay casos especiales, salvo que la pila esté vacía.

2.2.1.1 Inserción en una pila vacía

Partiremos de que ya tenemos el nodo a insertar y, por supuesto un puntero que apunte a él, además el puntero a la pila valdrá NULO:

Fuente: Propia

Inserción en pila vacía

El proceso es muy simple, bastará con que:

1. nodo.siguiente apunte a NULO.

2. Pila apunte a nodo.

2.2.1.2 Insertar en una pila no vacía

Fuente: Propia

Push en pila no vacía

Podemos considerar el caso anterior como un caso particular de éste, la única diferencia es que podemos y debemos trabajar con una pila vacía como con una pila normal.

De nuevo partiremos de un nodo a insertar, con un puntero que apunte a él, y de una pila, en este caso no vacía:

Fuente: Propia

Resultado

El proceso sigue siendo muy sencillo:

1. Hacemos que nodo.siguiente apunte a Pila.

2. Hacemos que Pila apunte a nodo.

Page 15: Manual de EStructura de Datos - itsa.edu.coitsa.edu.co/docs/25-E-Arrieta-Manual-de-Estructura-de-Datos.pdf · LISTAS ENLAZADAS..... 18 3.1 Definición ... Empecemos recordando que

15

2.2.2 Eliminación en una Pila

Eliminar (pop)

Ahora sólo existe un caso posible, ya que sólo podemos leer desde un extremo de la pila.

Partiremos de una pila con uno o más nodos, y usaremos un puntero auxiliar, nodo:

Fuente: Propia

Resultado

1. Hacemos que nodo apunte al primer elemento de la pila, es decir a Pila.

2. Asignamos a Pila la dirección del segundo nodo de la pila: Pila.siguiente.

3. Guardamos el contenido del nodo para devolverlo como retorno, recuerda que la operación pop equivale a leer y borrar.

4. Liberamos la memoria asignada al primer nodo, el que queremos eliminar.

Si la pila sólo tiene un nodo, el proceso sigue siendo válido, ya que el valor de Pila.siguiente es NULO, y después de eliminar el último nodo la pila quedará vacía, y el valor de Pila será NULO.

Page 16: Manual de EStructura de Datos - itsa.edu.coitsa.edu.co/docs/25-E-Arrieta-Manual-de-Estructura-de-Datos.pdf · LISTAS ENLAZADAS..... 18 3.1 Definición ... Empecemos recordando que

16

Pilas en java ejemploimport javax.swing.*;public class Pilas { public static void main (String args[]){ byte max=9,op=0; int tope=-1,i=0; byte V []= new byte[10]; while(op!=4){ op=Byte.parseByte(JOptionPane.showInputDialog(“Digite un numero\n”+ “1.Insertar\n”+ “2.Eliminar\n”+ “3.Mostrar\n”+ “4.Salir”)); switch(op){ case 1: if(tope!=max){ tope=tope+1; V[tope]=Byte.parseByte(JOptionPane.showInputDialog(“Digite su valor”)); }else{ JOptionPane.showMessageDialog(nulo, “Overflow”); } break;case 2: if(tope!=-1){ tope=tope-1; }else{ JOptionPane.showMessageDialog(nulo, “Underflow”); } break; case 3: if(tope!=-1){ for(i=tope;i>=0;i=i-1){ JOptionPane.showMessageDialog(nulo,V[i]); } } else{ JOptionPane.showMessageDialog(nulo, “Pila vacia”); } break; case 4: JOptionPane.showMessageDialog(nulo, “Gracias”); break; } } }}

Page 17: Manual de EStructura de Datos - itsa.edu.coitsa.edu.co/docs/25-E-Arrieta-Manual-de-Estructura-de-Datos.pdf · LISTAS ENLAZADAS..... 18 3.1 Definición ... Empecemos recordando que

17

2.5 Aplicaciones del uso de Pilas

Con la implementación de las pilas es posible el uso de la modulación (recursividad). La variable que llama al mismo procedimiento en el que está, habrá que guardarla así como el resto de variables de la nueva llamada, para a la vuelta de la recursividad ir sacándolas, esto es posible a la implementación de pilas. Las pilas también se utilizan en muchas aplicaciones que utilizamos con frecuencia. Por ejemplo, la gestión de ventanas en Windows (cuando cerramos una ventana siempre recuperamos la que teníamos detrás). Otro ejemplo es la evaluación general de cualquier expresión matemática para evitar tener que calcular el número de variables temporales que hacen falta.

Por ejemplo: 3 + 4 * (8 – 2 * 5)

2.6 Ejercicios con Pilas1. Escriba una rutina que reciba una Pila P de números enteros y mueva sus elementos a una nueva Pila, pero

manteniendo el orden de salida de los mismos. Al finalizar la Pila P no debe contener elementos. 2. Escriba una rutina que reciba una Pila P de números enteros y mueva sus elementos a una nueva Pila, pero

invirtiendo el orden de salida de los mismos. Al finalizar la Pila P no debe contener elementos. 3. Escriba una rutina que reciba dos Pilas P1 y P2 de números flotantes y apile las mismas en una nueva Pila

resultante. Es de destacar que las Pilas recibidas no deben sufrir ningún tipo de cambio o alteración.4. Escriba una rutina que reciba dos Pilas P1 y P2 de números enteros y proceda a intercambiar sus elementos,

pero manteniendo el orden de salida de los elementos. Al finalizar la rutina, la Pila P1 tendrá los elementos de la Pila P2 y esta a su vez tendrá los elementos de la Pila P1.

5. Escriba una rutina que reciba una Pila P de números enteros y devuelva una copia exacta de la misma. Es de destacar que la Pila P no debe sufrir ningún tipo de cambio o alteración.

6. Escriba una rutina que reciba una Pila P de números flotantes y devuelva una nueva Pila pero con los elementos invertidos, es decir el último de la Pila P, pasará a ser el primero de la nueva Pila Es de destacar que la Pila P no debe sufrir ningún tipo de cambio o alteración.

7. Escriba una rutina que reciba una Pila P de números flotantes y devuelva otra pila, manteniendo el orden de salida de los elementos. Es de destacar que la Pila P no debe sufrir ningún tipo de cambio o alteración.

8. En un archivo f están almacenados números enteros arbitrariamente grandes. La disposición es tal que hay un número entero por cada línea de F. Escribir un programa que muestre por pantalla la suma de todos los números enteros. Al resolver el problema habrá que tener en cuenta que, al ser enteros grandes, no pueden almacenarse en variables numéricas.

9. Utilizar dos pilas para guardar los dos primeros números enteros, almacenándose digito a dígito. Al extraer los elementos de la pila, salen en orden inverso y, por lo tanto, de menor peso a mayor peso; se suman digito con digito y el resultado se guarda en una cola, también digito a digito. A partir de este primer paso se obtienen el siguiente número del archivo, se guarda en una pila y, a continuación, se suma digito a dígito con el número que se encuentra en la cola; el resultado se guarda en otra cola. El proceso se repite, nuevo número del archivo se mete en la pila, que se suma con el número actual de la cola.

Page 18: Manual de EStructura de Datos - itsa.edu.coitsa.edu.co/docs/25-E-Arrieta-Manual-de-Estructura-de-Datos.pdf · LISTAS ENLAZADAS..... 18 3.1 Definición ... Empecemos recordando que

18

Ejercicios Complementarios Pilas y Colas

1. Realizar un procedimiento que cuente la cantidad de elementos de una cola. Lo mismo para una pila y una lista. Las estructuras deben quedar en el estado original.

2. Realizar un procedimiento que invierta el orden de la cola. Lo mismo para una pila y una lista. 3. Realizar un procedimiento que saque el elemento N de la cola. Lo mismo para una pila y una lista.

Tener en cuenta que los demás elementos deben quedar en el mismo orden. 4. Realizar un procedimiento que ingrese un elemento en la posición N de una cola. Lo mismo para

una pila y una lista. Tener en cuenta que los demás elementos deben quedar en el mismo orden. 5. Una cola prioridad tiene una estructura similar a la de una cola, con la diferencia que cada elemento

tiene un campo que indica su prioridad. Los elementos se ingresan en la cola según el orden de prioridad. Realizar dos procedimientos, uno para ingresar un elemento en la cola prioridad y otro para extraerlo. Utilizar para ello un TDA COLA.

3. LISTAS ENLAZADAS3.1 Definición

La forma más simple de estructura dinámica es la lista abierta. En esta forma los nodos se organizan de modo que cada uno apunta al siguiente, y el último no apunta a nada, es decir, el puntero del nodo siguiente vale NULO.

En las listas abiertas existe un nodo especial: el primero. Normalmente diremos que nuestra lista es un puntero a ese primer nodo y llamaremos a ese nodo la cabeza de la lista. Eso es porque mediante ese único puntero podemos acceder a toda la lista.

Cuando el puntero que usamos para acceder a la lista vale NULO, diremos que la lista está vacía.

El nodo típico para construir listas tiene esta forma:

struct nodo { int dato; nodo *siguiente;}

3.2 Declaraciones de tipos para manejar listas

Normalmente se definen varios tipos que facilitan el manejo de las listas, la declaración de tipos puede tener una forma parecida a esta:

struct nodo { int dato; nodo *siguiente;}

Lista es el tipo para declarar listas, como puede verse, un puntero a un nodo y una lista son la misma cosa. En realidad, cualquier puntero a un nodo es una lista, cuyo primer elemento es el nodo apuntado.

Page 19: Manual de EStructura de Datos - itsa.edu.coitsa.edu.co/docs/25-E-Arrieta-Manual-de-Estructura-de-Datos.pdf · LISTAS ENLAZADAS..... 18 3.1 Definición ... Empecemos recordando que

19

3.3 Operaciones básicas con listas

Con las listas tendremos unas operaciones básicas que se pueden realizar:· Agregar o insertar elementos.· Buscar o localizar elementos.· Borrar elementos.· Moverse a través de una lista, anterior, siguiente, primero.

Cada una de estas operaciones tendrá varios casos especiales, por ejemplo, no será lo mismo insertar un nodo en una lista vacía, o al principio de una lista no vacía, o la final, o en una posición intermedia.

3.3.1 Insertar elementos en una lista abierta

Algunos casos de inserción en una lista se verán a continuación.

3.3.1.1 Insertar un elemento en una lista vacía

Este es uno de los casos más sencillo. Partiremos de que ya tenemos el nodo a insertar y, por supuesto un puntero que apunte a él, además el puntero a la lista valdrá NULO:

Fuente: Propia

El proceso es muy simple, bastará con que:

1. nodo.siguiente apunte a NULO.2. Lista apunte a nodo.

3.3.1.2 Insertar un elemento en la primera posición de una lista

Fuente: Propia

Podemos considerar el caso anterior como un caso particular de éste, la única diferencia es que en el caso anterior la lista es una lista vacía, pero siempre podemos, y debemos considerar una lista vacía como una lista.De nuevo partiremos de un nodo a insertar, con un puntero que apunte a él, y de una lista, en este caso no vacía:

Fuente: Propia

Insertado al principio de la lista

El proceso sigue siendo muy sencillo:1. Hacemos que nodo.siguiente apunte a Lista.

2. Hacemos que Lista apunte a nodo.

Page 20: Manual de EStructura de Datos - itsa.edu.coitsa.edu.co/docs/25-E-Arrieta-Manual-de-Estructura-de-Datos.pdf · LISTAS ENLAZADAS..... 18 3.1 Definición ... Empecemos recordando que

20

3.3.1.3 Insertar un elemento en la última posición de una lista

Este es otro caso especial. Para este caso partiremos de una lista no vacía:

Fuente: Propia

Insertar al final

El proceso en este caso tampoco es excesivamente complicado:

1. Necesitamos un puntero que señale al último elemento de la lista. La manera de conseguirlo es empezar por el primero y avanzar hasta que el nodo que tenga como siguiente el valor NULO.

2. Hacer que nodo.siguiente sea NULO.

3. Hacer que ultimo.siguiente sea nodo.

Fuente: Propia

3.3.1.4 Insertar un elemento a continuación de un nodo cualquiera de una lista

De nuevo podemos considerar el caso anterior como un caso particular de este. Ahora el nodo “anterior” será aquel a continuación del cual insertaremos el nuevo nodo:

Insertar en medio

Suponemos que ya disponemos del nuevo nodo a insertar, apuntado por nodo, y un puntero al nodo a continuación del que lo insertaremos.

El proceso a seguir será:

1. Hacer que nodo.siguiente señale a anterior.siguiente.

2. Hacer que anterior.siguiente señale a nodo.

Fuente: Propia

Es muy importante que el programa nunca pierda el valor del puntero al primer elemento, ya que si no existe ninguna copia de ese valor, y se pierde, será imposible acceder al nodo y no podremos liberar el espacio de memoria que ocupa.

Page 21: Manual de EStructura de Datos - itsa.edu.coitsa.edu.co/docs/25-E-Arrieta-Manual-de-Estructura-de-Datos.pdf · LISTAS ENLAZADAS..... 18 3.1 Definición ... Empecemos recordando que

21

3.3.2 Eliminar elementos en una lista abierta

De nuevo podemos encontrarnos con varios casos, según la posición del nodo a eliminar.

3.3.2.1 Eliminar el primer nodo de una lista abierta Eliminar primer nodo

Es el caso más simple. Partiremos de una lista con uno o más nodos, y usaremos un puntero auxiliar, nodo:

1. Hacemos que nodo apunte al primer elemento de la lista, es decir a Lista.

2. Asignamos a Lista la dirección del segundo nodo de la lista: Lista.siguiente.

3. Liberamos la memoria asignada al primer nodo, el que queremos eliminar.

Si no guardamos el puntero al primer nodo antes de actualizar Lista, después nos resultaría imposible liberar la memoria que ocupa. Si liberamos la memoria antes de actualizar Lista, perderemos el puntero al segundo nodo.

Fuente: Propia

Si la lista sólo tiene un nodo, el proceso es también válido, ya que el valor de Lista.siguiente es NULO, y después de eliminar el primer nodo la lista quedará vacía, y el valor de Lista será NULO.

Una operación que se suele usar para borrar listas completas es eliminar el primer nodo hasta que la lista esté vacía.

3.3.2.2 Eliminar un nodo cualquiera de una lista abierta

En todos los demás casos, eliminar un nodo se puede hacer siempre del mismo modo. Supongamos que tenemos una lista con al menos dos elementos, y un puntero al nodo anterior al que queremos eliminar. Y un puntero auxiliar nodo.

El proceso es parecido al del caso anterior:

1. Hacemos que nodo apunte al nodo que queremos borrar.

2. Ahora, asignamos como nodo siguiente del nodo anterior, el siguiente al que queremos eliminar: anterior.sig = nodo.sig

3. Eliminamos la memoria asociada al nodo que queremos eliminar.

Fuente: Propia

Si el nodo a eliminar es el último, es procedimiento es igualmente válido, ya que anterior pasará a ser el último, y anterior.siguiente valdrá NULO.

Page 22: Manual de EStructura de Datos - itsa.edu.coitsa.edu.co/docs/25-E-Arrieta-Manual-de-Estructura-de-Datos.pdf · LISTAS ENLAZADAS..... 18 3.1 Definición ... Empecemos recordando que

22

Listas Enlazadas en java ejemplo.import javax.swing.*;public class p1 { static class estudiante{ String nombre;float nota;estudiante liga; }public static void main (String args[]){estudiante app=nulo,aux=nulo,aux1=nulo,aux2=nulo;byte op=0; String refn;while(op!=7){op=Byte.parseByte(JOptionPane.showInputDialog( “Digite un su opcion\n”+ “1.Crear lista\n”+ “2.Insertar al principio\n”+ “3.Insertar antes de una referencia\n”+ “4.Insertar despues de una referencia\n”+ “5.Eliminar con referencia\n”+ “6.Imprimir\n” + “7.Salir”)); switch(op){ case 1: if(app==nulo){app=new estudiante();app.nombre=JOptionPane.showInputDialog(“Ingrese el noombre del estudiante”);app.nota=Float.parseFloat(JOptionPane.showInputDialog(“Digite la nota”));app.liga=nulo; }else{JOptionPane.showMessageDialog(nulo, “La lista ya ha sido creada”);} break; case 2: if(app!=nulo){aux=new estudiante();aux.nombre=JOptionPane.showInputDialog(“Ingrese el noombre del estudiante”);aux.nota=Float.parseFloat(JOptionPane.showInputDialog(“Digite la nota”));aux.liga=app;app=aux; }else{JOptionPane.showMessageDialog(nulo, “La lista no ha sido creada”);} break; case 3: if (app!=nulo){ refn=JOptionPane.showInputDialog(“ingrese en nombre del estudiantes”); aux=app; while(!aux.nombre.equals(refn)){ aux2=aux; aux=aux.liga;} aux1=new estudiante(); aux1.nombre=JOptionPane.showInputDialog(“ingrese en nombre del estudiantes”); aux1.nota=Float.parseFloat(JOptionPane.showInputDialog(“Digite la nota”)); }else{JOptionPane.showMessageDialog(nulo, “La lista no ha sido creada”);}

Page 23: Manual de EStructura de Datos - itsa.edu.coitsa.edu.co/docs/25-E-Arrieta-Manual-de-Estructura-de-Datos.pdf · LISTAS ENLAZADAS..... 18 3.1 Definición ... Empecemos recordando que

23

break; case 4: if(app!=nulo){aux=app;refn=JOptionPane.showInputDialog(“Ingrese el nombre del estudiante”);while(!aux.nombre.equals(refn)){aux=aux.liga; } aux1= new estudiante();app.nombre=JOptionPane.showInputDialog(“Ingrese el noombre del estudiante”);app.nota=Float.parseFloat(JOptionPane.showInputDialog(“Digite la nota”)); aux1.liga=aux.liga;aux.liga=aux1; }else{JOptionPane.showMessageDialog(nulo, “La lista no ha sido creada”);} break; case 5: if(app==nulo){aux=app;refn=JOptionPane.showInputDialog(“Ingrese el nombre del estudiante”);while(!aux.nombre.equals(refn)){aux2=aux;aux=aux.liga; } aux2.liga=aux.liga;aux=nulo;} break; case 6: aux=app; while(aux.liga!=nulo){ JOptionPane.showMessageDialog(nulo, aux.nombre+” “+ aux.nota); aux=aux.liga; } break; case 7:JOptionPane.showMessageDialog(nulo, “Gracias por utilizar el programa”);break; } } } }

Page 24: Manual de EStructura de Datos - itsa.edu.coitsa.edu.co/docs/25-E-Arrieta-Manual-de-Estructura-de-Datos.pdf · LISTAS ENLAZADAS..... 18 3.1 Definición ... Empecemos recordando que

24

3.4 LISTAS DOBLEMENTE ENLADAS (Listas dobles)Las listas doblemente enlazadas son estructuras de datos similares a las listas simples. La asignación de memoria es hecha al momento de la ejecución, la diferencia radica en que el enlace entre los elementos se hace con dos apuntadores (uno que apunta hacia el nodo anterior y otro apunta hacia el siguiente nodo).

Fuente: Propia

El puntero anterior del primer elemento debe apuntar hacia NULO. El puntero siguiente del último elemento debe apuntar hacia NULO. Para acceder a un elemento, la lista puede ser recorrida en ambos sentidos:· comenzando por el inicio, el apuntador siguiente permite el desplazamiento hacia el próximo

elemento.· comenzando por el final, el apuntador anterior permite el desplazamiento hacia el elemento anterior.

El nodo típico para construir listas dobles tiene esta forma:struct nodo { int dato; nodo *siguiente; nodo *anterior;}

Para controlar la lista se conservan algunos elementos: el primer elemento, el último elemento y posiblemente el número de elementos. Para ello, se pueden utilizar dos punteros principales:El puntero inicio contendrá la dirección del primer elemento de la lista. El puntero fin contendrá la dirección del último elemento de la lista. Cualquiera que sea la posición en la lista, los punteros inicio y fin apuntan siempre al primer y último elemento.

3.4.1 Operaciones básicas con listas doblemente enlazadas

Las operaciones con listas doblemente enlazas son muy similares a las de listas simples, por lo tanto solo se indicara los pasos a seguir en cada una de ellas.

3.4.1.1 Insertar un elemento en una lista doble

Antes de cualquier otra operación sobre la lista, se inicializa el puntero inicio y el puntero fin con el valor NULO. Para agregar un elemento a la lista hay varias situaciones:1. Inserción en una lista vacía2. Inserción al inicio de la lista3. Inserción al final de la lista4. Inserción antes de un elemento5. Inserción después de un elemento

Page 25: Manual de EStructura de Datos - itsa.edu.coitsa.edu.co/docs/25-E-Arrieta-Manual-de-Estructura-de-Datos.pdf · LISTAS ENLAZADAS..... 18 3.1 Definición ... Empecemos recordando que

25

3.4.1.1 Inserción en una lista vacía

Pasos:· Asignación de memoria para el nuevo elemento· Rellenar el campo de datos del nuevo elemento· El puntero anterior del nuevo elemento apuntará hacia NULO (ya que la inserción es hecha en una

lista vacía utilizamos la dirección del puntero inicio que vale NULO)· El puntero siguiente del nuevo elemento apuntará hacia NULO (ya que la inserción es hecha en una

lista vacía se utiliza la dirección del puntero fin que vale NULO)· Los punteros inicio y fin apuntaran hacia el nuevo elemento· El tamaño es actualizado

3.4.1.2 Inserción al inicio de la lista

Pasos:

· Asignación de memoria al nuevo elemento· Rellenar el campo de datos del nuevo elemento· El puntero anterior del nuevo elemento apunta hacia NULO· El puntero siguiente del nuevo elemento apunta hacia el 1er elemento· El puntero anterior del 1er elemento apunta hacia el nuevo elemento· El puntero inicio apunta hacia el nuevo elemento· El puntero fin no cambia· El tamaño es incrementado

3.4.1.3 Inserción al final de la lista

Pasos:· Asignación de memoria al nuevo elemento· Rellenar el campo de datos del nuevo elemento· El puntero siguiente del nuevo elemento apunta hacia NULO· El puntero anterior del nuevo elemento apunta hacia el último elemento (es el puntero fin que

contiene por ahora su dirección)· El puntero siguiente del último elemento apuntará hacia el nuevo elemento· El puntero fin apunta hacia el nuevo elemento· El puntero inicio no cambia

3.4.1.4 Inserción antes de un elemento de la lista

La inserción se efectuara antes de cierta posición pasado como argumento a la función. La posición indicada no debe ser ni el primer ni el último elemento. En ese caso hay que utilizar las funciones de inserción al inicio y/o al final de la lista.

Page 26: Manual de EStructura de Datos - itsa.edu.coitsa.edu.co/docs/25-E-Arrieta-Manual-de-Estructura-de-Datos.pdf · LISTAS ENLAZADAS..... 18 3.1 Definición ... Empecemos recordando que

26

Pasos:· Asignación de memoria al nuevo elemento· Rellenar el campo de datos del nuevo elemento· Elegir una posición en la lista (la inserción se hará después de la posición elegida)· El puntero siguiente del nuevo elemento apunta hacia el elemento actual.· El puntero anterior del nuevo elemento apunta hacia la dirección hacia la que apunta el puntero

anterior del elemento actual.· El puntero siguiente del elemento que precede al elemento actual apuntará hacia el nuevo elemento· El puntero anterior del elemento actual apunta hacia el nuevo elemento· El puntero fin no cambia· El puntero inicio cambia si la posición elegida es la posición 1

3.4.1.5 Inserción después de un elemento de la lista

La posición indicada no debe ser el último elemento. En ese caso hay que utilizar la función de inserción al final de la lista.

Pasos:· Asignación de memoria al nuevo elemento· Rellenar el campo de datos del nuevo elemento· Elegir una posición en la lista (la inserción se hará después de la posición elegida)· El puntero siguiente del nuevo elemento apunta hacia la dirección hacia la que apunta el puntero

siguiente del elemento actual · El puntero anterior del nuevo elemento apunta hacia el elemento actual.· El puntero anterior del elemento que va después del elemento actual apuntará hacia el nuevo

elemento.· El puntero siguiente del elemento actual apunta hacia el nuevo elemento· El puntero inicio no cambia· El puntero fin cambia si la posición elegida es la posición del último elemento (el tamaño de la lista)

3.4.2. Eliminación de un elemento de la lista En comparación a la lista enlazada simple en el que la eliminación solo puede ser hecha después que un elemento ha sido designado, las listas doblemente enlazadas son más flexibles gracias a los 2 punteros que permiten guardar la ubicación de la lista, tanto hacia atrás como hacia delante.Para eliminar un elemento de la lista hay varias situaciones:

· 1. Eliminación al inicio de la lista· 2. Eliminación al final de la lista· 3. Eliminación antes de un elemento· 4. Eliminación después de un elemento· 5 Eliminación de un elemento

Page 27: Manual de EStructura de Datos - itsa.edu.coitsa.edu.co/docs/25-E-Arrieta-Manual-de-Estructura-de-Datos.pdf · LISTAS ENLAZADAS..... 18 3.1 Definición ... Empecemos recordando que

27

En el caso de listas doblemente enlazadas la eliminación en cualquier posición no presenta ningún problema gracias a los punteros anterior y siguiente, que permiten conservar el enlace entre los elementos de la lista.

· Si deseamos eliminar el elemento al inicio de la lista será el elemento apuntado por inicio.· Si deseamos eliminar el elemento al inicio de la lista será el elemento apuntado por final.· Si deseamos eliminar cualquier elemento entonces elegimos su posición en la lista.

3.4.2.1 Eliminación en la lista doble del primer elemento

Pasos:

· El puntero inicio contendrá la dirección contenida por el puntero siguiente del primer elemento que deseamos eliminar (si este puntero vale NULO entonces actualizamos el puntero fin ya que estamos en el caso de una lista con un solo elemento, si no hacemos apuntar el puntero anterior del segundo elemento hacia NULO).

3.4.2.2 Eliminación en la lista doble del último elemento

Pasos:

· El puntero fin contendrá la dirección del último elemento.· Hacemos apuntar al puntero siguiente del penúltimo elemento (es el elemento hacia el que

apunta el puntero anterior del último elemento), hacia NULO.· Actualizamos el puntero fin.

3.4.2.3 Eliminación en la lista doble de cualquier elemento

Pasos:

· El puntero sup_elemento contendrá la dirección del elemento a eliminar.· El puntero siguiente del elemento que antecede al elemento a eliminar apunta hacia la dirección

contenida en el puntero siguiente del elemento a eliminar.· El puntero anterior del elemento que va después del elemento a eliminar apunta hacia la dirección

contenida en el puntero anterior del elemento a eliminar.

Page 28: Manual de EStructura de Datos - itsa.edu.coitsa.edu.co/docs/25-E-Arrieta-Manual-de-Estructura-de-Datos.pdf · LISTAS ENLAZADAS..... 18 3.1 Definición ... Empecemos recordando que

28

3.5 Ejercicios con Listas enlazadas

1. Implementar una lista enlazada de datos enteros con las funciones básicas2. Implementar una lista enlazada de datos enteros que calcule el mayor de los datos e indique la posición en que se encuentre.3. Implementar una lista enlazada de datos enteros que calcule el dato mínimo y cuente la cantidad de veces que se repita.4. Implementar una lista enlazada de datos enteros que sume los datos de una lista recursivamente.5. Implementar una lista enlazada de datos enteros que sume los datos pares de una lisa recursivamente.6. Implementar una lista enlazada de datos enteros que visualice los datos pares de la lista recursivamente.7. Implementar una lista enlazada de datos enteros que muestre los números primos.8. Implementar una lista enlazada de datos enteros que ordene los datos de la lista.9. Implementar una lista enlazada de datos enteros que verifique si esta ordenada.10. Implementar una lista enlazada de datos enteros que la invierta.11. Implementar una lista enlazada de datos enteros que busque un dato de la lista recursivamente.12. Implementar una lista enlazada de datos enteros que elimine un dato recursivamente.13. Implementar una lista enlazada de datos enteros que duplique los datos de la lista y los almacene en otra lista

4. Recursividad4.1 Concepto de recursividad.

• La recursividad es una técnica de programación muy potente que puede ser utilizada en lugar de la iteración.• Permite diseñar algoritmos recursivos que dan soluciones elegantes y simples, y generalmente bien estructuradas y modulares, a problemas de gran complejidad.Es una técnica que nos permite que un algoritmo se invoque a sí mismo para resolver una versión más pequeña del problema original que le fue encomendado

public void f_rec(...)…{…f_rec(…)…}

El caso es que las definiciones recursivas aparecen con frecuencia en matemáticas, e incluso en la vida real. Un ejemplo: basta con apuntar una cámara al monitor que muestra la imagen que muestra esa cámara. El efecto es verdaderamente curioso, en especial cuando se mueve la cámara alrededor del monitor.En matemáticas, tenemos múltiples definiciones recursivas:- Números naturales: (1) 1 es número natural. (2) el siguiente número de un número natural es un número natural

Page 29: Manual de EStructura de Datos - itsa.edu.coitsa.edu.co/docs/25-E-Arrieta-Manual-de-Estructura-de-Datos.pdf · LISTAS ENLAZADAS..... 18 3.1 Definición ... Empecemos recordando que

29

- El factorial: n!, de un número natural (incluido el 0): (1) si n = 0 entonces: 0! = 1 (2) si n > 0 entonces: n! = n · (n-1)!

Asimismo, puede definirse un programa en términos recursivos, como una serie de pasos básicos, o paso base (también conocido como condición de parada), y un paso recursivo, donde vuelve a llamarse al programa. En una computadora, esta serie de pasos recursivos debe ser finita, terminando con un paso base. Es decir, a cada paso recursivo se reduce el número de pasos que hay que dar para terminar, llegando un momento en el que no se verifica la condición de paso a la recursividad. Ni el paso base ni el paso recursivo son necesariamente únicos.Por otra parte, la recursividad también puede ser indirecta, si tenemos un procedimiento P que llama a otro Q y éste a su vez llama a P. También en estos casos debe haber una condición de parada.

4.2 Ejemplos

a. Factorial.En matemáticas es frecuente definir un concepto en función del proceso usado para generarlo. Por ejemplo, una descripción matemática de n! es:n! = 1 si n = 0n⋅(n-1) ⋅....⋅1 si n > 0También se puede definir de forma más sencilla:n! = 1 si n = 0n⋅(n-1)! si n > 0Esta segunda definición es recursiva, puesto que se expresa un método factorial en términos de sí mismo. A partir de esta definición, es fácil obtener un algoritmo recursivo para calcular el factorial de un número natural n.public static int factorialR(int n) {int fact;if (n==0) fact=1;elsefact=n*factorial(n-1);return fact ;}

De esto se pueden sacar varias conclusiones:

1. El método se invoca a sí mismo (esto es lo que lo convierte en recursivo).2. Cada llamada recursiva se hace con un parámetro de menor valor que el de la anterior llamada.

Así, cada vez se está invocando a otro problema idéntico pero de menor tamaño.3. Existe un caso degenerado en el que se actúa de forma diferente, esto es, ya no se utiliza la

recursividad. Lo importante es que la forma en la que el tamaño del problema disminuye asegura que se llegará a este caso degenerado o caso base. Esto es necesario, porque de lo contrario se estaría ejecutando indefinidamente.

Page 30: Manual de EStructura de Datos - itsa.edu.coitsa.edu.co/docs/25-E-Arrieta-Manual-de-Estructura-de-Datos.pdf · LISTAS ENLAZADAS..... 18 3.1 Definición ... Empecemos recordando que

30

• Para determinar si un método recursivo está bien diseñado se utiliza el método de las tres preguntas:

1. La pregunta Caso-Base: Existe una salida no recursiva o caso base del y éste funciona correctamente para él?2. La pregunta Invocar más pequeño Cada llamada recursiva se refiere a un caso más pequeño del problema original?3. La pregunta Caso-General. Suponiendo que la(s) llamada(s) recursiva(s) funciona(n) correctamente, así como el caso base, funciona correctamente todo el método?.

Si por ejemplo, le aplicamos esto al método factorial(n), podemos comprobar cómo las respuestas a las 3 preguntas son afirmativas, con lo que podemos deducir que el algoritmo recursivo está bien diseñado.

b. Multiplicación de conejos.

Supongamos que partimos de una pareja de conejos recién nacidos, y queremos calcular cuántas parejas de conejos forman la familia al cabo de n meses si:

1. Los conejos nunca mueren.2. Un conejo puede reproducirse al comienzo del tercer mes de vida.3. Los conejos siempre nacen en parejas macho-hembra. Al comienzo de cada mes, cada pareja macho-hembra, sexualmente madura, se reproduce en exactamente un par de conejos macho-hembra. Para una n pequeña, por ejemplo 6, la solución se puede obtener fácilmente a mano:

Mes 1: 1 pareja, la inicial.Mes 2: 1 pareja, ya que todavía no es sexualmente madura.Mes 3: 2 parejas; la original y una pareja de hijos suyos.Mes 4: 3 parejas; la original, una pareja de hijos suyos nacidos ahora y la pareja de hijos suyos nacidos en el mes anterior.Mes 5: 5 parejas; la original, una pareja de hijos suyos nacidos ahora, las dos parejas nacidas en los meses 3 y 4, y una pareja de hijos de la pareja nacida en el mes 3.Mes 6: 8 parejas; las 5 del mes anterior, una pareja de hijos de la original, una pareja de hijos de la nacida en el mes 3 y una pareja de hijos nacida en el mes 4.

Si deseamos saber el número de parejas al cabo de n meses, para un n cualquiera, podemos construir un algoritmo recursivo fácilmente a partir de la siguiente relación:Parejas(n) = 1 si n <= 2Parejas(n-1)+Parejas(n-2) si n > 2

En esta relación Parejas(n-1) son las parejas vivas en el mes n-1, y Parejas(n-2) son las parejas que nacen en el mes n a partir de las que había en el mes n-2.

La serie de números Parejas (1), Parejas (2), Parejas (3),... es conocida como la Serie de Fibonacci, la cual modela muchos fenómenos naturales.

Page 31: Manual de EStructura de Datos - itsa.edu.coitsa.edu.co/docs/25-E-Arrieta-Manual-de-Estructura-de-Datos.pdf · LISTAS ENLAZADAS..... 18 3.1 Definición ... Empecemos recordando que

31

La función recursiva quedaría:

public static int parejas(int n){ int par;if (n<=2) par=1;else par=parejas(n-1)+parejas(n-2);return par;}

c. Recursión indirecta.

A continuación se expone un ejemplo de programa que utiliza recursión indirecta, y nos dice si un número es par o impar. Al igual que el programa anterior, hay otro método mucho más sencillo de determinar si un número es par o impar, basta con determinar el resto de la división entre dos. Por ejemplo: si hacemos par(2) devuelve (cierto). Si hacemos impar(4) devuelve (falso). public static boolean par(int n){ if (n == 0) return true; return impar(n-1); }

public static boolean impar(int n){ if (n == 0) return false; return par(n-1); }

Ejemplo: Si hacemos la llamada impar(3) hace las siguientes llamadas:par(2)impar(1)par(0) . devuelve 1 (cierto)

Por lo tanto 3 es un número impar.

Qué pasa si se hace una llamada recursiva que no termina?

Cada llamada recursiva almacena los parámetros que se pasaron al procedimiento, y otras variables necesarias para el correcto funcionamiento del programa. Por tanto si se produce una llamada recursiva infinita, esto es, que no termina nunca, llega un momento en el que no quedará memoria para almacenar más datos, y en ese momento se abortará la ejecución del programa. Para probar esto se puede intentar hacer esta llamada en el programa factorial definido anteriormente:

factorialR(-1);Por supuesto no hay que pasar parámetros a una función que estén fuera de su dominio, pues el factorialR está definido solamente para números naturales, pero es un ejemplo claro.

Page 32: Manual de EStructura de Datos - itsa.edu.coitsa.edu.co/docs/25-E-Arrieta-Manual-de-Estructura-de-Datos.pdf · LISTAS ENLAZADAS..... 18 3.1 Definición ... Empecemos recordando que

32

4.3 Uso de la recursividad.Se evidenció que la recursión es una técnica potente de programación para resolver, mediante soluciones simples y claras, problemas de incluso gran complejidad. Sin embargo, la recursión también tiene algunas desventajas desde el punto de vista de la eficiencia. Muchas veces un algoritmo iterativo es más eficiente que su correspondiente recursivo. Existen dos factores que contribuyen a ello

a. La sobrecarga asociada con las llamadas a los métodos.• Este factor es propio de los métodos en general, aunque es mayor con la recursividad, ya que una simple llamada inicial a un método puede generar un gran número de llamadas recursivas.• Aunque el uso de la recursión, como herramienta de modularidad, puede clarificar programas complejos que compensaría la sobrecarga adicional, no debemos usar la recursividad por el gusto de usarla. Por ejemplo, el método factorialR presentada antes no debería usarse en la práctica.Se puede fácilmente escribir una función iterativa Factorial tan clara como la recursiva y sin la sobrecarga de ésta.public int factorial(int n){int cont, fact;fact=1;for (cont=2; cont<=n; cont++)fact=fact*cont;return fact;}Podemos encontrar algunas diferencias interesantes entre FactorialR y FactorialI, que se presentarán por lo general entre métodos recursivos e iterativos.

• El método iterativo utiliza una construcción cíclica (for, while,…) para controlar la ejecución, mientras que la solución recursiva utiliza una estructura de selección.• La versión iterativa necesita un par de variables locales, mientras que la versión recursiva sólo una.

b. La ineficiencia inherente de algunos métodos recursivos.

Esta ineficiencia es debida al método empleado para resolver el problema concreto que se esté abordando.

Por ejemplo, en el método presentado para resolver el problema de la “multiplicación de conejos”, apreciamos un gran inconveniente: los mismos valores son calculados varias veces. Por ejemplo, para calcular Parejas R(7), tenemos que calcular ParejasR(3) cinco veces. Mientras más grande sea n, mayor ineficiencia.Se puede construir una solución iterativa basada en la misma relación, que actúa hacia delante en lugar de hacia atrás, y calcula cada valor sólo una vez.

Page 33: Manual de EStructura de Datos - itsa.edu.coitsa.edu.co/docs/25-E-Arrieta-Manual-de-Estructura-de-Datos.pdf · LISTAS ENLAZADAS..... 18 3.1 Definición ... Empecemos recordando que

33

public int parejasII(int n){int R, R1, R2, i;R1=1;R2=1;R=1;for (i=3; i<=n; i++){R=R1+R2;R2=R1;R1=R;}return R;}

El hecho de que la recursividad tenga estas dos desventajas, no quiere decir que sea una mala técnica y que no se deba utilizar, sino que hay que usarla cuando realmente sea necesaria:

El verdadero valor de la recursividad es como herramienta para resolver problemas para los que no hay soluciones iterativas simples.

¿Cuándo utilizar la recursión?

No se debe utilizar cuando la solución iterativa sea clara a simple vista. Sin embargo, en otros casos, obtener una solución iterativa es mucho más complicado que una solución recursiva, y es entonces cuando se puede plantear la duda de si merece la pena transformar la solución recursiva en otra iterativa. En realidad la recursión se basa en almacenar en una pila los valores de las variables locales que haya para un procedimiento en cada llamada recursiva. Esto reduce la claridad del programa. Aun así, hay que considerar que el compilador transformará la solución recursiva en una iterativa, utilizando una pila, para cuando compile al código de la computadora.

Por otra parte, casi todos los esquemas de vuelta atrás y divide y vencerás son recursivos, pues de alguna manera parece mucho más natural una solución recursiva.

En general mucho más sencillo escribir un programa recursivo que su equivalente iterativo.

Page 34: Manual de EStructura de Datos - itsa.edu.coitsa.edu.co/docs/25-E-Arrieta-Manual-de-Estructura-de-Datos.pdf · LISTAS ENLAZADAS..... 18 3.1 Definición ... Empecemos recordando que

34

4.4 Ejercicios1. Escriba una definición recursiva de una función que tiene un parámetro n de tipo entero y que devuelve el n-ésimo número de Fibonacci. Los números de Fibonacci se definen de la siguiente manera:F0 = 1F1 = 1Fi+2 = Fi + Fi+1

2. La forma para calcular cuantas maneras diferentes tengo para elegir r cosas distintas de un conjunto de n cosas es:C(n,r) = n! (r!*(n-r)!)Donde la función factorial se define comon! = n *(n-1)*(n-2)*…*2*1Descubra una versión recursiva de la fórmula anterior y escriba una función recursiva que calcule el valor de dicha fórmula.

3. Escriba una función recursiva que ordene de menor a mayor un arreglo de enteros basándose en la siguiente idea: coloque el elemento más pequeño en la primera ubicación, y luego ordene el resto del arreglo con una llamada recursiva.

4. Escribir una función recursiva que devuelva la suma de los primeros N enteros

5. Escribir un programa que encuentre la suma de los enteros positivos pares desde N hasta 2. Chequear que si N es impar se imprima un mensaje de error.

6. Escribir un programa que calcule el máximo común divisor (MCD) de dos enteros positivos. Si M >= N una función recursiva para MCD esMCD = M si N =0MCD = MCD (N, M mod N) si N <> 0El programa le debe permitir al usuario ingresar los valores para M y N desde la consola. Una función recursiva es entonces llamada para calcular el MCD. El programa entonces imprime el valor para el MCD. Si el usuario ingresa un valor para M que es < que N el programa es responsable de switchear los valores.7. Programe un método recursivo que transforme un número entero positivo a notación binaria.

8. Programe un método recursivo que transforme un número expresado en notación binaria a un número entero.

9. Programe un método recursivo que calcule la suma de un arreglo de números enteros.

10. Programe un método recursivo que invierta los números de un arreglo de enteros.11. Cuál es el resultado de esta función para distintos valores de X?Static int f(int x){ if (x >100) { return (x-10);

}else{ return(f(f(x+11)));}

}12. Implemente una función recursiva que nos diga si una cadena es palíndromo.

Page 35: Manual de EStructura de Datos - itsa.edu.coitsa.edu.co/docs/25-E-Arrieta-Manual-de-Estructura-de-Datos.pdf · LISTAS ENLAZADAS..... 18 3.1 Definición ... Empecemos recordando que

35

5. Árboles5.1 Definición

Un árbol es una estructura no lineal en la que cada nodo puede apuntar a uno o varios nodos.

También se suele dar una definición recursiva: un árbol es una estructura en compuesta por un dato y varios árboles.

Esto son definiciones simples. Pero las características que implican no lo son tanto.

Fuente: Propia

Definiremos varios conceptos. En relación con otros nodos:

· Nodo hijo: cualquiera de los nodos apuntados por uno de los nodos del árbol. En el ejemplo, ‘L’ y ‘M’ son hijos de ‘G’.

· Nodo padre: nodo que contiene un puntero al nodo actual. En el ejemplo, el nodo ‘A’ es padre de ‘B’, ‘C’ y ‘D’.

Los árboles con los que trabajaremos tienen otra característica importante: cada nodo sólo puede ser apuntado por otro nodo, es decir, cada nodo sólo tendrá un padre. Esto hace que estos árboles estén fuertemente jerarquizados, y es lo que en realidad les da la apariencia de árboles.

En cuanto a la posición dentro del árbol:

· Nodo raíz: nodo que no tiene padre. Este es el nodo que usaremos para referirnos al árbol. En el ejemplo, ese nodo es el ‘A’.

· Nodo hoja: nodo que no tiene hijos. En el ejemplo hay varios: ‘F’, ‘H’, ‘I’, ‘K’, ‘L’, ‘M’, ‘N’ y ‘O’.

· Nodo rama: aunque esta definición apenas la usaremos, estos son los nodos que no pertenecen a ninguna de las dos categorías anteriores. En el ejemplo: ‘B’, ‘C’, ‘D’, ‘E’, ‘G’ y ‘J’.

Otra característica que normalmente tendrán nuestros árboles es que todos los nodos contengan el mismo número de punteros, es decir, usaremos la misma estructura para todos los nodos del árbol. Esto hace que la estructura sea más sencilla, y por lo tanto también los programas para trabajar con ellos.

Tampoco es necesario que todos los nodos hijos de un nodo concreto existan. Es decir, que pueden usarse todos, algunos o ninguno de los punteros de cada nodo.

Page 36: Manual de EStructura de Datos - itsa.edu.coitsa.edu.co/docs/25-E-Arrieta-Manual-de-Estructura-de-Datos.pdf · LISTAS ENLAZADAS..... 18 3.1 Definición ... Empecemos recordando que

36

Un árbol en el que en cada nodo o bien todos o ninguno de los hijos existe, se llama árbol completo.

En una cosa, los árboles se parecen al resto de las estructuras que hemos visto: dado un nodo cualquiera de la estructura, podemos considerarlo como una estructura independiente. Es decir, un nodo cualquiera puede ser considerado como la raíz de un árbol completo.

Existen otros conceptos que definen las características del árbol, en relación a su tamaño:

· Orden: es el número potencial de hijos que puede tener cada elemento de árbol. De este modo, diremos que un árbol en el que cada nodo puede apuntar a otros dos es de orden dos, si puede apuntar a tres será de orden tres, etc.

· Grado: el número de hijos que tiene el elemento con más hijos dentro del árbol. En el árbol del ejemplo, el grado es tres, ya que tanto ‘A’ como ‘D’ tienen tres hijos, y no existen elementos con más de tres hijos.

· Nivel: se define para cada elemento del árbol como la distancia a la raíz, medida en nodos. El nivel de la raíz es cero y el de sus hijos uno. Así sucesivamente. En el ejemplo, el nodo ‘D’ tiene nivel 1, el nodo ‘G’ tiene nivel 2, y el nodo ‘N’, nivel 3.

· Altura: la altura de un árbol se define como el nivel del nodo de mayor nivel. Como cada nodo de un árbol puede considerarse a su vez como la raíz de un árbol, también podemos hablar de altura de ramas. El árbol del ejemplo tiene altura 3, la rama ‹B› tiene altura 2, la rama ‹G› tiene altura 1, la ‹H› cero, etc.

Los árboles de orden dos son bastante especiales, de hecho les dedicaremos varios capítulos. Estos árboles se conocen también como árboles binarios.

Frecuentemente, aunque tampoco es estrictamente necesario, para hacer más fácil moverse a través del árbol, agregaremos un puntero a cada nodo que apunte al nodo padre. De este modo podremos avanzar en dirección a la raíz, y no sólo hacia las hojas.

Es importante conservar siempre el nodo raíz ya que es el nodo a partir del cual se desarrolla el árbol, si perdemos este nodo, perderemos el acceso a todo el árbol.

El nodo típico de un árbol difiere de los nodos que hemos visto hasta ahora para listas, aunque sólo en el número de nodos. Veamos un ejemplo de nodo para crear árboles de orden tres:

struct nodo { int dato; struct nodo *rama1; struct nodo *rama2; struct nodo *rama3;}

5.2 Operaciones básicas con árboles

Salvo que trabajemos con árboles especiales, como los que veremos más adelante, las inserciones serán siempre en punteros de nodos hoja o en punteros libres de nodos rama. Con estas estructuras no es tan fácil generalizar, ya que existen muchas variedades de árboles.

Page 37: Manual de EStructura de Datos - itsa.edu.coitsa.edu.co/docs/25-E-Arrieta-Manual-de-Estructura-de-Datos.pdf · LISTAS ENLAZADAS..... 18 3.1 Definición ... Empecemos recordando que

37

De nuevo tenemos casi el mismo repertorio de operaciones de las que disponíamos con las listas:· Agregar o insertar elementos.· Buscar o localizar elementos.· Borrar elementos.· Moverse a través del árbol.· Recorrer el árbol completo.

Los algoritmos de inserción y borrado dependen en gran medida del tipo de árbol que estemos implementando, de modo que por ahora los pasaremos por alto y nos centraremos más en el modo de recorrer árboles.

5.3 Recorridos por árboles

El modo evidente de moverse a través de las ramas de un árbol es siguiendo los punteros, del mismo modo en que nos movíamos a través de las listas.Esos recorridos dependen en gran medida del tipo y propósito del árbol, pero hay ciertos recorridos que usaremos frecuentemente. Se trata de aquellos recorridos que incluyen todo el árbol.Hay tres formas de recorrer un árbol completo, y las tres se suelen implementar mediante recursividad. En los tres casos se sigue siempre a partir de cada nodo todas las ramas una por una.Supongamos que tenemos un árbol de orden tres, y queremos recorrerlo por completo.Partiremos del nodo raíz:

RecorrerArbol(raiz);La función RecorrerArbol, aplicando recursividad, será tan sencilla como invocar de nuevo a la función RecorrerArbol para cada una de las ramas:void RecorrerArbol(Arbol a) { if(a == NULO) return; RecorrerArbol(a.rama[0]); RecorrerArbol(a.rama[1]); RecorrerArbol(a.rama[2]);}

Lo que diferencia los distintos métodos de recorrer el árbol no es el sistema de hacerlo, sino el momento que elegimos para procesar el valor de cada nodo con relación a los recorridos de cada una de las ramas.Los tres tipos son: Pre-orden, In-orden y Post-orden.

5.3.1 Pre-orden

En este tipo de recorrido, el valor del nodo se procesa antes de recorrer las ramas:void PreOrden(Arbol a) { if(a == NULO) return; Procesar(dato); RecorrerArbol(a.rama[0]); RecorrerArbol(a.rama[1]); RecorrerArbol(a.rama[2]);}Si seguimos el árbol del ejemplo en pre-orden, y el proceso de los datos es sencillamente mostrarlos por pantalla, obtendremos algo así:

A B E K F C G L M D H I J N O

Page 38: Manual de EStructura de Datos - itsa.edu.coitsa.edu.co/docs/25-E-Arrieta-Manual-de-Estructura-de-Datos.pdf · LISTAS ENLAZADAS..... 18 3.1 Definición ... Empecemos recordando que

38

5.3.2 In-orden

En este tipo de recorrido, el valor del nodo se procesa después de recorrer la primera rama y antes de recorrer la última. Esto tiene más sentido en el caso de árboles binarios, y también cuando existen ORDEN-1 datos, en cuyo caso procesaremos cada dato entre el recorrido de cada dos ramas (este es el caso de los árboles-b):

void InOrden(Arbol a) { if(a == NULO) return; RecorrerArbol(a.rama[0]); Procesar(dato); RecorrerArbol(a.rama[1]); RecorrerArbol(a.rama[2]);}

Si seguimos el árbol del ejemplo en in-orden, y el proceso de los datos es sencillamente mostrarlos por pantalla, obtendremos algo así:

K E B F A L G M C H D I N J O

5.3.3 Post-orden

En este tipo de recorrido, el valor del nodo se procesa después de recorrer todas las ramas:

void PostOrden(Arbol a) { if(a == NULO) return; RecorrerArbol(a.rama[0]); RecorrerArbol(a.rama[1]); RecorrerArbol(a.rama[2]); Procesar(dato);}

Si seguimos el árbol del ejemplo en post-orden, y el proceso de los datos es sencillamente mostrarlos por pantalla, obtendremos algo así:

K E F B L M G C H I N O J D A

5.4 Eliminar nodos en un árbol

El proceso general es muy sencillo en este caso, pero con una importante limitación, sólo podemos borrar nodos hoja:

El proceso sería el siguiente:

1. Buscar el nodo padre del que queremos eliminar.2. Buscar el puntero del nodo padre que apunta al nodo que queremos borrar.3. Liberar el nodo.4. padre.nodo[i] = NULO;

Page 39: Manual de EStructura de Datos - itsa.edu.coitsa.edu.co/docs/25-E-Arrieta-Manual-de-Estructura-de-Datos.pdf · LISTAS ENLAZADAS..... 18 3.1 Definición ... Empecemos recordando que

39

Cuando el nodo a borrar no sea un nodo hoja, diremos que hacemos una “poda”, y en ese caso eliminaremos el árbol cuya raíz es el nodo a borrar. Se trata de un procedimiento recursivo, aplicamos el recorrido PostOrden, y el proceso será borrar el nodo.

El procedimiento es similar al de borrado de un nodo:

1. Buscar el nodo padre del que queremos eliminar.

2. Buscar el puntero del nodo padre que apunta al nodo que queremos borrar.

3. Podar el árbol cuyo padre es nodo.

4. padre.nodo[i] = NULO;

En el árbol del ejemplo, para podar la rama ‘B’, recorreremos el subárbol ‘B’ en postorden, eliminando cada nodo cuando se procese, de este modo no perdemos los punteros a las ramas apuntadas por cada nodo, ya que esas ramas se borrarán antes de eliminar el nodo.

De modo que el orden en que se borrarán los nodos será:

K E F y B

5.5 Árboles ordenados

A partir del siguiente capítulo sólo hablaremos de árboles ordenados, ya que son los que tienen más interés desde el punto de vista de TAD, y los que tienen más aplicaciones genéricas.

Un árbol ordenado, en general, es aquel a partir del cual se puede obtener una secuencia ordenada siguiendo uno de los recorridos posibles del árbol: inorden, preorden o postorden.

En estos árboles es importante que la secuencia se mantenga ordenada aunque se añadan o se eliminen nodos.

Existen varios tipos de árboles ordenados, que veremos a continuación:

· Árboles binarios de búsqueda (ABB): son árboles de orden 2 que mantienen una secuencia ordenada si se recorren en inorden.

· Árboles AVL: son árboles binarios de búsqueda equilibrados, es decir, los niveles de cada rama para cualquier nodo no difieren en más de 1.

· Árboles perfectamente equilibrados: son árboles binarios de búsqueda en los que el número de nodos de cada rama para cualquier nodo no difieren en más de 1. Son por lo tanto árboles AVL también.

· Árboles 2-3: son árboles de orden 3, que contienen dos claves en cada nodo y que están también equilibrados. También generan secuencias ordenadas al recorrerlos en inorden.

· Árboles-B: caso general de árboles 2-3, que para un orden M, contienen M-1 claves.

Page 40: Manual de EStructura de Datos - itsa.edu.coitsa.edu.co/docs/25-E-Arrieta-Manual-de-Estructura-de-Datos.pdf · LISTAS ENLAZADAS..... 18 3.1 Definición ... Empecemos recordando que

40

5.6 Árboles binarios de búsqueda (ABB)

5.6.1 Definición

Se trata de árboles de orden 2 en los que se cumple que para cada nodo, el valor de la clave de la raíz del subárbol izquierdo es menor que el valor de la clave del nodo y que el valor de la clave raíz del subárbol derecho es mayor que el valor de la clave del nodo.

Árbol binario de búsqueda

Fuente: Propia

5.6.2 Operaciones en ABB

El repertorio de operaciones que se pueden realizar sobre un ABB es parecido al que realizábamos sobre otras estructuras de datos, más alguna otra propia de árboles:

· Buscar un elemento.

· Insertar un elemento.

· Borrar un elemento.

· Movimientos a través del árbol:o Izquierda.o Derecha.o Raíz.

· Información:

o Comprobar si un árbol está vacío.

o Calcular el número de nodos.

o Comprobar si el nodo es hoja.

o Calcular la altura de un nodo.

o Calcular la altura de un árbol.

Page 41: Manual de EStructura de Datos - itsa.edu.coitsa.edu.co/docs/25-E-Arrieta-Manual-de-Estructura-de-Datos.pdf · LISTAS ENLAZADAS..... 18 3.1 Definición ... Empecemos recordando que

41

5.6.3 Buscar un elemento

Partiendo siempre del nodo raíz, el modo de buscar un elemento se define de forma recursiva.

· Si el árbol está vacío, terminamos la búsqueda: el elemento no está en el árbol.

· Si el valor del nodo raíz es igual que el del elemento que buscamos, terminamos la búsqueda con éxito.

· Si el valor del nodo raíz es mayor que el elemento que buscamos, continuaremos la búsqueda en el árbol izquierdo.

· Si el valor del nodo raíz es menor que el elemento que buscamos, continuaremos la búsqueda en el árbol derecho.

El valor de retorno de una función de búsqueda en un ABB puede ser un puntero al nodo encontrado, o NULO, si no se ha encontrado.

5.6.4 Insertar un elemento

Para insertar un elemento nos basamos en el algoritmo de búsqueda. Si el elemento está en el árbol no lo insertaremos. Si no lo está, lo insertaremos a continuación del último nodo visitado.

Necesitamos un puntero auxiliar para conservar una referencia al padre del nodo raíz actual. El valor inicial para ese puntero es NULO.

· Padre = NULO

· nodo = Raíz

· Bucle: mientras actual no sea un árbol vacío o hasta que se encuentre el elemento.o Si el valor del nodo raíz es mayor que el elemento que buscamos, continuaremos la

búsqueda en el árbol izquierdo: Padre=nodo, nodo=nodo.izquierdo.o Si el valor del nodo raíz es menor que el elemento que buscamos, continuaremos la

búsqueda en el árbol derecho: Padre=nodo, nodo=nodo.derecho.

· Si nodo no es NULO, el elemento está en el árbol, por lo tanto salimos.

· Si Padre es NULO, el árbol estaba vacío, por lo tanto, el nuevo árbol sólo contendrá el nuevo elemento, que será la raíz del árbol.

· Si el elemento es menor que el Padre, entonces insertamos el nuevo elemento como un nuevo árbol izquierdo de Padre.

· Si el elemento es mayor que el Padre, entonces insertamos el nuevo elemento como un nuevo árbol derecho de Padre.

Este modo de actuar asegura que el árbol sigue siendo ABB.

Page 42: Manual de EStructura de Datos - itsa.edu.coitsa.edu.co/docs/25-E-Arrieta-Manual-de-Estructura-de-Datos.pdf · LISTAS ENLAZADAS..... 18 3.1 Definición ... Empecemos recordando que

42

5.6.5 Borrar un elemento

Para borrar un elemento también nos basamos en el algoritmo de búsqueda. Si el elemento no está en el árbol no lo podremos borrar. Si está, hay dos casos posibles:

1. Se trata de un nodo hoja: en ese caso lo borraremos directamente.

2. Se trata de un nodo rama: en ese caso no podemos eliminarlo, puesto que perderíamos todos los elementos del árbol de que el nodo actual es padre. En su lugar buscamos el nodo más a la izquierda del subárbol derecho, o el más a la derecha del subárbol izquierdo e intercambiamos sus valores. A continuación eliminamos el nodo hoja.

Necesitamos un puntero auxiliar para conservar una referencia al padre del nodo raíz actual. El valor inicial para ese puntero es NULO.

· Padre = NULO

· Si el árbol está vacío: el elemento no está en el árbol, por lo tanto salimos sin eliminar ningún elemento.

· (1) Si el valor del nodo raíz es igual que el del elemento que buscamos, estamos ante uno de los siguientes casos:

o El nodo raíz es un nodo hoja:§ Si ‘Padre’ es NULO, el nodo raíz es el único del árbol, por lo tanto el puntero al

árbol debe ser NULO.§ Si raíz es la rama derecha de ‘Padre’, hacemos que esa rama apunte a NULO.§ Si raíz es la rama izquierda de ‘Padre’, hacemos que esa rama apunte a NULO.§ Eliminamos el nodo, y salimos.

o El nodo no es un nodo hoja:§ Buscamos el ‘nodo’ más a la izquierda del árbol derecho de raíz o el más a la

derecha del árbol izquierdo. Hay que tener en cuenta que puede que sólo exista uno de esos árboles. Al mismo tiempo, actualizamos ‘Padre’ para que apunte al padre de ‘nodo’.

§ Intercambiamos los elementos de los nodos raíz y ‘nodo’.§ Borramos el nodo ‘nodo’. Esto significa volver a (1), ya que puede suceder que

‘nodo’ no sea un nodo hoja. (Ver ejemplo 3)

· Si el valor del nodo raíz es mayor que el elemento que buscamos, continuaremos la búsqueda en el árbol izquierdo.

· Si el valor del nodo raíz es menor que el elemento que buscamos, continuaremos la búsqueda en el árbol derecho.

Page 43: Manual de EStructura de Datos - itsa.edu.coitsa.edu.co/docs/25-E-Arrieta-Manual-de-Estructura-de-Datos.pdf · LISTAS ENLAZADAS..... 18 3.1 Definición ... Empecemos recordando que

43

Ejemplo 1: Borrar un nodo hoja

En el árbol de ejemplo, borrar el nodo 10.

1. Localizamos el nodo a borrar, al tiempo que mantenemos un puntero a ‘Padre’.

2. Hacemos que el puntero de ‘Padre’ que apuntaba a ‘nodo’, ahora apunte a NULO.

3. Borramos el ‘nodo’.

Fuente: Propia

Borrar un nodo hoja

Ejemplo 2: Borrar un nodo rama con intercambio de un nodo hoja.

En el árbol de ejemplo, borrar el nodo 4.1. Localizamos el nodo a borrar (‘raíz’).2. Buscamos el nodo más a la derecha del árbol izquierdo de ‘raíz’, en este caso el 3, al tiempo que

mantenemos un puntero a ‘Padre’ a ‘nodo’.3. Intercambiamos los elementos 3 y 4.4. Hacemos que el puntero de ‘Padre’ que apuntaba a ‘nodo’, ahora apunte a NULO.5. Borramos el ‘nodo’.

Fuente: Propia

Page 44: Manual de EStructura de Datos - itsa.edu.coitsa.edu.co/docs/25-E-Arrieta-Manual-de-Estructura-de-Datos.pdf · LISTAS ENLAZADAS..... 18 3.1 Definición ... Empecemos recordando que

44

Ejemplo 3: Borrar un nodo rama con intercambio de un nodo rama.

Para este ejemplo usaremos otro árbol. En éste borraremos el elemento 6.

Fuente: Propia

1. Localizamos el nodo a borrar (‘raíz’).2. Buscamos el nodo más a la izquierda del árbol derecho de ‘raíz’, en este caso el 12, ya que el

árbol derecho no tiene nodos a su izquierda, si optamos por la rama izquierda, estaremos en un caso análogo. Al mismo tiempo que mantenemos un puntero a ‘Padre’ a ‘nodo’.

3. Intercambiamos los elementos 6 y 12.4. Ahora tenemos que repetir el bucle para el nodo 6 de nuevo, ya que no podemos eliminarlo.

Fuente: Propia

5. Localizamos de nuevo el nodo a borrar (‘raíz’).6. Buscamos el nodo más a la izquierda del árbol derecho de ‘raíz’, en este caso el 16, al mismo

tiempo que mantenemos un puntero a ‘Padre’ a ‘nodo’.7. Intercambiamos los elementos 6 y 16.8. Hacemos que el puntero de ‘Padre’ que apuntaba a ‘nodo’, ahora apunte a NULO.9. Borramos el ‘nodo’.

Fuente: Propia

Este modo de actuar asegura que el árbol sigue siendo ABB

Page 45: Manual de EStructura de Datos - itsa.edu.coitsa.edu.co/docs/25-E-Arrieta-Manual-de-Estructura-de-Datos.pdf · LISTAS ENLAZADAS..... 18 3.1 Definición ... Empecemos recordando que

45

Árboles ABB en Java.import javax.swing.*;public class NewClass { static class nodo{ int dato; nodo ligad; nodo ligai; } public static void Insercion(int datoi,nodo aux) { nodo aux1; if(aux.dato>datoi){ if(aux.ligai==nulo){ aux1 =new nodo(); aux1.dato= datoi; aux1.ligai=nulo; aux1.ligad=nulo; aux.ligai=aux1; JOptionPane.showMessageDialog(nulo,”el dato ha sido insertado”); }else{ aux=aux.ligai; Insercion(datoi, aux); } }else{ if(aux.ligad==nulo){ aux1 =new nodo(); aux1.dato= datoi; aux1.ligai=nulo; aux1.ligad=nulo; aux.ligad=aux1; JOptionPane.showMessageDialog(nulo,”el dato ha sido insertado”); }else{ aux=aux.ligad; Insercion(datoi,aux); } } }public static void Busqueda(int datoi,nodo aux) { if(aux.dato==datoi){ JOptionPane.showMessageDialog(nulo,”el numero existe”); }else{ if(datoi<aux.dato){ if(aux.ligai==nulo){ JOptionPane.showMessageDialog(nulo, “el numero no existe”); }else{ Busqueda(datoi,aux.ligai); } }else{ if(aux.ligad==nulo){ JOptionPane.showMessageDialog(nulo, “el numero no existe”); }else{ Busqueda(datoi,aux.ligad); } } }}public static void Eliminar(int datoi, nodo aux) {

Page 46: Manual de EStructura de Datos - itsa.edu.coitsa.edu.co/docs/25-E-Arrieta-Manual-de-Estructura-de-Datos.pdf · LISTAS ENLAZADAS..... 18 3.1 Definición ... Empecemos recordando que

46

nodo aux1,aux2; aux1=aux.ligai; aux2=aux.ligad; if(aux1.dato!=datoi && aux2.dato!=datoi){ if(datoi<aux.dato){ if(aux1==nulo){ JOptionPane.showMessageDialog(nulo, “el numero no existe”); }else{ Eliminar(datoi,aux1); } }else{ if(aux2==nulo){ JOptionPane.showMessageDialog(nulo, “el numero no existe”); }else{ Eliminar(datoi,aux2); } } }else{ if(aux1.dato==datoi){ aux.ligai=nulo; aux1=nulo; JOptionPane.showMessageDialog(nulo, “la hoja ha sido eliminada”); }else{ if(aux2.dato==datoi){ aux.ligad=nulo; aux2=nulo; JOptionPane.showMessageDialog(nulo, “la hoja ha sido eliminada”); } } }}

public static void main (String args[]){ nodo raiz=nulo, aux=nulo ; byte op=0; int datoi;while(op!=5){op=Byte.parseByte(JOptionPane.showInputDialog( “Digite un su opcion\n”+ “1.Crear Arbol\n”+ “2.Insertar \n”+ “3.Busqueda \n”+ “4.Eliminar\n” + “5.Salir”));switch(op){//crear arbol case 1:{ if(raiz==nulo){ raiz=new nodo(); raiz.dato=Integer.parseInt(JOptionPane.showInputDialog(“Ingrese el dato”)); raiz.ligad=nulo; raiz.ligai=nulo; }else{ JOptionPane.showMessageDialog(nulo, “El arbol ya ha sido creado”); } }

Page 47: Manual de EStructura de Datos - itsa.edu.coitsa.edu.co/docs/25-E-Arrieta-Manual-de-Estructura-de-Datos.pdf · LISTAS ENLAZADAS..... 18 3.1 Definición ... Empecemos recordando que

47

break;//”2.Insertar case 2:{aux=raiz; datoi=Integer.parseInt(JOptionPane.showInputDialog(“inserte el dato a insertar”)); Insercion(datoi,aux); } break;//”3.Busqueda case 3:{ aux=raiz; datoi=Integer.parseInt(JOptionPane.showInputDialog(“inserte el dato a buscar”)); Busqueda(datoi,aux); } break;//”4.Eliminar case 4:{aux=raiz;datoi=Integer.parseInt(JOptionPane.showInputDialog(“inserte el dato de la hoja a eliminar”));Eliminar(datoi,aux); } break; //5.Salir case 5:{ JOptionPane.showMessageDialog(nulo, “Gracias por utilizar el programa”); break; } } } }}

Page 48: Manual de EStructura de Datos - itsa.edu.coitsa.edu.co/docs/25-E-Arrieta-Manual-de-Estructura-de-Datos.pdf · LISTAS ENLAZADAS..... 18 3.1 Definición ... Empecemos recordando que

48

5.7 EJERCICIOS

1. Determine el nivel, la altura y el grado de cada nodo.

Fuente: Propia

2. Dados los conjuntos:

A = { 25, 34, 42, 21, 23, 18, 45, 22, 9, 35 }

B = { 50, 20, 80, 10, 70, 25, 85, 62, 79, 5 }

a. Transfórmelo en ABB.

b. Elimine de A: {23, 34} y de B: {20, 70}.

3. Dado el siguiente árbol, señale su recorrido en In-Orden, Post-Orden y Pre-Orden.

Fuente: Propia

4. Dado el siguiente árbol, señale su recorrido en In-Orden, Post-Orden y Pre-Orden.

Fuente: Propia

Page 49: Manual de EStructura de Datos - itsa.edu.coitsa.edu.co/docs/25-E-Arrieta-Manual-de-Estructura-de-Datos.pdf · LISTAS ENLAZADAS..... 18 3.1 Definición ... Empecemos recordando que

49

6. Bibliografía de Referencia

· WEISS, M.A.: Data Structures and Algorithm Analysis in Java, 3rd Edition, Pearson/Addison Wesley, 2011.

· DEITEL, P.J. y DEITEl, H.M.: Java. Cómo programar (7ª edición), Prentice Hall, 2008.

· CAIRO,OSVALDO Y SILVIA, Guardati. Estructura de Datos, McGraw Hill. 2006

· HERNÁNDEZ, Z.J. y otros: Fundamentos de Estructuras de Datos. Soluciones en Ada, Java y C++, Thomson, 2005.

· MEHTA, D.P. Y SAHNI, S.: Handbook of Data Structures and Applications, Chapman & Hall/CRC, 2005.

· WEISS, M.A.: Estructuras de datos en Java, Pearson/Addison Wesley, 2000.

· FRANCH GUTIÉRREZ, X.: Estructuras de Datos. Especificación, Diseño e Implementación. 3ª edición, Ed. Edicions UPC, 2001.

· CAIRO, Guardati. (1993). Estructura de Datos, Prentice-Hall. 267 p.

· JOYANES AGUILAR, Luís. (1996). Fundamentos de programación: algoritmos y estructura datos. McGraw-Hill. 234 p.

· JOYANES, L., ZAHONERO, I., FERNÁNDEZ, M. Y SÁNCHEZ, L.: Estructura de datos. Libro de problemas, McGraw Hill, 1999.