Está en la página 1de 11

ARBOLES BINARIOS

Definiciones:

 Un árbol es una colección de nodos (del tipo que se quiera),


uno de los cuales se llama raíz junto con una relación de
“paternidad” que impone una estructura jerárquica sobre los
nodos.
 Una manera natural de definirlo es de forma recursiva:
- Caso base: o el árbol esta vacío o tiene un solo
nodo.
- Caso general: un nodo raíz ( r ) y cero o más
subárboles A1, A2,..., An cada uno de ellos tiene
su raíz conectada a r mediante una arista.

Ejemplo (índice de un libro):

libro

C1 C2 C3

1.1 1.2 2.1 2.2

2.1.1 2.1.2

Libro seria el nodo raíz (r).


Cada subárbol tendría raíz C1, C2, C3.
El subárbol con raíz C2 tendría como subárboles 2.1 y 2.2.

Partes de un árbol:
 Raíz: el nodo inicial o base de una estructura en árbol.
 Nodo hoja: el nodo de un árbol que no tiene hijos.
 Dos nodos son hermanos si tienen el mismo padre.
 Un camino de un nodo N1 a otro Nk se define como la
secuencia de nodos N1, N2,..., Nk tal que Ni es el
padre de Ni+1 para 1 <= i < k.
 La longitud de este camino es el número de aristas que
lo forman, k-1.
 Existe un camino de longitud cero entre cada nodo y él
mismo. En un árbol hay exactamente un camino entre la
raíz y cada nodo.
 Un nodo es antecesor de un segundo si se encuentra
en el camino de la raíz a este.
 Un nodo es descendiente de un segundo si se
encuentra en el camino de este a algún nodo hoja.
 La profundidad de un nodo es la longitud del camino
único entre la raíz y este.
 La altura de un nodo es el camino más largo del nodo a
una hoja. La altura de un árbol es la altura de la raíz.

Ejemplo:

B C D

E F G

H I
Raíz: A
Hojas: E, H, I, G, D.
B es padre de E, F.
F es hijo de B y hermano de E.
Camino de B a I, B—F—I.
Longitud 2.
Profundidad de F 2 y altura 1.

Recorridos:

Para recorrer un árbol se empieza por el nodo raíz y se continúa


por los hijos, de izquierda a derecha, de tal manera que en cada
nodo, antes de pasar al siguiente hermano se recorren primero todos
sus hijos, también de izquierda a derecha.


Recorrido en orden previo o preorden: en este modo de
recorrido, se trabaja en el nodo antes de pasar al
siguiente.
 Recorrido en orden posterior o postorden: en este, se
trabaja en cada nodo una vez que ya se ha trabajado
con todos sus hijos y no antes.
 Recorrido en orden simétrico o inorden: en esta forma
de recorrido se trabaja en los nodos hoja la primera y
única vez que se pasa por ellos y en el resto de nodos
la segunda vez.
Ejemplo:

2 3 4

5 6 7

8 9 10

Recorrido preorden:

1-2-3-5-8-9-6-10-4-7
Recorrido
postorden:

2-8-9-5-10-6-3-7-4-1
Recorrido
inorden:
2-1-8-5-9-
3-10-6-7-4

RELACIÓN CON GRAFOS:

 Un grafo se define como sigue:

G = (V, A)

Donde -V (G) es un conjunto finito, no vacío, de


vértices.
-A (G) es un conjunto de aristas (pares de
vértices)

 Un grafo dirigido es un grafo en el cual solo puedes ir


de un vértice a otro en una dirección.
 Un grafo tiene un ciclo si posee una trayectoria sin
vértices repetidos, excepto el primero y el último. Un
grafo diremos que es acíclico si no posee ciclos.
 Un grafo es conexo si todo par de vértices esta unido
por una trayectoria.
 Un árbol es un grafo dirigido, acíclico y conexo.
 Los árboles abarcadores de coste mínimo son grafos
donde cada arista tiene asociado un coste, de manera
que al moverte de un vértice a otro lo hagas por el
camino de menor coste.
Ejemplo: Redes de comunicación.

1 1
6 5
1 1
5
2 5 3 5 4 2 3 4
3 6 4 2 3 4 2

6
5 6 5 6

Grafo árbol abarcador de coste mínimo

TIPOS DE ÁRBOLES BINARIOS:

Un árbol binario es un árbol en el que cada nodo puede tener dos,


uno o ningún hijo.

Árboles binarios de expresión:

Las expresiones están formadas por valores sobre los que


pueden ejecutarse operaciones binarias. Las distintas partes de la
expresión tienen distintos niveles de procedencia de evaluación, de
tal manera que se puede escribir una expresión en un árbol binario.

Dependiendo de la forma de recorrer el árbol la expresión


habrá que interpretarla de diferente manera. Para tenerla escrita de
la forma habitual habrá que recorrer el árbol en orden simétrico.

Ejemplo de árbol de expresión:

_ +

12 3 4 1

Preorden: *(- (12 3)) (+ (4 1))


Postorden: ((12 3) -) ((4 1) +) *

Inorden: (12 – 3) * (4 + 1)

Montículos:

- Un árbol binario lleno es un árbol binario en el que todas


las hojas están al mismo nivel y cada nodo que no es una
hoja tiene dos hijos.
- Un árbol binario completo es un árbol binario que es lleno
o esta lleno hasta el penúltimo nivel tan a la izquierda
como sea posible

Un montículo debe ser un árbol binario completo y para


cada nodo del montículo, el valor almacenado en ese nodo es mayor
o igual que el valor de cada uno de sus hijos.
La característica especial de los montículos es que
siempre sabemos donde esta el valor máximo (en la raíz).Son útiles
para la ordenación.

Ejemplo:
Valor máximo es 10 y se encuentra en la raíz del árbol.
Es un árbol binario completo ya que esta lleno hasta el
penúltimo nivel y en el último nivel tiene los nodos a la
izquierda.

10

8 9

4 5 6 7

2 3

Árbol binario de búsqueda:

Es un árbol binario en el que el hijo izquierdo, si existe,


contiene un valor más pequeño que el del nodo padre y el hijo
derecho, si existe, contiene un valor mayor al del nodo padre.

Aplicaciones de los árboles binarios de búsqueda y comparación con


listas:
- Facilita la búsqueda, aunque no facilita los
accesos directos como sucedía con los arrays.
Suministra un acceso más rápido y constante, así
es conveniente para aplicaciones en las que el
tiempo de búsqueda debe minimizarse.
- Ocupa más espacio que una lista enlazada
(contiene un puntero extra).
- Los algoritmos para manipular el árbol son más
complicados que los de las listas.

Ejemplo de árbol binario de búsqueda:

2 8

1 4

La raíz es 6 que es3 mayor que 2 y menor que 8.


Así el nodo que contiene el valor 2 es mayor que el valor del nodo de
la izquierda, 1 y menor que el valor del nodo de la derecha.

Operaciones que suelen darse en los árboles binarios de


búsqueda:

Estructura.

{Declaración de tipos}
ELEMENTO = T;
NODO = registro de
Valor: ELEMENTO; {Genérico}
izq: puntero a NODO;
der: puntero a NODO;
fin registro;
/* Implementación en C*/
struct nodo{
int valor;
struct nodo *izq;
struct nodo *der;
};
typedef struct nodo NODO;
 Crear árbol.

/*Implementación en C*/
/*separar memoria para un nodo*/

NODO *getnode()
{NODO *p;
p=(NODO*) malloc (sizeof (NODO));
return p;
}

NODO *crea_arbol (int v)


{NODO *p;
p=getnode();
p->valor=v;
p->izq=NULL;
p->der=NULL;
return p;
}
 Comprobar vació.

Vacia (A: NODO, resp: lógico)


Necesita: un árbol A y un valor lógico resp.
Modifica: resp, indicando si el árbol esta vació ( falso ) o no
(cierto).
/* Implementación en C*/
int vacio (NODO *arbol)
{if (arbol==NULL) return 1;
else return 0;
}

 Buscar: devuelve un putero al nodo del árbol que tiene el valor


buscado o nil si no existe el nodo. Primero se mira si el árbol esta
vacío, en tal caso el resultado sería nil. Es importante hacer esta
comprobación primero pues sino intentarías buscar en una estructura
vacía, lo que causaría un error al ejecutarlo. Después se comprueba
si el valor buscado es la raíz del árbol y sino se hacen llamadas
recursivas a los subárboles izquierdo y derecho según la relación del
valor buscado con la raíz.

/*Implementación en C*/

/*Buscar elementos en un arbol*/


NODO *buscar(int v, NODO *A){
if (vacio(A)==1){
printf ("\n elemento no encontrado");
return NULL;
}
else{
if(v < A->valor) buscar(v,A->izq);
else{
if(v > A->valor) buscar(v,A->der);
else return A;
}
}
}

 Buscar_min y buscar_max: Devuelve punteros a los elementos


menor y mayor respectivamente. Se devuelven punteros, en vez de
los valores máximo y mínimo, para que de esta forma las funciones
sean lo más semejantes posible a la de buscar, ya que de esta forma
se simplifican las cosas.

/*Implementación en C*/

/*buscar el minimo de un arbol*/

NODO *buscar_min (NODO *A)


{if (vacio(A)==1) return NULL;
else
{if (A->izq==NULL) return A;
else buscar_min (A->izq);
}
}
Seria muy parecido buscar el máximo.
 Insertar: para insertar un nodo X en el árbol A se llama a la
función buscar y si X ya esta en el árbol no se hace nada y sino se
encuentra X se añade al final del camino recorrido.

/*Implementación en C*/

/*Para insertar elementos*/


NODO *insertar (int v, NODO *A)
{
if (vacio(A)==1) {
A=crea_arbol (v);
if (vacio(A)==1) printf ("\n memoria agotada\n");
}
else
{
if (v < A->valor)
{
if(A->izq==NULL) A->izq=crea_arbol(v);
else insertar(v,A->izq);
}
if (v > A->valor)
{
if(A->der==NULL) A->der=crea_arbol(v);
else insertar(v,A->der);
}
/*Si v ya se encuentra en el arbol no se hace nada*/
}
return A;
}

 Eliminar: Hay que tener en cuenta varias posibilidades:


 Si el nodo es una hoja se puede eliminar sin más.
 Si el nodo tiene un hijo hay que ajustar el puntero del padre al
