You are on page 1of 5

LISTAS ENLAZADAS

Existen estructuras de datos dinámicas que se, utilizan para almacenar datos que están
cambiando constante mente. A diferencia de los vectores, las estructuras de datos dinámicas
se expanden y se contraen haciéndolas mas flexibles a la hora de añadir o eliminar
información.

Esto quiere decir que podemos crear un nuevo nodo para insertarlo entre los nodos ya
existentes o eliminando un nodo existente. Entre las estructuras de datos lineales encontrarnos
las listas enlazadas, las pilas y colas.

Las listas enlazadas permiten almacenar información en posiciones de memoria que no sean
contiguas. Estas listas para almacenar la información, contienen elementos llamados nodos.
Estos nodos poseen dos campos uno para almacenar la información o valor del elemento y otro
para el enlace que determina la posición del siguiente elemento o nodo de la lista.

Para inserta o borrar información no es necesario realizar un desplazamiento, para esto las
listas cuentan con punteros o enlaces que contienen la posición o dirección del otro nodo de la
lista. Por esta razón no es necesario que los elementos de la lista se almacenen en posiciones
contiguas.

Cuando en una lista enlazada no hay ningún elemento quiere decir que la lista esta vacía,
además existe un puntero de cabecera para acceder al primer nodo de la lista y un puntero
nulo para determinar el último elemento (nodo) de la lista.

• Insertar: inserta un nodo con dato x en la lista, pudiendo realizarse esta inserción al
principio o final de la lista o bien en orden.
• Eliminar: elimina un nodo de la lista, puede ser según la posición o por el dato.
• Buscar: busca un elemento en la lista.
• Localizar: obtiene la posición del nodo en la lista.
• Vaciar: borra todos los elementos de la lista

LISTAS DOBLEMENTE ENLAZADAS

En algunas aplicaciones podemos desear recorrer la lista hacia adelante y hacia atrás, o dado
un elemento, podemos desear conocer rápidamente los elementos anterior y siguiente. En tales
situaciones podríamos desear darle a cada celda sobre una lista un puntero a las celdas
siguiente y anterior en la lista tal y como se muestra en la figura.

Otra ventaja de las listas doblemente enlazadas es que podemos usar un puntero a la celda
que contiene el i-ésimo elemento de una lista para representar la posición i, mejor que usar el
puntero a la celda anterior aunque lógicamente, también es posible la implementación similar a
la expuesta en las listas simples haciendo uso de la cabecera. El único precio que pagamos por
estas características es la presencia de un puntero adicional en cada celda y
consecuentemente procedimientos algo más largos para algunas de las operaciones básicas
de listas. Si usamos punteros (mejor que cursores) podemos declarar celdas que consisten en
un elemento y dos punteros a través de:

typedef struct celda{


tipoelemento elemento;
struct celda *siguiente,*anterior;
}tipocelda;

typedef tipocelda *posicion;

Un procedimiento para borrar un elemento en la posición p en una lista doblemente enlazada


es:

void borrar (posicion p)


{
if (p->anterior != NULL)
p->anterior->siguiente = p->siguiente;
if (p->siguiente != NULL)
p->siguiente->anterior = p->anterior;
free(p);
}

El procedimiento anterior se expresa de forma gráfica en como muestra la figura:

Donde los trazos contínuos denotan la situación inicial y los punteados la final. El ejemplo visto
se ajusta a la supresión de un elemento o celda de una lista situada en medio de la misma.
Para obviar los problemas derivados de los elementos extremos (primero y último) es práctica
común hacer que la cabecera de la lista doblemente enlazada sea una celda que efectivamente
complete el círculo, es decir, el anterior a la celda de cabecera sea la última celda de la lista y
la siguiente la primera. De esta manera no necesitamos chequear para NULL en el anterior
procedimiento borrar.

Por consiguiente, podemos realizar una implementación de listas doblemente enlazadas con
cabecera tal que tenga una estructura circular en el sentido de que dado un nodo y por medio
de los punteros siguiente podemos volver hasta él como se puede observar en la figura (de
forma analoga para anterior).
Es importante notar que aunque la estructura física de la lista puede hacer pensar que
mediante la operación siguiente podemos alcanzar de nuevo un nodo de la lista, la estructura
lógica es la de una lista y por lo tanto habrá una posición primero y una posición fin de forma
que al aplicar una operación anterior o siguiente respectivamente sobre estas posiciones el
resultado será un error.

Respecto a la forma en que trabajarán las funciones de la implementación que proponemos


hay que hacer constar los siguientes puntos:

• La función de creación debe alojar memoria para la cabecera y hacer que los punteros
siguiente y anterior apunten a ella, devolviendo un puntero a dicha cabecera.

• La función primero(l) devolverá un puntero al nodo siguiente a la cabecera.

• La función fin(l) devolvera un puntero al nodo cabecera.

• Trabajar con varias posiciones simultáneamente tendrá un comportamiento idéntico al


