Docsity
Docsity

Prepara tus exámenes
Prepara tus exámenes

Prepara tus exámenes y mejora tus resultados gracias a la gran cantidad de recursos disponibles en Docsity


Consigue puntos base para descargar
Consigue puntos base para descargar

Gana puntos ayudando a otros estudiantes o consíguelos activando un Plan Premium


Orientación Universidad
Orientación Universidad


Estructuras de Datos en Lenguaje Java (CCG), Apuntes de Programación Java

contiene interesantes ejemplos para aprender el lenguaje de programación java

Tipo: Apuntes

2020/2021

Subido el 01/05/2021

marco-huanca
marco-huanca 🇧🇴

3

(1)

2 documentos

1 / 37

Toggle sidebar

Esta página no es visible en la vista previa

¡No te pierdas las partes importantes!

bg1
Universidad de Santiago de Chile
Facultad de Ingeniería
Departamento de Ingeniería Industrial
Estructuras de Datos
Abstractas en Lenguaje
Java
Listas Enlazadas, Colas, Pilas y Árboles Binarios
Creado por Carlo Casorzo G. para el curso Fundamentos
de Informática Industrial
pf3
pf4
pf5
pf8
pf9
pfa
pfd
pfe
pff
pf12
pf13
pf14
pf15
pf16
pf17
pf18
pf19
pf1a
pf1b
pf1c
pf1d
pf1e
pf1f
pf20
pf21
pf22
pf23
pf24
pf25

Vista previa parcial del texto

¡Descarga Estructuras de Datos en Lenguaje Java (CCG) y más Apuntes en PDF de Programación Java solo en Docsity!

Universidad de Santiago de Chile Facultad de Ingeniería Departamento de Ingeniería Industrial

Estructuras de Datos

Abstractas en Lenguaje

Java

Listas Enlazadas, Colas, Pilas y Árboles Binarios

Creado por Carlo Casorzo G. para el curso Fundamentos

de Informática Industrial

Índice

  • Consideraciones previas
    1. Almacenamiento de datos
    • 1.1 El problema de los arreglos
      • estructuras de datos en base a punteros 1.2 Una solución: la Lista Enlazada y las
    1. Lista Enlazada Simple
    • 2.1 Qué es una lista enlazada
    • 2.2 Cómo acceder a un elemento de una lista
    • 2.3 Diseño de la clase ListaEnlazada.................................................................. - 2.3.1 Método añadir() - 2.3.2 Método encontrar() - 2.3.3 Método remover() - 2.3.4 Desafíos para el alumno
    1. Pila
    • 3.1 Qué es una pila .............................................................................................
    • 3.2 Diseño de la clase Pila .................................................................................. - 3.2.1 Método verPrimero() - 3.2.2 Método sacar() - 3.2.3 Método apilar() - 3.2.4 Desafíos para el alumno
    1. Cola
    • 4.1 Qué es una cola
    • 4.2 Diseño de la clase Cola - 4.2.1 Método enfilar() - 4.2.2 Método sacar() - 4.2.3 Desafíos para el alumno
    1. Árbol Binario
    • 5.1 Conceptos básicos
    • 5.2 Qué es un árbol binario - 5.2.1 Recorrido en árboles binarios........................................................................
    • 5.3 Diseño de la clase ArbolBinario................................................................... - 5.3.1 Método buscar() - 5.3.2 Método insertar() - 5.3.3 Método mostrarPreOrden() - 5.3.4 Método mostrarInOrden () - 5.3.5 Método mostrarPosOrden () - 5.3.6 Desafíos para el alumno
    1. La aplicación ilustrativa

1. Almacenamiento de datos

1.1 El problema de los arreglos

Los arreglos y las estructuras de datos (listas, colas, pilas, etc.) son entes informáticos abstractos que nos permiten almacenar datos, es decir, en un lenguaje de programación como Java, objetos y/o tipos primitivos ( int , double , char , boolean , etc...).