hijo del nodo a eliminar. Si se quiere liberar el nodo eliminado
hay que conservar un puntero a este.
 Cuando el nodo a eliminar tiene dos hijos lo que se hace es
sustituir el nodo por el menor del subárbol derecho y luego se
elimina este último, ya de manera sencilla dado que no tendrá
nunca hijo izquierdo puesto que es el menor del subárbol.
Si el número de eliminaciones esperadas no es demasiado
grande se usa también la eliminación perezosa, en la que en vez de
eliminar realmente un nodo, solo se marca como eliminado. La
penalización de tiempo es muy pequeña y si luego se quiere volver a
insertar el nodo eliminado no hace falta tener que crear una celda
nueva, que requiere mucho trabajo, sino sólo quitar la marca.

Árboles AVL ( Adelson-Velskii y Landis ):

Son árboles binarios de búsqueda con una condición de


equilibrio, la cual debe ser fácil de mantener. Dicha propiedad
asegura que la profundidad del árbol sea O (log n).

La idea de equilibrio más sencilla sería que los subárboles


izquierdo y derecho fuesen igual de profundos, pero esto no evita que
el árbol sea demasiado profundo. Otra idea seria que todos los nodos
tuviesen los dos subárboles a la misma altura, pero esto, teniendo en
cuenta que la altura de un árbol vacío se define como –1, se restringe
a los árboles de (2^k)-1 nodos, con lo cual no es muy útil.

Los árboles AVL son árboles binarios en los que la diferencia de


altura entre los subárboles de cada nodo no puede ser superior a
uno. Hay que mantener en la estructura de nodo información sobre la
altura.
6

2 8

1 4 7

Todas las operaciones son iguales que en los árboles


binarios de búsqueda, excepto la inserción y la eliminación, a no ser
que esta sea perezosa, dado que estas pueden alterar la condición de
equilibrio en el árbol.

Para poder hacer siempre estas operaciones hay que


introducir una modificación al árbol, la rotación.

- Rotación sencilla:

K
k 2
1

K Z K
X
2 1

X Y Y Z

Los dos son árboles binarios de búsqueda. Hay que tener


en cuenta que k1 es menor que k2 en ambos árboles, que todos los
elementos del subárbol X son menores que k1, todos los elementos
del subárbol Z son mayores que k2 y todos los elementos del
subárbol Y están entre k1 y k2. La transformación del uno al otro se
llama rotación. La rotación cambia la estructura del árbol, pero esta
no deja de ser un árbol binario de búsqueda.

La rotación se puede hacer en cualquier nodo del árbol.


Para mantener la condición de equilibrio de los AVL lo que se hace
cuando se inserta un elemento es ir recorriendo el árbol desde el
nodo insertado hasta la raíz, comprobando a cada paso si se
conserva la condición de equilibrio y si no es así se hace una
rotación, que en muchos casos sirve para mantener la condición de
equilibrio.
6 6

2 8 2 7

1 4 7
1 4 6,5 8

3 6,5
3

- Rotación doble:

Existe un caso en el que la rotación simple no recompone el árbol,


ejemplo.

4 4

2 6 2 6

1 3 5 7 1 3 55 15

15 7

14 14

El problema anterior se soluciona con la rotación doble como se indica a


continuación:

K
3
K
2
K
1 K K
3 1
A
K
2 D A B C D

B C
Aplicando la rotación doble al ejemplo nos queda el árbol siguiente:

2 6

1 3 5 14

7 15

Árboles desplegados:

En los árboles desplegados, después de tener acceso a


un nodo, este se lleva hasta la raíz.

Si el nodo que se mueve es muy profundo, por el camino


se cambian muchos nodos que son relativamente profundos, y de esta
manera cabe esperar que con la reestructuración se hagan también
más rápidos los futuros accesos a estos nodos.

Algunos estudios han demostrado que los accesos a un


nodo al que ya se ha accedido con anterioridad son mucho más
frecuentes de lo que cabría esperar, lo cual hace que estos árboles,
además de tener una buena cota de tiempo en teoría sean buenos en
la práctica. Tampoco hace falta mantener la información relativa a la
altura o equilibrio, lo que puede ahorrar espacio y simplificar el
código en cierta medida.

Despliegue : rotaremos en el camino de acceso al nodo de


abajo hacia arriba. Si X es un nodo, distinto de la raíz, en el camino
de acceso en el que estamos rotando:

- Si el padre de X es la raíz, simplemente rotamos.


- Sino; X tiene un padre ( P ) y un abuelo ( A ):

 Zig-zag; X es un hijo izquierdo y P un hijo


derecho ( o al revés ). En este caso se hace
una rotación doble como la de los AVL
a
x

D
p p a

A
x A B C D

B C

 Zig-zig: ambos, X y P, son hijos izquierdos


( o derechos )

x
a

p
p D A

B
C a
x

A B C D

También podría gustarte