de las listas enlazadas excepto respecto al problema referente al borrado cuando se
utilizan posiciones consecutivas. Es posible implementar la función de borrado de tal
forma que borrar un elemento de una posición p invalida el valor de dicha posición p y
no afecta a ninguna otra posición. Nosotros en nuestra implementación final optaremos
por pasar un puntero a la posición para el borrado de forma que la posición usada
quede apuntando al elemento siguente que se va a borrar al igual que ocurría en el
caso de las listas simples. Otra posible solución puede ser que la función devuelva la
posición del elemento siguiente a ser borrado.

• La inserción se debe hacer a la izquierda del nodo apuntado por la posición ofrecida a
la función insertar. Esto implica que al contrario que en las listas simples, al insertar un
nodo, el puntero utilizado sigue apuntando al mismo elemento al que apuntaba y no al
nuevo elemento insertado. Si se desea, es posible modificar la función de forma que se
pase un puntero a la posición de inserción para poder modificarla y hacer que apunte al
nuevo elemento insertado. En cualquier caso, el comportamiento final de la función
deberá quedar reflejado en el conjunto de especificaciones del TDA.

ÁRBOLES BINARIOS

Se define un árbol binario como un conjunto finito de elementos (nodos) que bien esta vacío o
esta formado por una raíz con dos arboles binarios disjuntos, es decir, dos descendientes
directos llamados subarbol izquierdo y subarbol derecho.

Los árboles binarios (también llamados de grado 2 )tienen una especial importancia.

Las aplicaciones de los arboles binarios son muy variadas ya que se les puede utilizar para
representar una estructura en la cual es posible tomar decisiones con dos opciones en distintos
puntos.

Arbol binario de búsqueda.

Los árboles binarios se utilizan frecuentemente para representar conjuntos de datos cuyos
elementos se identifican por una clave única. Si el árbol está organizado de tal manera que la
clave de cada nodo es mayor que todas las claves su subárbol izquierdo, y menor que todas
las claves del subárbol derecho se dice que este árbol es un árbol binario de búsqueda.
Ejemplo:

Operaciones básicas

Una tarea muy común a realizar con un árbol es ejecutar una determinada operación con cada
uno de los elementos del árbol. Esta operación se considera entonces como un parámetro de
una tarea más general que es la visita de todos los nodos o, como se denomina usualmente,
del recorrido del árbol.

Si se considera la tarea como un proceso secuencial, entonces los nodos individuales se visitan
en un orden específico, y pueden considerarse como organizados según una estructura lineal.
De hecho, se simplifica considerablemente la descripción de muchos algoritmos si puede
hablarse del proceso del siguiente elemento en el árbol, según un cierto orden subyacente.

Hay dos formas básicas de recorrer un árbol: El recorrido en amplitud y el recorrido en


profundidad.

Clasificación de Arboles Binarios

Existen cuatro tipos de árbol binario:.

• Arbol Binario Distinto.

• Arbol Binario Similares.

• Arbol Binario Equivalentes.

• Arbol Binario Completos.

A continuación se hará una breve descripción de los diferentes tipos de árbol binario así como
un ejemplo de cada uno de ellos.

Arbol Binario Distinto

Se dice que dos árboles binarios son distintos cuando sus estructuras son diferentes.

Ejemplo:

Arbol Binario Similar

Dos arboles binarios son similares cuando sus estructuras son idénticas, pero la información
que contienen sus nodos es diferente.

Ejemplo:
Arbol Binario Equivalente

Son aquellos arboles que son similares y que además los nodos contienen la misma
información. Ejemplo:

Arbol Binario Completo

Son aquellos arboles en los que todos sus nodos excepto los del ultimo nivel, tiene dos hijos; el
subarbol izquierdo y el subarbol derecho.

Terminología

La terminología que por lo regular se utiliza para el manejo de arboles es la siguiente:

• Hijo: X es hijo de Y, sí y solo sí el nodo X es apuntado por Y. También se dice que X


es descendiente directo de Y.

• Padre: X es padre de Y sí y solo sí el nodo X apunta a Y. También se dice que X es


antecesor de Y.

• Hermano: Dos nodos serán hermanos si son descendientes directos de un mismo


nodo.

• Hoja: Se le llama hoja o terminal a aquellos nodos que no tienen ramificaciones (hijos).

• Nodo anterior: Es un nodo que no es raíz ni terminal.

• Grado: Es el número de descendientes directos de un determinado nodo.

• Grado de un árbol: Es el máximo grado de todos los nodos del árbol.

• Nivel: Es el número de arcos que deben ser recorridos para llegar a un determinado
nodo. Por definición la raíz tiene nivel 1.

• Altura: Es el máximo número de niveles de todos los nodos del árbol.

• Peso: Es el número de nodos del árbol sin contar la raíz.

• Longitud de camino: Es el número de arcos que deben ser recorridos para llegar
desde la raíz al nodo X. Por definición la raíz tiene longitud de camino 1, y sus
descendientes directos longitud de camino 2 y así sucesivamente.