Los arreglos son, probablemente, la estructura más usada para almacenar y ordenar datos dentro de la programación, debido a que la sintaxis para acceder a sus datos es muy amigable para el programador. Por ejemplo, si tenemos un arreglo de int ’s, y queremos sumar el segundo número de un arreglo con el cuarto, simplemente usamos la notación [ ] para acceder a ellos:

O, por ejemplo, si queremos llenar un arreglo con todas las letras minúsculas, desde la a la z, simplemente hacemos:

() () ver “Conversión por cast”, capitulo 4, página 100

Y así, minúsculas sería igual a {'a', 'b', 'c', 'd',..., 'z'}. Si tratamos de representar gráficamente lo que un arreglo significa en la memoria, podríamos analizar el siguiente esquema:

Y para acceder a un objeto en el arreglo, simplemente hacemos referencia a su índice y usamos la notación [ ].

Pero esta forma de almacenamiento tiene ciertas desventajas, por ejemplo:

  1. Los arreglos tienen una capacidad de almacenamiento determinada y no modificable, lo que se transforma realmente en un problema si queremos almacenar datos sin saber cuantos almacenaremos en total, o si la cantidad de datos es variable.

  2. Por (1), los programadores utilizan arreglos “lo suficientemente grandes”, desperdiciando espacio de memoria que nunca se utiliza. Por ejemplo, si queremos registrar a los clientes que compraron en nuestra tienda cada día, podríamos crear un arreglo de una capacidad estimada de 100 espacios para cada día. Pero en el caso que en una ocasión sólo entraran a comprar 22 clientes, 78 espacios del arreglo son desperdiciados, y, por lo tanto, espacio en la memoria es desperdiciado. O peor aún, que entraran 150 personas a comprar y que nuestro arreglo no fuera capaz de almacenar a todos los clientes. En estos casos, los arreglos no son la herramienta indicada.

  3. La inserción de un objeto al principio del arreglo, sin sobrescribir el primer espacio, se torna mas complicada, pues debemos correr en un espacio a la derecha a todo el resto de los datos. Es decir, los arreglos no manejan los datos de forma dinámica.

Para arreglar este problema, haremos uso de una nueva forma de almacenamientos: las estructuras datos basadas en punteros, con su principal exponente, la Lista Enlazada.

1.2 Una solución: la Lista Enlazada y

estructuras de datos en base a punteros

Para entender este tipo de estructuras es necesario tener claro algunos conceptos:

puntero/apuntado: un puntero es un espacio en la memoria que almacena una referencia o dirección de memoria de otra variable, conocida como su apuntado. El valor de un puntero puede ser null, lo que significa actualmente no se refiere a ningún apuntado.

referencia: la operación de referencia en un puntero sirve para tener acceso a su apuntado.

Asignación de puntero: una operación de asignación entre dos punteros, como p=q , hace que los dos punteros se refieran (o apunten) al mismo apuntado. No se copia dos veces en la memoria al apuntado, sino que los dos punteros almacenan la dirección de memoria del apuntado.

Ahora bien, para solucionar el problema encontrado al usar arreglos en ese tipo de situaciones, introduciremos un nuevo tipo de estructura de almacenamiento: Las Estructuras de Datos en Base a Punteros.

2. Lista Enlazada Simple

2.1 Qué es una lista enlazada

La Lista Enlazada Simple es la más fundamental estructura de datos basada en punteros, y del concepto fundamental de ésta derivan las otras estructuras de datos.

Para solucionar un problema como el presentado anteriormente, necesitamos una estructura que, al contrario de los arreglos, sea capaz de modificar su capacidad, es decir, que maneje los datos de forma dinámica. Para lograr esto, nace la idea de lista enlazada.

Un arreglo asigna memoria para todos sus elementos ordenados como un sólo bloque. En cambio, la lista enlazada asigna espacio para cada elemento por separado, en su propio bloque de memoria, llamado nodo. La lista conecta estos nodos usando punteros, formando una estructura parecida a la de una cadena.

Un nodo es un objeto como cualquier otro, y sus atributos serán los encargados de hacer el trabajo de almacenar y apuntar a otro nodo. Cada nodo tiene dos atributos: un atributo “contenido” , usado para almacenar un objeto; y otro atributo “siguiente” , usado para hacer referencia al siguiente nodo de la lista.

Los nodos serán representados por los siguientes símbolos:

La parte frontal de la lista es representada por un puntero al primer nodo. Es decir, un atributo de la lista enlazada es un nodo primero.

Y podríamos representar a una lista enlazada que almacena int ’s por medio del siguiente diagrama:

La lista también tendrá un atributo largo , del tipo long (o int , si se quiere) , que representará la cantidad de elementos en ella.

2.3 Diseño de la clase ListaEnlazada

Para mostrar cómo definir los métodos de la lista enlazada, crearemos una clase de ejemplo. Ahora que conocemos la idea detrás de la definición de los atributos de la lista enlazada y del nodo, crearemos la clase de ejemplo. El código del ejemplo está en la carpeta “Lista Enlazada Simple”.

El nombre de nuestra clase será ListaEnlazada. Esta lista almacenará sólo un tipo de datos, que serán objetos de la clase Cliente , definida por nosotros mismos. Para que ésto sea posible, es necesario que los Clientes tengan un atributo único para cada uno, de tal forma que podamos diferenciarlos. Este atributo será el RUT.

Así definiremos la clase Cliente:

Nota: Para poder continuar, vea detalles sobre los atributos y el método constructor en los comentarios del archivo Cliente.java

Ahora tenemos todo para empezar a construir nuestra clase ListaEnlazada. En esta implementación de lista enlazada, definiremos los siguientes métodos:

NOMBRE DEL METODO VALOR DE RETORNO

TIPOS DE ARGUMENTO UTILIDAD

estaVacia()

boolean Ninguno Retorna true si la lista está vacía, sino, false

vaciar()

void Ninguno Remueve todo el contenido de la lista

largo()

long Ninguno Retorna el largo (numero de elementos) de la lista

tieneElRut(rut)

boolean String Retorna true si la lista tiene el rut especificado, sino, false tengaMasElementos(puntero) boolean Nodo Retorna true si puntero tiene sucesor, sino, false

añadir(nuevoCliente)

void String Añade a nuevoCliente al final de la lista

buscar(rut)

Cliente String Retorna el Cliente con el rut especificado

remover(rut)

void String Remueve al Cliente con el rut especificado. Devuelve true si elimina a alguno, sino, false

Ahora revisaremos como funcionan, en términos de punteros, los métodos principales: añadir() , buscar() y remover( ). Para detalles sobre el funcionamiento de los demás métodos, variables, etc., ver comentarios del archivo ListaEnlazada.java.

2.3.1 Método añadir()

Para añadir un nodo a la lista, primero creamos un nodo, al que llamaremos nuevoNodo , que contenga al Cliente entregado por el argumento, al que llamaremos nuevoCliente.

Luego conectamos a nuevoNodo con el último nodo de la lista. Para referirnos a éste, creamos un puntero, al que llamaremos puntero , al primer nodo y avanzamos hasta el último por medio de la instrucción puntero = puntero.siguiente como fue explicado anteriormente.

2.3.2 Método buscar()

Para encontrar un Cliente en la lista, debemos buscarlo por medio de su atributo único: el RUT. Es decir, crearemos un puntero puntero con el que recorreremos la lista, comparando el RUT especificado con el RUT de cada Client, hasta encontrar una coincidencia. Para ésto, tendremos que usar una instrucción como ésta:

Gráficamente:

Para ver detalles del código, ver el archivo ListaEnlazada.java.

2.3.3 Método remover()

Para eliminar un nodo de la lista, primero debemos ubicar el nodo anterior éste, es decir, puntero.siguiente será el nodo a eliminar. Ahora, simplemente dejamos sin puntero al nodo diciendo: puntero.siguiente = puntero.siguiente.siguiente, para que el Recolector de Basura de Java lo recoja y sea eliminado. Gráficamente:

Para ver detalles del código, ver el archivo ListaEnlazada.java. El resto de los métodos creados en esta implementación están explicados claramente en los comentarios de el archivo ListaEnlazada.java.

Estos tres métodos son los mas básicos que podemos crear a partir de la “lógica del puntero” , y la implementación de otros métodos basados en la misma idea queda a gusto (o necesidad) del programador. Por ejemplo, podríamos crear métodos que filtren la lista según una característica de los clientes (como discriminar entre los clientes frecuentes y los no frecuentes), o un método que traspase los datos de los clientes a un archivo de texto para guardarlos, y otro que sea capaz de leer este archivo y llenar una lista con los datos guardados. En fin, hay infinitas posibilidades.

3. Pila

3.1 Qué es una pila

La Pila o Stack es una estructura de datos muy simple. Imagina que tienes una pistola de juguete que dispara pelotas, y, para cargarla, tienes que introducir las pelotas una a una por la parte frontal del cañón de la pistola, una tras otra. La primera pelota que dispararás será la última que introdujiste, y la última que dispararás será la primera que introdujiste. Eso es una pila.

Otro ejemplo muy común es la pila de platos que formas para lavarlos: el último plato que apilaste será el primero que lavarás.

La pila es una estructura que se basa en el concepto LIFO: Last In First Out, es decir, el último elemento que entra es el primero que sale.

La implementación de una pila es muy parecida a la de lista enlazada, y sólo difiere en la forma que gestionamos los elementos almacenados. En una pila, crearemos métodos que cumplan las funciones expuestas anteriormente y aclaradas con la Fig. 3.1.1 , es decir, un método que agregue un nodo al principio de la pila y otro que elimine el primer nodo de la pila. Para esta implementación, los nodos serán instancias de la clase Nodo, definida de la misma forma que la definimos para la lista enlazada. Para poder continuar, es necesario que revises detalladamente la definición de esta clase. La explicación de su funcionamiento fue agregada en el mismo código en forma de comentario.

3.2 Diseño de la clase Pila

Seguiremos la misma idea de lista enlazada, pero ahora, al primer nodo de la pila lo llamaremos primeroDeLaPila.

En esta implementación, definiremos los siguientes métodos:

NOMBRE DEL METODO

VALOR DE RETORNO

TIPOS DE ARGUMENTO UTILIDAD

estaVacia()

boolean Ninguno Retorna true si la pila está vacía, sino, false

vaciar()

void Ninguno Remueve todo el contenido de la pila

largo()

long Ninguno Retorna el largo (numero de elementos) de la pila

verPrimero()

Object Ninguno Retorna el valor del contenido de primeroDeLaPila sacar() void Ninguno Elimina al primer nodo de la pila

sacarYverPrimero ()

Object Ninguno Elimina al primer nodo de la pila, pero lo retorna

apilar(nuevoObjeto)

void Object Añade un nodo que contiene a nuevoObjeto a la pila, “empujando” al resto de los nodos

Ahora revisaremos como funcionan, en términos de punteros, los métodos principales: apilar() y sacar(). Para detalles sobre el funcionamiento de los demás métodos, variables, etc., ver comentarios del archivo Pila.java.

3.2.1 Método verPrimero()

La idea del método verPrimero() es, simplemente, decirnos quién es el objeto que está al principio de la pila, es decir, el último objeto ingresado y, potencialmente, el primero a eliminar. Para tener acceso a este objeto, hacemos uso del atributo primeroDeLaPila, que no es más que un puntero al primer nodo de la pila. Así que este método sólo retorna el contenido de primeroDeLaPila , es decir, primeroDeLaPila.contenido.

Para ver detalles del código, ver el archivo Pila.java.

Otra forma de visualizar a apilar() y sacar() :

Para ver detalles del código, ver el archivo Pila.java.

3.2.4 Desafíos para el alumno

En esta ocasión, propongo un desafío muy simple:

  • Basándose en la implementación de Pila anteriormente mostrada, crear una pila que tenga una capacidad máxima de elementos, es decir, que pueda “estar lleno”.

Ahora, abordemos la siguiente estructura de datos a estudiar: la Cola.