Top Banner
6.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. Árbol 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'.
59

6.1 Definición

Mar 12, 2023

Download

Documents

Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: 6.1 Definición

6.1 Definición^

Un árbol es una estructura no lineal en la que cada nodo puedeapuntar a uno o varios nodos.También se suele dar una definición recursiva: un árbol es unaestructura en compuesta por un dato y varios árboles.Esto son definiciones simples. Pero las características queimplican no lo son tanto.

Árbol

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' sonhijos de 'G'.

Nodo padre: nodo que contiene un puntero al nodoactual. En el ejemplo, el nodo 'A' es padre de 'B','C' y 'D'.

Los árboles con los que trabajaremos tienen otra característicaimportante: cada nodo sólo puede ser apuntado por otro nodo, esdecir, cada nodo sólo tendrá un padre. Esto hace que estosárboles estén fuertemente jerarquizados, y es lo que en realidadles 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 nodoque usaremos para referirnos al árbol. En el ejemplo,ese nodo es el 'A'.

Nodo hoja: nodo que no tiene hijos. En el ejemplo hayvarios: 'F', 'H', 'I', 'K', 'L', 'M', 'N' y 'O'.

Page 2: 6.1 Definición

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

Otra característica que normalmente tendrán nuestros árboles esque todos los nodos contengan el mismo número de punteros, esdecir, usaremos la misma estructura para todos los nodos delárbol. Esto hace que la estructura sea más sencilla, y por lotanto también los programas para trabajar con ellos.Tampoco es necesario que todos los nodos hijos de un nodoconcreto existan. Es decir, que pueden usarse todos, algunos oninguno de los punteros de cada nodo.Un árbol en el que en cada nodo o bien todos o ninguno de loshijos existe, se llama árbol completo.En una cosa, los árboles se parecen al resto de las estructurasque hemos visto: dado un nodo cualquiera de la estructura,podemos considerarlo como una estructura independiente. Esdecir, un nodo cualquiera puede ser considerado como la raíz deun á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 puedetener cada elemento de árbol. De este modo, diremosque un árbol en el que cada nodo puede apuntar aotros dos es de orden dos, si puede apuntar a tresserá de orden tres, etc.

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

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

Altura: la altura de un árbol se define como el niveldel nodo de mayor nivel. Como cada nodo de un árbolpuede considerarse a su vez como la raíz de un árbol,

Page 3: 6.1 Definición

también podemos hablar de altura de ramas. El árboldel ejemplo tiene altura 3, la rama 'B' tiene altura2, la rama 'G' tiene altura 1, la 'H' cero, etc.

Los árboles de orden dos son bastante especiales, de hecho lesdedicaremos varios capítulos. Estos árboles se conocen tambiéncomo árboles binarios.Frecuentemente, aunque tampoco es estrictamente necesario, parahacer más fácil moverse a través del árbol, añadiremos unpuntero a cada nodo que apunte al nodo padre. De este modopodremos avanzar en dirección a la raíz, y no sólo hacia lashojas.Es importante conservar siempre el nodo raíz ya que es el nodo apartir 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 vistohasta 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;};

O generalizando más:

#define ORDEN 5

struct nodo { int dato; struct nodo *rama[ORDEN];};

6.2 Declaraciones de tipos para manejar árboles en C^

Para C, y basándonos en la declaración de nodo que hemos vistomás arriba, trabajaremos con los siguientes tipos:

typedef struct _nodo { int dato;

Page 4: 6.1 Definición

struct _nodo *rama[ORDEN];} tipoNodo; typedef tipoNodo *pNodo;typedef tipoNodo *Arbol;

Al igual que hicimos con las listas que hemos visto hasta ahora,declaramos un tipo tipoNodo para declarar nodos, y untipopNodo para es el tipo para declarar punteros a un nodo.Arbol es el tipo para declarar árboles de orden ORDEN.

Árbol

El movimiento a través de árboles, salvo que implementemospunteros al nodo padre, será siempre partiendo del nodo raízhacia un nodo hoja. Cada vez que lleguemos a un nuevo nodopodremos optar por cualquiera de los nodos a los que apunta paraavanzar al siguiente nodo.En general, intentaremos que exista algún significado asociado acada uno de los punteros dentro de cada nodo, los árboles queestamos viendo son abstractos, pero las aplicaciones no tienenpor qué serlo. Un ejemplo de estructura en árbol es el sistemade directorios y ficheros de un sistema operativo. Aunque eneste caso se trata de árboles con nodos de dos tipos, nodosdirectotio y nodos fichero, podríamos considerar que los nodoshoja son ficheros y los nodos rama son directorios.Otro ejemplo podría ser la tabla de contenido de un libro, porejemplo de este mismo curso, dividido en capítulos, y cada unode ellos en subcapítulos. Aunque el libro sea algo lineal, comouna lista, en el que cada capítulo sigue al anterior, también esposible acceder a cualquier punto de él a través de la tabla decontenido.

Page 5: 6.1 Definición

También se suelen organizar en forma de árbol los organigramasde mando en empresas o en el ejército, y los árbolesgenealógicos.

Page 6: 6.1 Definición

ÁrbolesAntes de comenzar a trabajar sobre árboles es importante estudiar recursividad

Contenidos

1. 1   Introducción 2. 2   Visión Recursiva 3. 3   Ejemplo de un árbol en python 4. 4   Recorridos 1. 4.1   Profundidad Primero 2. 4.2   Ancho Primero

5. 5   Código fuente

IntroducciónAlgunas definiciones:

Un árbol es una estructura de datos enlazada que organiza elementos en forma jerárquicaEs decir, hay una relación padre/hijos.

Cada nodo puede tener más de un hijo, pero un solo padre. Existe un nodo que no tiene padre denominado raiz. Los nodos que no tienen hijos se denominan hojas Un árbol es de orden N (o N-ario) cuando la máxima cantidad de hijos que puede tener un

nodo es N. La profundidad de un árbol es la distancia (saltos entre nodos) desde la raiz hasta la

hoja más lejana.

Page 7: 6.1 Definición

Visión Recursiva

Cada rama de un árbol puede ser visto como un sub-árbol. De esta forma, el árbol genealógico de la abuela Bouvier está compuesto por el árbol de Marge, el de Patty y el de Selma. Esto repercute en el estilo de programación: todas las funciones que recorren o modifican el árbolhacen uso de características recursivas. Esto significa que a veces el término nodo y árbol se usan indistintamente, lo cual puede causar confusión al desprevenido.

Ejemplo de un árbol en python

definición

Page 8: 6.1 Definición

Haciendo uso de la noción recursiva de un árbol, podemos definir un árbol como la unión de unelemento y sus ramas.

class Arbol:

    def __init__(self, elemento):

        self.hijos = []

        self.elemento = elemento

agregar elementos

 Una forma de agregar elementos podría ser:

abuela = "Jacqueline Gurney"

marge = "Marge Bouvier"

patty = "Patty Bouvier"

selma = "Selma Bouvier"

bart = "Bart Simpson"

lisa = "Lisa Simpson"

maggie = "Maggie Simpson"

ling = "Ling Bouvier"

arbol = Arbol(abuela)

agregarElemento(arbol, patty, abuela)

agregarElemento(arbol, selma, abuela)

agregarElemento(arbol, ling, selma)

agregarElemento(arbol, marge, abuela)

agregarElemento(arbol, bart, marge)

Page 9: 6.1 Definición

agregarElemento(arbol, lisa, marge)

agregarElemento(arbol, maggie, marge)

Para lo cual necesitamos escribir el método agregarElemento que recibe el árbol, el elemento a agregar, y el padre de dicho elemento. Este método obtiene el sub-árbol que contiene al elemento padre y le agrega una nueva rama (árbol) con el nuevo elemento.

Es importante entender que la búsqueda del sub-árbol debe ser recursiva. La condición de corte es si el elemento está en el árbol actual, si no  se busca en cada uno de los hijos.

def agregarElemento(arbol, elemento, elementoPadre):

    subarbol = buscarSubarbol(arbol, elementoPadre);

    subarbol.hijos.append(Arbol(elemento))

def buscarSubarbol(arbol, elemento):

    if arbol.elemento == elemento:

        return arbol

    for subarbol in arbol.hijos:

        arbolBuscado = buscarSubarbol(subarbol, elemento)

        if (arbolBuscado != None):

            return arbolBuscado

    return None

Profundidad y grado

La profundidad de un árbol es 1 + la profundidad del hijo más profundo

Page 10: 6.1 Definición

El grado es el maximo entre la cantidad de sus hijos directos y el grado de sus hijos

def profundidad(arbol):    if len(arbol.hijos) == 0:

        return 1

    return 1 + max(map(profundidad,arbol.hijos))

def grado(arbol):

    return max(map(grado, arbol.hijos) + [len(arbol.hijos)])

Recorridos

Recorrer un árbol significa comenzar a visitar a cada uno de los elementos (tanto al elemento de la raiz como a los elementos de sus descendientes). A veces se realiza porque se quiere ejecutar algo por cada uno u otras veces es porque se está buscando uno en particular.

Existen dos maneras de recorrer un árbol: profundidad primero y ancho primero.

Profundidad PrimeroEsta forma es la más sencilla: consiste en explorar toda una rama antes de pasar a la siguientedecir, hasta no terminar de recorrer toda la rama de marge, no iniciará con Patty.

Acá un diagrama que muestra el órden de recorrido sobre un árbol:

Page 11: 6.1 Definición

En el método que vimos anteriormente de buscarSubArbol estamos usando esta estrategia para encontrar el elemento. A continuación un ejemplo de una función de orden superior que ejecuta sobre cada elemento del árbol:

def ejecutarProfundidadPrimero(arbol, funcion):    funcion(arbol.elemento)

    for hijo in arbol.hijos:

        ejecutarProfundidadPrimero(hijo, funcion)

Al utilizarlo con una función que imprime un elemento :

def printElement(element):

    print element

ejecutarProfundidadPrimero(arbol, printElement)

podemos ver el siguiente resultado:

Jacqueline Gurney

Page 12: 6.1 Definición

Patty Bouvier

Selma Bouvier

Ling Bouvier

Marge Bouvier

Bart Simpson

Lisa Simpson

Maggie Simpson

Ancho PrimeroEsta forma es un poco más compleja pero puede ser conveniente dependiendo del problema. En nuestro ejemplo irá recorriendo generación a generación: Primero la abuela, luego todas las hijas, y luego todos los nietos.

Acá otro diagrama a modo esquemático:

Para poder resolver este algoritmo, es necesario retrasar la ejecución de la función sobre los hijos hasta que no se haya terminado de ejecutar todos los hermanos/primos. Por eso se usa una cola que entra, Primero que sale) para lograr esto. Cuando se visita a un nodo, ejecuta la función, agrega sus hijos a la cola, y luego llama a la función recursiva pero en lugar de hacerlo sobre algún hijo suyo (como hacía profundidad primero) lo hace sobre el próximo de la cola, que seguramente es un alguien de su nivel  si aún quedan o el primero del próximo nivel.

Page 13: 6.1 Definición

def ejecutarAnchoPrimero(arbol, funcion, cola = deque()):

    funcion(arbol.elemento)

    if (len(arbol.hijos) > 0):

        cola.extend(arbol.hijos)

    if (len(cola) != 0):

        ejecutarAnchoPrimero(cola.popleft(), funcion, cola)

Se usa de la siguiente manera:

def printElement(element):    print element

ejecutarAnchoPrimero(arbol, printElement)

Veamos paso a paso que ocurre:

Invocación:1

Elemento: Jackeline

 Consola Cola

 Jacqueline Gurney

Patty Bouvier

Selma Bouvier

Marge Bouvier

Invocación:2

Elemento: Patty

Page 14: 6.1 Definición

No tiene hijos, por lo tanto no agrega elementos a la cola

 Consola Cola

Jacqueline GurneyPatty Bouvier

Selma Bouvier

Marge Bouvier

Invocación:3

Elemento: Selma

Agrega a Ling a la cola

 Consola Cola

Jacqueline GurneyPatty BouvierSelma Bouvier

Marge Bouvier

Ling Bouvier

Invocación:4

Elemento: Marge

Agrega a Bart, Lisa y Maggie

 Consola Cola

Jacqueline GurneyPatty BouvierSelma BouvierMarge Bouvier

Ling Bouvier

Bart Simpson

Lisa Simpson

Maggie Simpson

Page 15: 6.1 Definición

Invocación:5

Elemento: Ling

 Consola Cola

Jacqueline GurneyPatty BouvierSelma BouvierMarge BouvierLing Bouvier

Bart Simpson

Lisa Simpson

Maggie Simpson

Invocación:6

Elemento: Bart

 Consola Cola

Jacqueline GurneyPatty BouvierSelma BouvierMarge BouvierLing BouvierBart Simpson

Lisa Simpson

Maggie Simpson

Invocación:7

Elemento: Lisa

 Consola Cola

Jacqueline GurneyPatty Bouvier

Maggie Simpson

Page 16: 6.1 Definición

Selma BouvierMarge BouvierLing BouvierBart SimpsonLisa Simpson

Invocación:8

Elemento: Maggie

 Consola Cola

Jacqueline GurneyPatty BouvierSelma BouvierMarge BouvierLing BouvierBart SimpsonLisa SimpsonMaggie Simpson

from _2_arboles import *

def printElement(element): print element

abuela = "Jacqueline Gurney"marge = "Marge Bouvier"patty = "Patty Bouvier"selma = "Selma Bouvier"bart = "Bart Simpson"lisa = "Lisa Simpson"maggie = "Maggie Simpson"ling = "Ling Bouvier"

arbol = Arbol(abuela)agregarElemento(arbol, patty, abuela)agregarElemento(arbol, selma, abuela)agregarElemento(arbol, ling, selma)

Page 17: 6.1 Definición

agregarElemento(arbol, marge, abuela)agregarElemento(arbol, bart, marge)agregarElemento(arbol, lisa, marge)agregarElemento(arbol, maggie, marge)

print profundidad(arbol)print grado(arbol)

## PROFUNDIDAD#

ejecutarProfundidadPrimero(arbol, printElement)

## ANCHO#

ejecutarAnchoPrimero(arbol, printElement)

otrafrom collections import deque

class Arbol: def __init__(self, elemento): self.elemento = elemento self.hijos = []

def agregarElemento(arbol, elemento, elementoPadre): subarbol = buscarSubarbol(arbol, elementoPadre); subarbol.hijos.append(Arbol(elemento))

def buscarSubarbol(arbol, elemento): if arbol.elemento == elemento: return arbol for subarbol in arbol.hijos: encontrado = buscarSubarbol(subarbol, elemento) if encontrado != None: return encontrado return None # Exceptions

def profundidad(arbol): if len(arbol.hijos) == 0:

Page 18: 6.1 Definición

return 1 profundidades = map(profundidad, arbol.hijos) return 1 + max(profundidades)

def grado(arbol): return max(map(grado, arbol.hijos) + [len(arbol.hijos)])

## RECORRIDOS#

def ejecutarProfundidadPrimero(arbol, funcion): funcion(arbol.elemento) for hijo in arbol.hijos: ejecutarProfundidadPrimero(hijo, funcion)

def ejecutarAnchoPrimero(arbol, funcion, cola = deque()): funcion(arbol.elemento) if (len(arbol.hijos) > 0): cola.extend(arbol.hijos) if (len(cola) != 0): ejecutarAnchoPrimero(cola.popleft(), funcion, cola)from collections import deque

class Arbol: def __init__(self, elemento): self.elemento = elemento self.hijos = []

def agregarElemento(arbol, elemento, elementoPadre): subarbol = buscarSubarbol(arbol, elementoPadre); subarbol.hijos.append(Arbol(elemento))

def buscarSubarbol(arbol, elemento): if arbol.elemento == elemento: return arbol for subarbol in arbol.hijos: encontrado = buscarSubarbol(subarbol, elemento) if encontrado != None: return encontrado return None # Exceptions

Page 19: 6.1 Definición

def profundidad(arbol): if len(arbol.hijos) == 0: return 1 profundidades = map(profundidad, arbol.hijos) return 1 + max(profundidades)

def grado(arbol): return max(map(grado, arbol.hijos) + [len(arbol.hijos)])

## RECORRIDOS#

def ejecutarProfundidadPrimero(arbol, funcion): funcion(arbol.elemento) for hijo in arbol.hijos: ejecutarProfundidadPrimero(hijo, funcion)

def ejecutarAnchoPrimero(arbol, funcion, cola = deque()): funcion(arbol.elemento) if (len(arbol.hijos) > 0): cola.extend(arbol.hijos) if (len(cola) != 0): ejecutarAnchoPrimero(cola.popleft(), funcion, cola)

Árbol binario de búsquedaUn árbol binario de búsqueda también llamados BST (acrónimo del inglés Binary Search Tree) es un tipo particular de árbol binario que presenta una estructura de datos en forma de árbol usada en informática.

Índice  [ocultar] 

1   Descripción

Page 20: 6.1 Definición

2   Implementación en Python 3   Operaciones

o 3.1   Búsqueda o 3.2   Inserción o 3.3   Borrado o 3.4   Otras Operaciones o 3.5   Recorridos

4   Tipos de árboles binarios de búsqueda 5   Comparación de rendimiento 6   Buscando el Árbol binario de búsqueda óptimo 7   Véase también 8   Referencias 9   Enlaces externos

Descripción[editar]Un árbol binario de búsqueda (ABB) es un árbol binario definido de la siguiente forma:

Árbol binariola mayoría de los árboles binarios son de búsquedaUn árbol binario no vacío, de raíz R, es un árbol binario de búsqueda si:

En caso de tener subárbol izquierdo, la raíz R debe ser mayor que el valor máximo almacenado en el subárbol izquierdo, y que el subárbol izquierdo sea un árbol binario de búsqueda.

En caso de tener subárbol derecho, la raíz R debe ser menor que el valor mínimo almacenado en el subárbol derecho, y que el subárbol derecho sea un árbol binario de búsqueda.

Page 21: 6.1 Definición

Un árbol binario de búsqueda de tamaño 9 y profundidad 3, con raíz 8 y hojas 1, 4, 7 y 13

Para una fácil comprensión queda resumido en que es un árbol binario que cumple que el subárbol izquierdo de cualquier nodo (si no está vacío) contiene valores menores que el que contiene dicho nodo, y el subárbol derecho (si no está vacío) contiene valores mayores.Para estas definiciones se considera que hay una relación de orden establecida entre los elementos de los nodos. Que cierta relación estédefinida, o no, depende de cada lenguaje de programación. De aquí se deduce que puede haber distintos árboles binarios de búsqueda para un mismo conjunto de elementos.La altura h en el peor de los casos es siempre el mismo tamaño que el número de elementos disponibles. Y en el mejor de los casos viene dadapor la expresión  , donde ceil indica redondeo porexceso.El interés de los árboles binarios de búsqueda (ABB) radica en que su recorrido en inordenproporciona los elementos ordenados de forma ascendente y en que la búsqueda de algún elemento suele ser muy eficiente.Dependiendo de las necesidades del usuario que trate con una estructura de este tipo, se podrá permitir la igualdad estricta en alguno, en ninguno o en ambos de los subárboles que penden de la raíz.Permitir el uso de la igualdad provoca la aparición de valores dobles y hace la búsqueda más compleja.Un árbol binario de búsqueda no deja de ser un caso particular de árbol binario, así usando la siguiente especificación de árbol binarioenmaude:

Page 22: 6.1 Definición

fmod ARBOL-BINARIO {X :: TRIV}is sorts ArbolBinNV{X} ArbolBin{X} . subsort ArbolBinNV{X} < ArbolBin{X} . *** generadores op crear : -> ArbolBin{X} [ctor] . op arbolBin : X$Elt ArbolBin{X} ArbolBin{X} -> ArbolBinNV{X} [ctor] . endfm

podemos hacer la siguiente definición para un árbol binario de búsqueda (también en maude):

fmod ARBOL-BINARIO-BUSQUEDA {X :: ORDEN} is protecting ARBOL-BINARIO{VOrden}{X} . sorts ABB{X} ABBNV{X} . subsort ABBNV{X} < ABB{X} . subsort ABB{X} < ArbolBin{VOrden}{X} . subsort ABBNV{X} < ArbolBinNV{VOrden}{X} . *** generadores op crear : -> ArbolBin{X} [ctor] . op arbolBin : X$Elt ArbolBin{X} ArbolBin{X} -> ArbolBinNV{X} [ctor] . endfm

con la siguiente teoría de orden:

fth ORDEN is protecting BOOL . sort Elt . *** operaciones op _<_ : Elt Elt -> Bool . endfth

para que un árbol binario pertenezca al tipo árbol binario de búsquedadebe cumplir la condición de ordenación siguiente que iría junto al módulo ARBOL-BINARIO-BUSQUEDA:

var R : X$Elt . vars INV DNV : ABBNV{X} . vars I D : ABB{X} . mb crear : ABB{X} .

Page 23: 6.1 Definición

mb arbolBin(R, crear, crear) : ABBNV{X} . cmb arbolBin(R, INV, crear) : ABBNV{X} if R > max(INV) . cmb arbolBin(R, crear, DNV) : ABBNV{X} if R < min(DNV) . cmb arbolBin(R, INV, DNV) : ABBNV{X} if (R > max(INV)) and (R < min(DNV)) . ops min max : ABBNV{X} -> X$Elt . eq min(arbolBin(R, crear, D)) = R . eq min(arbolBin(R, INV, D)) = min(INV) . eq max(arbolBin(R, I, crear)) = R . eq max(arbolBin(R, I, DNV)) = max(DNV) .

Implementación en Python[editar]class nodo: izq , der, dato = None, None, 0 def __init__(self, dato): # crea un nodo self.izq = None self.der = None self.dato = datoclass arbolBinario: def __init__(self): # inicializa la raiz self.raiz = None def agregarNodo(self, dato): # crea un nuevo nodo y lo devuelve return nodo(dato) def insertar(self, raiz, dato): # inserta un dato nuevo en el árbol if raiz == None: # si no hay nodos en el árbol lo agrega return self.agregarNodo(dato) else: # si hay nodos en el árbol lo recorre if dato <= raiz.dato: # si el dato ingresado es menor que el dato guardado va al subárbol izquierdo raiz.izq = self.insertar(raiz.izq, dato) else:

Page 24: 6.1 Definición

# si no, procesa el subárbol derecho raiz.der = self.insertar(raiz.der, dato) return raiz

Operaciones[editar]Todas las operaciones realizadas sobre árboles binarios de búsqueda están basadas en la comparación de los elementos o clave de los mismos, por lo que es necesaria una subrutina, que puede estar predefinida en el lenguaje de programación, que los compare y pueda establecer una relación de orden entre ellos, es decir, que dados dos elementos sea capaz de reconocer cual es mayor y cual menor. Se habla de clave de un elemento porque en la mayoría de los casos el contenidode los nodos será otro tipo de estructura y es necesario que la comparación se haga sobre algún campo al que se denomina clave.

Búsqueda[editar]La búsqueda Silaina consiste en acceder a la raíz del árbol, si el elemento a localizar coincide con éste la búsqueda ha concluido con éxito, si el elemento es menor se busca en el subárbol izquierdo y si es mayor en el derecho. Si se alcanza un nodo hoja y el elemento no hasido encontrado se supone que no existe en el árbol. Cabe destacar quela búsqueda en este tipo de árboles es muy eficiente, representa unafunción logarítmica. El máximo número de comparaciones que necesitaríamos para saber si un elemento se encuentra en un árbol binario de búsqueda estaría entre [log2(N+1)] y N, siendo N el número de nodos. La búsqueda de un elemento en un ABB (Árbol Binario de Búsqueda) se puede realizar de dos formas, iterativa o recursiva.Ejemplo de versión iterativa en el lenguaje de programación C, suponiendo que estamos buscando una clave alojada en un nodo donde está el correspondiente "dato" que precisamos encontrar:data Buscar_ABB(abb t,clave k) { abb p; dato e; e=NULL; p=t; if (!estaVacio(p)) { while (!estaVacio(p) && (p->k!=k) ) { if (k < p->k) { p=p->l;

Page 25: 6.1 Definición

} if (p->k < k) { p=p->r; } } if (!estaVacio(p) &&(p->d!=NULL) ) { e=copiaDato(p->d); } } return e;}Véase ahora la versión recursiva en ese mismo lenguaje:int buscar(tArbol *a, int elem){ if (a == NULL) { return 0; } else if (a->clave < elem) { return buscar(a->hDerecho, elem); } else if (a->clave > elem) { return buscar(a->hIzquierdo, elem); } else { return 1; }}Otro ejemplo en Python:def buscar(raiz, clave):# busca el valor clave dentro del arbolif raiz == None: print 'No se encuentra'else: # if clave == raiz.dato:

print 'El valor ',clave,' se encuentra en el ABB' elif clave < raiz.dato:

Page 26: 6.1 Definición

# lado izquierdoreturn buscar(raiz.izq, clave)

else:# lado derechoreturn buscar(raiz.der, clave)

En Pascal:Function busqueda(T:ABR, y: integer):ABRbegin if (T=nil) or (^T.raiz=y) then busqueda:=T; else if (^T.raiz<y) then busqueda:=busqueda(^T.dch,y); else busqueda:=busqueda(^T.izq,y);end;Una especificación en maude para la operación de búsqueda quedaría de la siguiente forma:

op esta? : X$Elt ABB{X} -> Bool . var R R1 R2 : X$Elt . vars I D : ABB{X} . eq esta?(R, crear) = false . eq esta?(R1, arbolBin(R2, I, D)) = if R1 == R2 then true else if R1 < R2 then esta?(R1, I) else esta?(R1, D) fi fi .

Inserción[editar]La inserción es similar a la búsqueda y se puede dar una solución tanto iterativa como recursiva. Si tenemos inicialmente como parámetro un árbol vacío se crea un nuevo nodo como único contenido el elemento a insertar. Si no lo está, se comprueba si el elemento dado es menor que la raíz del árbol inicial con lo que se inserta en el subárbol izquierdo y si es mayor se inserta en el subárbol derecho.

Page 27: 6.1 Definición

Evolución de la inserción del elemento "5" en un ABB.

Como en el caso de la búsqueda puede haber varias variantes a la hora de implementar la inserción en el TAD (Tipo Abstracto de Datos), y es la decisión a tomar cuando el elemento (o clave del elemento) a insertar ya se encuentra en el árbol, puede que éste sea modificado o que sea ignorada la inserción. Es obvio que esta operación modifica elABB perdiendo la versión anterior del mismo.A continuación se muestran las dos versiones del algoritmo en pseudolenguaje, iterativa y recursiva, respectivamente.

PROC InsertarABB(árbol:TABB; dato:TElemento)VARIABLES nuevonodo,pav,pret:TABB clavenueva:Tclave ele:TElementoINICIO nuevonodo <- NUEVO(TNodoABB) nuevonodo^.izq <- NULO nuevonodo^.der <- NULO nuevonodo^.elem <- dato SI ABBVacío (árbol) ENTONCES árbol <- nuevonodo ENOTROCASO clavenueva <- dato.clave pav <- árbol // Puntero Avanzado pret <- NULO // Puntero Retrasado MIENTRAS (pav <- NULO) HACER pret <- pav

Page 28: 6.1 Definición

ele = pav^.elem SI (clavenueva < ele.clave ) ENTONCES pav <- pav^.izq EN OTRO CASO pav <- pav^.dch FINSI FINMIENTRAS ele = pret^.elem SI (clavenueva < ele.clave ) ENTONCES pret^.izq <- nuevonodo EN OTRO CASO pret^.dch <- nuevonodo FINSI FINSIFINPROC InsertarABB(árbol:TABB; dato:TElemento)VARIABLES ele:TElementoINICIO SI (ABBVacío(árbol)) ENTONCES árbol <- NUEVO(TNodoABB) árbol^.izq <- NULO árbol^.der <- NULO árbol^.elem <- dato EN OTRO CASO ele = InfoABB(árbol) SI (dato.clave < ele.clave) ENTONCES InsertarABB(árbol^.izq, dato) EN OTRO CASO InsertarABB(árbol^.dch, dato) FINSI FINSIFIN

Se ha podido apreciar la simplicidad que ofrece la versión recursiva, este algoritmo es la traducción en C. El árbol es pasado por referenciapara que los nuevos enlaces a los subárboles mantengan la coherencia.void insertar(tArbol **a, int elem){

Page 29: 6.1 Definición

if (*a == NULL) { *a = (tArbol *) malloc(sizeof(tArbol)); (*a)->clave = elem; (*a)->hIzquierdo = NULL; (*a)->hDerecho = NULL; } else if ((*a)->clave < elem) insertar(&(*a)->hDerecho, elem); else if ((*a)->clave > elem) insertar(&(*a)->hIzquierdo, elem);}En Python el mecanismo de inserción se define, por ejemplo, dentro de la clase que defina el ABB (ver más arriba).Otro ejemplo en Pascal:Procedure Insercion(var T:ABR, y:integer)var ultimo:ABR; actual:ABR; nuevo:ABR;begin ultimo:=nil; actual:=T; while (actual<>nil) do begin ultimo:=actual; if (actual^.raiz<y) then actual:=actual^.dch else actual:=actual^.izq; end; new(nuevo); ^nuevo.raiz:=y; ^nuevo.izq:=nil; ^nuevo.dch:=nil; if ultimo=nil then T:=nuevo else if ultimo^.raiz<y then ultimo^.dch:=nuevo else ultimo^.izq:=nuevo;end;

Page 30: 6.1 Definición

Véase también un ejemplo de algoritmo recursivo de inserción en un ABBen el lenguaje de programación Maude:

op insertar : X$Elt ABB{X} -> ABBNV{X} . var R R1 R2 : X$Elt . vars I D : ABB{X} . eq insertar(R, crear) = arbolBin(R, crear, crear) . eq insertar(R1, arbolBin(R2, I, D)) = if R1 < R2 then arbolBin(R2, insertar(R1, I), D) else arbolBin(R2, I, insertar(R1, D)) fi .

La operación de inserción requiere, en el peor de los casos, un tiempoproporcional a la altura del árbol.

Borrado[editar]La operación de borrado no es tan sencilla como las de búsqueda e inserción. Existen varios casos a tener en consideración:

Borrar un nodo sin hijos o nodo hoja: simplemente se borra y se establece a nulo el apuntador de su padre.

Nodo a eliminar 74

Borrar un nodo con un subárbol hijo: se borra el nodo y se asigna su subárbol hijo como subárbol de su padre.

Page 31: 6.1 Definición

Nodo a eliminar 70

Borrar un nodo con dos subárboles hijo: la solución está en reemplazar el valor del nodo por el de su predecesor o por el de susucesor en inorden y posteriormente borrar este nodo. Su predecesoren inorden será el nodo más a la derecha de su subárbol izquierdo (mayor nodo del subarbol izquierdo), y su sucesor el nodo más a la izquierda de su subárbol derecho (menor nodo del subarbol derecho).En la siguiente figura se muestra cómo existe la posibilidad de realizar cualquiera de ambos reemplazos:

Nodo a eliminar 59

El siguiente algoritmo en C realiza el borrado en un ABB. El procedimiento reemplazar busca la mayor clave del subárbol izquierdo y la asigna al nodo a eliminar.void reemplazar(tArbol **a, tArbol **aux); /*Prototipo de la funcion ''reemplazar''*/void borrar(tArbol **a, int elem){ tArbol *aux; if (*a == NULL) return; if ((*a)->clave < elem) borrar(&(*a)->hDerecho, elem); else if ((*a)->clave > elem) borrar(&(*a)->hIzquierdo, elem); else if ((*a)->clave == elem) { aux = *a; if ((*a)->hIzquierdo == NULL) *a = (*a)->hDerecho; else if ((*a)->hDerecho == NULL) *a = (*a)->hIzquierdo;

Page 32: 6.1 Definición

else reemplazar(&(*a)->hIzquierdo, &aux); free(aux); }} void reemplazar(tArbol **a, tArbol **aux){ if ((*a)->hDerecho == NULL) { (*aux)->clave = (*a)->clave; *aux = *a; *a = (*a)->hIzquierdo; } else reemplazar(&(*a)->hDerecho, & aux);}Otro ejemplo en Pascal.Procedure Borrar(var T:ABR, x:ABR)var aBorrar:ABR; anterior:ABR; actual:ABR; hijo:ABR;begin if (^x.izq=nil) or (^x.dch=nil) then aBorrar:=x; else aBorrar:=sucesor(T,x); actual:=T; anterior:=nil; while (actual<>aBorrar) do begin anterior:=actual; if (^actual.raiz<^aBorrar.raiz) then actual:=^actual.dch; else actual:=^actual.izq; end; if (^actual.izq=nil) then hijo:=^actual.dch; else hijo:=^actual.izq;

Page 33: 6.1 Definición

if (anterior=nil) then T:=hijo; else if (^anterior.raiz<^actual.raiz) then ^anterior.dch:=hijo; else ^anterior.izq:=hijo; if (aBorrar<>x) then ^x.raiz:=^aBorrar.raiz; free(aBorrar);end;Véase también un ejemplo de algoritmo recursivo de borrado en un ABB en el lenguaje de programación Maude, considerando los generadores crear y arbolBin. Esta especificación hace uso de la componente clave a partir de la cual se ordena el árbol.

op eliminar : X$Elt ABB{X} -> ABB{X} . varS R M : X$Elt . vars I D : ABB{X} . vars INV DNV : ABBNV{X} . ops max min : ArbolBin{X} -> X$Elt . eq min(arbolBin(R, crear, D)) = R . eq max(arbolBin(R, I, crear)) = R . eq min(arbolBin(R, INV, D)) = min(INV) . eq max(arbolBin(R, I, DNV )) = max(DNV) . eq eliminar(M, crear) = crear . ceq eliminar(M, arbolBin(R, crear, D)) = D if M == clave(R) . ceq eliminar(M, arbolBin(R, I, crear)) = I if M == clave(R) . ceq eliminar(M, arbolBin(R, INV, DNV)) = arbolBin(max(INV), eliminar(clave(max(INV)), INV), DNV) if M == clave(R) . ceq eliminar(M, arbolBin(R, I, D)) = arbolBin(R, eliminar(M, I), D) if M < clave(R) . ceq eliminar(M, arbolBin(R, I, D)) = arbolBin(R, I, eliminar(M, D)) if clave(R) < M .

Otras Operaciones[editar]Otra operación sería por ejemplo comprobar que un árbol binario es un árbol binario de búsqueda. Su implementación en maude es la siguiente:

op esABB? : ABB{X} -> Bool .

Page 34: 6.1 Definición

var R : X$Elt . vars I D : ABB{X} . eq esABB?(crear) = true . eq esABB?(arbolbBin(R, I, D)) = (Max(I) < R) and (Min(D) > R) and (esABB?(I)) and (esABB?(D)) .

Recorridos[editar]Se puede hacer un recorrido de un árbol en profundidad o en anchura.Los recorridos en anchura son por niveles, se realiza horizontalmente desde la raíz a todos los hijos antes de pasar a la descendencia de alguno de los hijos.El coste de recorrer el ABB es O(n), ya que se necesitan visitar todoslos vértices.El recorrido en profundidad lleva al camino desde la raíz hacia el descendiente más lejano del primer hijo y luego continúa con el siguiente hijo. Como recorridos en profundidad tenemos inorden, preorden y postorden.Una propiedad de los ABB es que al hacer un recorrido en profundidad inorden obtenemos los elementos ordenados de forma ascendente.

Ejemplo árbol binario de búsqueda

Resultado de hacer el recorrido en:Inorden = [6, 9, 13, 14, 15, 17, 20, 26, 64, 72].Preorden = [15, 9, 6, 14, 13, 20, 17, 64, 26, 72].Postorden =[6, 13, 14, 9, 17, 26, 72, 64, 20, 15].

Recorridos en Visual Basic .Net

Page 35: 6.1 Definición

'función de recorrido en PREORDEN Public Function preorden() As String cadenasalida = "" rePreorden(raíz) Return cadenasalida End Function Private Sub rePreorden(ByVal padre As Nodo) If IsNothing(padre) Then Return End If cadenasalida = cadenasalida & "-" & padre.dato rePreorden(padre.ant) rePreorden(padre.sig) End Sub 'función de recorrido en POSTORDEN Public Function postorden() As String cadenasalida = "" reposorden(raíz) Return cadenasalida End Function Private Sub repostorden(ByVal padre As Nodo) If IsNothing(padre) Then Return End If repostorden(padre.ant) repostorden(padre.sig) cadenasalida = cadenasalida & "-" & padre.dato End Sub 'función de recorrido en ENORDEN Public Function inorden() As String cadenasalida = "" reinorden(raíz) Return cadenasalida End Function Private Sub reinorden(ByVal padre As Nodo) If IsNothing(padre) Then Return End If reinorden(padre.ant) cadenasalida = cadenasalida & "-" & padre.dato

Page 36: 6.1 Definición

reinorden(padre.sig) End Sub

Recorridos en C con funciones recursivasstruct Nodo{ char nombre[30]; struct Nodo *izq; struct Nodo *der;}; typedef struct Nodo Nodo;typedef Nodo *Arbol; void preOrden(Arbol abb){ if(abb) { printf("%s\n", abb->nombre); preOrden(abb->izq); preOrden(abb->der); }} void postOrden(Arbol abb){ if(abb) { postOrden(abb->izq); postOrden(abb->der); printf("%s\n", abb->nombre); }} void inOrden(Arbol abb){ if(abb) { inOrden(abb->izq); printf("%s\n", abb->nombre); inOrden(abb->der); }}

Page 37: 6.1 Definición

Tipos de árboles binarios de búsqueda[editar]Hay varios tipos de árboles binarios de búsqueda. Los árboles AVL, árbol rojo-negro, son árboles autobalanceables . Los árbol biselado son árboles también autobalanceables con la propiedad de que los elementos accedidos recientemente se accederá más rápido en posteriores accesos. En el montículo como en todos los árboles binarios de búsqueda cada nodo padre tiene un valor mayor que sus hijos y además es completo, esto es cuando todos los niveles están llenos con excepción del último que puede no estarlo, por último, en lo montículos, cada nodo mantiene una prioridad y siempre, un nodo padre tendrá una prioridad mayor a la de su hijo.Otras dos maneras de configurar un árbol binario de búsqueda podría ser como un árbol completo o degenerado.Un árbol completo es un árbol con "n" niveles, donde cada nivel d <= n-1; el número de nodos existentes en el nivel "d" es igual que 2d. Esto significa que todos los posibles nodos existen en esos niveles, no hay ningún hueco. Un requirimiento adicional para un árbol binario completo es que para el nivel "n", los nodos deben estar ocupados de izquierda a derecha, no pudiendo haber un hueco a la izquierda de un nodo ocupado.Un árbol degenerativo es un árbol que, para cada nodo padre, sólo hay asociado un nodo hijo. Por lo que se comporta como una lista enlazada.

Comparación de rendimiento[editar]D. A. Heger(2004)1 realiza una comparación entre los diferentes tipos de árboles binarios de búsqueda para encontrar que tipo nos daría el mejor rendimiento para cada caso. Los montículos se encuentran como eltipo de árbol binario de búsqueda que mejor resultado promedio da, mientras que los árboles rojo-negro los que menor rendimiento medio nos aporta.

Buscando el Árbol binario de búsqueda óptimo[editar]Si nosotros no tenemos en mente planificar un árbol binario de búsqueda, y sabemos exactamente como de frecuente serán visitados cadaelemento podemos construir un árbol binario de búsqueda óptimo con lo que conseguiremos que la media de gasto generado a la hora de buscar un elemento sea minimizado.Asumiendo que conocemos los elementos y en qué nivel está cada uno, también conocemos la proporción de futuras búsquedas que se harán para

Page 38: 6.1 Definición

encontrar dicho elemento. Si es así, podemos usar una solución basada en la programación dinámica.En cambio, a veces sólo tenemos la estimación de los costes de búsqueda, como pasa con los sistemas que nos muestra el tiempo que ha necesitado para realizar una búsqueda. Un ejemplo, si tenemos un ABB de palabras usado en un corrector ortográfico, deberíamos balancear elárbol basado en la frecuencia que tiene una palabra en el Corpus lingüístico, desplazando palabras como "de" cerca de la raíz y palabras como "vesánico" cerca de las hojas. Un árbol como tal podría ser comparado con los árboles Huffman que tratan de encontrar elementos que son accedidos frecuentemente cerca de la raíz para producir una densa información; de todas maneras, los árboles Huffman sólo puede guardar elementos que contienen datos en las hojas y estos elementos no necesitan ser ordenados.En cambio, si no sabemos la secuencia en la que los elementos del árbol van a ser accedidos, podemos usar árboles biselados que son tan buenos como cualquier árbol de búsqueda que podemos construir para cualquier secuencia en particular de operaciones de búsqueda.Árboles alfabéticos son árboles Huffman con una restricción de orden adicional, o lo que es lo mismo, árboles de búsqueda con modificación tal que todos los elementos son almacenados en las hojas.

Especificación del Árbol BinarioCrearArbolEs vacíoConstruirRaízInicia el árbol como vacíoCrea un árbol con un elemento raíz y dos ramas, izquierda y derecha que son, a su vez, árbolesComprueba si el árbol no tiene nodosDevuelve el nodo raíz

14. Especificación del Árbol BinarioPerteneceBorrarDerechoIzquierdoObtiene la rama subárbol izquierdo de un árbol dadoObtiene la rama subárbol Derecho de un árbol dadoElimina del árbol el nodo con un elemento determinadoDetermina si un elemento se encuentra en el árbol

Page 39: 6.1 Definición

Estructuras de datos dinámicasÁrboles< Estructuras de datos dinámicas

Contenido  [ocultar] 

1   Árboles o 1.1   Definición no recursiva o 1.2   Definición recursiva o 1.3   Definiciones o 1.4   Operaciones básicas o 1.5   Implementación primer hijo - siguiente hermano o 1.6   Árboles binarios

1.6.1   Árboles binarios de búsqueda 1.6.1.1   Operación “buscar” 1.6.1.2   Operación “insertar” 1.6.1.3   Operación “recorrer” 1.6.1.4   Operación “borrado”

1.6.2   Árboles binarios perfectamente equilibrados 1.6.3   Árboles equilibrados

1.6.3.1   Factor de equilibrio (FE) de un nodo 1.6.3.2   Inserciones en los “márgenes” 1.6.3.3   Inserciones por “dentro”

1.6.4   Árboles rojinegros 1.6.4.1   Altura de un ARN

1.6.4.1.1   Demostración 1.6.4.2   Operaciones 1.6.4.3   Reparación del balance del árbol 1.6.4.4   Inserción ascendente

1.6.4.4.1   Reparación del balance 1.6.4.5   Inserción descendente

Page 40: 6.1 Definición

o 1.7   Árboles B 1.7.1   Relación entre los árboles B y los árboles rojinegros 1.7.2   Búsqueda 1.7.3   Inserción 1.7.4   Borrado

o 1.8   Árboles B+ 1.8.1   Búsqueda por clave 1.8.2   Inserción 1.8.3   Eliminación

Árboles[editar]Árbol: estructura no lineal y dinámica de datos. Dinámica: puede cambiar durante la ejecución de un programa. No lineal: a cada elemento del árbol pueden seguirle varios elementos. Están formados por un conjunto de nodos y un conjunto de aristas que conectan pares de nodos.

Definición no recursiva[editar]Conjunto de nodos y conjunto de aristas que conectan pares de nodos con las siguientes características:

Se distingue un nodo raíz (no tiene padre). A cada nodo c (excepto la raíz) le llega una arista desde

exactamente un nodo p diferente a c, al cual se le llama padre de c.

Hay un único camino desde la raíz hasta cada nodo. La misma longitud del camino es su número de aristas.

Definición recursiva[editar]Un árbol es o bien vacío o consiste en una raíz y cero o más subárboles no vacíos  ,  ,…,  , cada una de cuyas raíces está conectada por medio de una arista con la raíz.

Definiciones[editar]

Los nodos que no tienen hijos se denominan hojas. Un árbol con N nodos debe tener (N-1) aristas.

Page 41: 6.1 Definición

La profundidad de la raíz es 0 y la de cualquier nodo es la de su padre más 1.

La altura de un nodo es 1 más que la mayor altura de un hijo suyo. La altura de un árbol es la altura de la raíz.

Los nodos que tienen el mismo padre son hermanos. Si hay un camino del nodo u al nodo v, u es ascendiente de v y v es

descendiente de u. Si u   v son propios. El tamaño de un nodo es el número de descendientes (incluido él

mismo). El tamaño de un árbol es el tamaño de su raíz.

Operaciones básicas[editar]

Insertar Buscar Eliminar Ir a la raíz Recorrer

Implementación primer hijo - siguiente hermano[editar]Consiste en mantener los hijos de cada nodo en una lista enlazada. Cada nodo tiene dos referencias: una a su hijo más a la izquierda y otra a su hermano de la derecha.

Árboles binarios[editar]Un árbol binario es o bien vacío o consta de una raíz, un hijo árbol binario izquierdo y otro derecho. Los árboles binarios de búsqueda permiten inserciones y acceso a los elementos en tiempo logarítmico. Los árboles binarios llamados colas con prioridad soportan acceso y eliminación del mínimo de una colección de elementos.

Árboles binarios de búsqueda[editar]Para todo nodo A del árbol:

Todos los valores de los nodos del subárbol izquierdo de A deben ser menores al valor del nodo A.

Page 42: 6.1 Definición

Todos los valores de los nodos del subárbol derecho de A deben ser mayores o iguales al valor del nodo A. Un recorrido en inorden del árbol proporciona una lista en orden ascendente de los valores almacenados en los nodos. Para describir las operaciones, se considera que estas se enmarcan dentro de la clase NodoBinario. El lenguaje utilizado es JAVA.

Operación “buscar”[editar]

public boolean buscar(Object o) { if (o.equals(valor)) return true; else if (o.compareTo(valor)<0) return buscar(getIzq(),o); else return buscar(getDer(),o); }

Operación “insertar”[editar]

public NodoBinario insertar(Comparable o){ if (o.compareTo(valor)<0) setIzq(insertar(getIzq(),o)); else setDer(insertar(getDer(),o)); return this; }

Dentro de la clase NodoBinarioVacio:

public NodoBinario insertar(Comparable o) { return new NodoBinario(o); }

Operación “recorrer”[editar]

Los recorridos pueden ser en preorden, postorden o inorden (orden simétrico). Todos son O(N).

public void preOrder(SList aList)

Page 43: 6.1 Definición

{ aList.addElement(value); left.preOrder(aList); right.preOrder(aList); } public void inOrder(SList aList) { left.inOrder(aList); aList.addElement(value); right.inOrder(aList); } public void posOrder(SList aList) { left.posOrder(aList); right.posOrder(aList); aList.addElement(value); }

Los recorridos no necesitan obligatoriamente recursividad, se puede emplear una pila para realizarlos iterativamente.Operación “borrado”[editar]

El nodo a borrar debe ser reemplazado por el nodo más a la derecha en el subárbol izquierdo o el nodo más a la izquierda en el subárbol derecho (el nodo más a la derecha del subárbol izquierdo será mayor o igual que cualquier otro nodo de ese subárbol y menor que todos los del subárbol derecho, y el nodo más a la izquierda del subárbol derecho será menor que todos los demás nodos de ese subárbol y mayor que todos los del subárbol izquierdo). Para el caso en el que el nodo elegido tengo un subárbol, hay por lo menos tres soluciones posibles:

La primera consiste en conservar la estructura del subárbol, y colgar del elemento ubicado en el extremo (el elemento menor o mayor) correspondiente al subárbol donde se encuentra el elemento apromover hacia la raíz (en este ejemplo, el subárbol izquierdo, porlo cual se buscará el elemento más a la izquierda), lo cual es consistente, porque todos los elementos en el subárbol promovido serán mayores que los del subárbol del cual estaban colgados a la derecha. El inconveniente que presenta esta solución es que debe utilizarse una función encontrarMínimo() o encontrarMáximo().

Page 44: 6.1 Definición

La segunda solución consiste en colgar del padre del nodo promovidohacia la raíz, el subárbol remanente. Esto es consistente, porque todo elemento del subárbol derecho de un nodo será mayor que el valor de ese nodo, y viceversa. Estas soluciones aprovechan la ventaja de contar con que el nodo promovido tiene, a lo sumo, un subárbol.

Un hueco dejado por un nodo promovido también puede pensarse como una eliminación. un arbol es un arbol jajaja

Árboles binarios perfectamente equilibrados[editar]La eficiencia de las operaciones depende exclusivamente de la altura del árbol. Para un árbol de N nodos perfectamente equilibrado el costede acceso es de orden logarítmico: O(log N). Sin embargo, se dice que si el árbol crece o decrece descontroladamente, el rendimiento puede disminuir considerablemente, siendo para el caso más desfavorable (insertar un conjunto de claves ordenadas en forma ascendente o descendente) el coste de acceso: O(N). En un árbol binario perfectamente equilibrado, el número de nodos en el subárbol izquierdoy el número de nodos en el subárbol derecho, difieren como mucho en una unidad, y los subárboles son también equilibrados

Árboles equilibrados[editar]Un procedimiento de inserción que siempre restaure la estructura del árbol a un equilibrio perfecto es poco eficiente. Se usa una formulación menos estricta de “equilibrio”: Un árbol está equilibrado si para cada uno de sus nodos ocurre que las alturas de sus dos subárboles difieren como mucho en 1(Árboles AVL).Factor de equilibrio (FE) de un nodo[editar]

El FE es la altura del subárbol izquierdo menos la altura del subárbolderecho. Los valores que puede tomar son -1, 0, 1. Si llegara a tomar los valores -2 o 2 debe reestructurarse el árbol. Todos los árboles perfectamente equilibrados son AVL. La longitud de camino media es prácticamente idéntica a la de un árbol perfectamente equilibrado. En un AVL se puede realizar con complejidad del O(log N) las siguientes operaciones:

Encontrar un nodo con una clave dada. Insertar un nodo con una clave dada.

Page 45: 6.1 Definición

Borrar un nodo con una clave dada. Un árbol AVL de altura H tiene por lo menos (Fibonacci(H+3) -1) nodos. Los pasos necesarios para insertar un nodo en un árbol AVL son:

Agregar el nodo como en un árbol binario de búsqueda. En el regreso por el camino de búsqueda se comprueba el FE de los

nodos. Si un nodo presenta un FE incorrecto (2 o -2) se reestructura el

árbol y se continúa el ascenso hasta llegar a la raíz. Casos en situación de reestructurar:

1. Una inserción en el subárbol izquierdo del hijo izquierdo de X.2. Una inserción en el subárbol derecho del hijo izquierdo de X.3. Una inserción en el subárbol izquierdo del hijo derecho de X.4. Una inserción en el subárbol derecho del hijo derecho de X.

Inserciones en los “márgenes”[editar]

1 y 4: inserciones en los “márgenes”: rotación simple: intercambia lospapeles de los padres y de los hijos, manteniendo la ordenación.

// Rotación izquierda - izquierda private static NodoAVL rotarConHijoIzq(NodoAVL A) { NodoAVL B = (NodoAVL) A.getIzq(); //Asigna nombre A.setIzq(B.getDer()); B.setDer(A); return B; }

Inserciones por “dentro”[editar]

2 y 3: inserciones por “dentro”: rotación doble. Notar que una rotación simple no resuelve el problema, ya que la rama que provocó eldesequilibrio se descuelga del nodo promovido y se cuelga al nodo que desciende un nivel, de manera que se mantiene con la misma profundidad, que es la que provocó el desequilibrio. Por lo tanto, antes de rotar, debe desplazarse el desequilibrio a la rama correspondiente, es decir, transformamos el caso de una inserción por dentro, a un caso de inserción en el margen, utilizando una rotación.

Page 46: 6.1 Definición

// Rotación izquierda - derecha private static NodoAVL rotarDobleConHijoIzq(NodoAVL A) { NodoAVL B = (NodoAVL) A.getIzq(); //Asigna nombre A.setIzq(rotarConHijoDer((NodoAVL) B)); return rotarConHijoIzq(A); }

Como se ve, el problema se convirtió en un problema igual al del primer caso. Los pasos necesarios para suprimir un nodo en un árbol AVL son:

Suprimir el nodo como en un árbol binario de búsqueda. En el regreso por el camino de supresión se comprueba el FE de los

nodos. Si un nodo presenta un FE incorrecto (2 o -2) se reestructura el

árbol y se continúa el ascenso hasta llegar a la raíz. La reestructuración se efectúa cuando al regresar por el camino de búsqueda después de una inserción o una supresión se comprueba que la condición del FE se ha violado. Una supresión puede provocar varias reestructuraciones. En la práctica se utilizan otros esquemas de equilibrio como los árboles rojinegros: como en los AVLlas operaciones son logarítmicas en el peor caso. La ventaja es quelas inserciones y eliminaciones pueden realizarse con un único recorrido descendente.

Árboles rojinegros[editar]Árbol binario de búsqueda, donde cada nodo está coloreado con los colores rojo o negro, y se verifican las siguientes propiedades:1. La raíz es negra.2. Si un nodo es rojo, sus hijos deben ser negros.3. Todos los caminos desde un nodo a un nodo vacío deben contener el mismo número de nodos negros.Las condiciones (2) y (3) aseguran que el árbol nunca esté demasiado desbalanceado. (2) asegura que no puedan haber demasiados nodos rojos,y (3) dice que, despreciando el número de nodos rojos, que es limitado, el árbol es perfectamente balanceado. La condición (1) es trivial: si la raíz es roja, simplemente se colorea negra, ya que esto

Page 47: 6.1 Definición

no violará ninguna regla. En los ARN la operación eliminar se complica. Cuando se necesitan árboles equilibrados y se requieren muchas eliminaciones se emplean los AA-Árboles que añaden una condición adicional a las impuestas por los ARN:4. Los hijos izquierdos no pueden ser rojos.Altura de un ARN[editar]

En un ARN con n nodos, la altura h será: 

Demostración[editar]La condición (3) nos permite asegurar que, despreciando el número de nodos rojos, el árbol es perfectamente balanceado, y, en virtud de esacaracterística, su altura  . La condición (2) evita que haya nodos rojos consecutivos, como máximo, la mitad de los nodos de un camino -que constituirán una altura- serán rojos.Operaciones[editar]

Se pueden realizar operaciones de búsqueda con complejidad O(log N), por lo expuesto anteriormente. Al realizar una inserción, la complejidad de búsqueda será O(log N), pero aparece un problema: el resultado será un árbol de búsqueda binario, pero no necesariamente unARN. Si coloreamos el nuevo nodo rojo, el balance de negros quedará intacto, pero se puede incurrir en una violación rojo-rojo. Si lo coloreamos negro, no se incurrirá en una violación rojo-rojo, pero en este caso, siempre alteraremos el balance de negros. Al eliminar, si el nodo a eliminar es negro, ambas violaciones pueden aparecer.Reparación del balance del árbol[editar]

Una vez detectada una violación, se deben tomar medidas que reparen elbalance del árbol. Estas medidas utilizan dos herramientas: rotacionesy cambios de color.Inserción ascendente[editar]

Sea X la nueva hoja añadida, P su padre, S el hermano de P (si existe)y G el abuelo.1. Los nuevos nodos se insertan en el árbol como hojas de color rojo.2. Si el padre es negro, hemos acabado.3. Si el padre es rojo violamos la regla 2, entonces debemos modificarel árbol de forma que se cumpla la regla (2) sin introducir violaciones de la propiedad (3).4. Si el padre P es la raíz, se colorea negro. La altura negra aumentaen 1, pero el balance se preserva. Hemos acabado.

Page 48: 6.1 Definición

Reparación del balance[editar]Asumiendo que P no es la raíz5. Si S es rojo, se puede aplicar un cambio de color: Se elimina así la violación, y el balance de negros se mantiene. ¿Qué pasa si el padre de G es también rojo? Solución: propagar este procedimiento hacia arriba hasta conseguir que no haya dos nodos rojos consecutivos o alcanzar la raíz. Esta propagación es análoga a la que se hace en los árboles AVL.6. Si S es negro, tenemos dos casos, con sus simétricos asociados: violación en el margen, y violación por dentro.7. Si es una violación es por dentro, la convertimos a una violación en el margen haciendo una rotación: Como X y P son rojos, no añaden nada en el balance de negros a los caminos que pasan por g, de manera que el balance se preserva.8. Teniendo el caso de violación por dentro, se efectúa una rotación simple. La rama izquierda de desbalancea, le falta un nodo negro para todos sus caminos, y aún tenemos la violación rojo-rojo. Solucionamos ambos problemas haciendo un cambio de color.Inserción descendente[editar]

Objetivo: garantizar que en el momento de la inserción S no sea rojo, de manera que sólo haya que añadir una hoja roja y, si fuere necesario, realizar una rotación (simple o doble). En el camino descendente, si un nodo X tiene dos hijos rojos, el color de X cambia a rojo y el de sus dos hijos a negro. El número de nodos negros en loscaminos por debajo de X permanece inalterable. Si X es la raíz, la convertiríamos en roja, hay que volver a negro (esto no puede violar ninguna de las reglas). Si el padre de X es rojo, hay que aplicar rotación simple o doble. ¿Qué pasa si el hermano del padre de X es también rojo? Esta situación NO puede darse, gracias al proceso efectuado en el camino descendiente.

Árboles B[editar]Mientras que la altura de un árbol binario completo es, aproximadamente,  , la altura de un árbol M-ario completo es, máso menos,  . Un B-árbol de orden M es un árbol M-ario que verifica:

Cada página, excepto la página raíz y las páginas hojas, tienen entre M/2 y M descendientes, y entre (M/2 -1) y (M-1) elementos.

Page 49: 6.1 Definición

La página raíz, o es una hoja o tiene entre 2 y M descendientes. Las páginas hojas están todas al mismo nivel.Relación entre los árboles B y los árboles rojinegros[editar]Si juntamos un nodo negro con sus hijos rojos, si los hubiere, en un mismo nodo, se obtiene un árbol no binario con altura igual a la altura negra, con un máximo de 3 elementos y 4 hijos, y un mínimo de un elemento; en definitiva, es un árbol B de orden 4.

Búsqueda[editar]Debe tenerse en memoria principal la página sobre la cual vamos a buscar. Considérese el elemento a buscar x. Si la búsqueda es infructuosa dentro de la página se estará en una de las siguientes situaciones:

 para 1 ≤i < n. La búsqueda continúa en la página  . . La búsqueda continúa en la página  . . La búsqueda continúa en la página . Si en algún caso la

referencia es nula, es decir, si no hay página descendiente, entonces no hay ningún elemento x en todo el árbol y se acaba la búsqueda.

Inserción[editar]Siempre se inserta en los nodos hojas. Primero se comprueba que la clave no se encuentre en el árbol. Si la cantidad de elementos es menor que 2n:

Se inserta en forma secuencial la clave. Si la cantidad de elementos es 2n:

Los 2n+1 elementos se dividen en dos páginas, excluyendo la clave del medio.

La clave del medio se inserta en el nodo padre.Borrado[editar]Si la clave a ser borrada no está en una hoja, su predecesor inmediatotiene que estar en una hoja (esto se debe a que todas las hojas tienenel mismo nivel, de manera que si existe un valor menor en un nodo más abajo, también tiene que haber uno mayor), y se puede promover, y así borrar el espacio que estaba en la hoja. Si la hoja queda con menos den elementos, se comprueba si se puede promover un elemento de un

Page 50: 6.1 Definición

hermano adyacente a su padre, y bajar el del padre a la hoja. Si el hermano tiene sólo n elementos, las dos hojas y la clave del medio se unen, y la clave del medio se elimina del nodo padre.

Árboles B+[editar]Las diferencias con los árboles B son que:

Sólo los nodos hoja apuntan a los registros o cubetas del fichero. Existe un orden lineal entre las hojas, que están encadenadas

mediante punteros para permitir un eficiente acceso secuencial.Búsqueda por clave[editar]

Buscar en la raíz el valor   más pequeño mayor que la clave x. La búsqueda sigue por el puntero   hasta que llegue a un nodo hoja, que será donde esté el puntero al bloque o cubeta (cuando un elemento se encuentre en una página raíz o interior la búsqueda continuará por la rama derecha de dicha clave, hasta llegar a una hoja).

Inserción[editar]Se busca el nodo hoja correspondiente y se inserta la clave si no estáallí. Si tiene lugar una partición, se inserta una clave en el nodo padre, que será duplicada si la partición ocurre en una hoja.

1. include<conio.h>2. include<stdio.h>3. include<iostream.h>4. include<stdlib.h>5. include<dos.h>

struct nodo {

int valor;struct nodo*sig;

}*cab; void insertar(int num); void mostrar(void); void BorrarTodo(void); void Eliminar(int num); void insertord(int num);

void main (void) { int op, num; cab=NULL;

Page 51: 6.1 Definición

while(1){clrscr();printf("1.-Insertar un nodo.\n2.-Eliminar un nodo.\n3.-Mostrar\n4.-Insettar nodos en orden\n0.-Salir\n");printf("\nElige una opcion: ");scanf("%d",&op);switch(op){case 1:printf("Valor del nodo que deseas insertar: ");scanf("%d",&num);insertar(num);break;case 2:printf("Dame el valor del nodo a eliminar: ");scanf("%d",&num);Eliminar(num);break;case 3:mostrar();break;case 4:printf("Valor del nodo que deseas insertar: ");scanf("%d",&num);insertord(num);break;case 0:BorrarTodo();exit(0);//para usar exit debes declarar stdlib.hbreak;default:printf("ERROR Opcion incorrecta");getch();

}getch();}

Page 52: 6.1 Definición

} void insertar(int num) {

nodo *aux,*nuevo;aux=cab;nuevo=new nodo;nuevo->valor=num;nuevo->sig=NULL;if(cab==NULL)cab=nuevo;

else{while(aux->sig!=NULL)aux=aux->sig;aux->sig=nuevo;

}

}

void mostrar(){nodo *aux; //Aux es un apuntadoraux=cab; //En esta line aux toma la direccion de cabwhile(aux!=NULL){printf("%d, ",aux->valor);aux=aux->sig;}}

void BorrarTodo(void)

{nodo *aux;aux=cab;while(aux!=NULL){cab=aux->sig;delete aux;

Page 53: 6.1 Definición

aux=cab;}}

void Eliminar(int num) {

nodo *aux,*aux2, *prev;int c=0;aux=cab;

while(c!=1){if(aux->valor==num){c=1;}else{prev=aux;aux=aux->sig;}}

if(c==1){if(prev->sig==NULL){cab=cab->sig;delete aux;}else{aux2 = aux->sig;prev->sig = aux2;delete aux;}}

Page 54: 6.1 Definición

}

void insertord(int num) {

nodo *aux,*nuevo,*aux2,*prev;int c=0;aux2=cab;nuevo=new nodo;// nuevo->valor=num;while(aux2!=NULL){if(aux2->valor>=num){

prev->sig=nuevo;nuevo->valor=num;nuevo->sig=aux2;cab=prev;aux2=NULL;c=1;}prev=aux2;aux2=aux2->sig;}if(aux2==NULL)if(c==0){insertar(num);}else{delete aux2;}

}

Eliminación[editar]Se busca el nodo hoja correspondiente y se elimina la clave. Si al eliminar una clave, n queda menor a (M/2 -1), entonces debe realizarseuna redistribución de claves, tanto en el índice como en las páginas hojas.

Page 55: 6.1 Definición

1. include<conio.h>2. include<stdio.h>3. include<iostream.h>4. include<stdlib.h>5. include<dos.h>

struct nodo {

int valor;struct nodo*sig;

}*cab; void insertar(int num); void mostrar(void); void BorrarTodo(void); void Eliminar(int num); void insertord(int num);

void main (void) { int op, num; cab=NULL;

while(1){clrscr();printf("1.-Insertar un nodo.\n2.-Eliminar un nodo.\n3.-Mostrar\n4.-Insettar nodos en orden\n0.-Salir\n");printf("\nElige una opcion: ");scanf("%d",&op);switch(op){case 1:printf("Valor del nodo que deseas insertar: ");scanf("%d",&num);insertar(num);break;case 2:printf("Dame el valor del nodo a eliminar: ");scanf("%d",&num);Eliminar(num);break;case 3:mostrar();break;case 4:

Page 56: 6.1 Definición

printf("Valor del nodo que deseas insertar: ");scanf("%d",&num);insertord(num);break;case 0:BorrarTodo();exit(0);//para usar exit debes declarar stdlib.hbreak;default:printf("ERROR Opcion incorrecta");getch();

}getch();}

} void insertar(int num) {

nodo *aux,*nuevo;aux=cab;nuevo=new nodo;nuevo->valor=num;nuevo->sig=NULL;if(cab==NULL)cab=nuevo;

else{while(aux->sig!=NULL)aux=aux->sig;aux->sig=nuevo;

}

}

void mostrar()

Page 57: 6.1 Definición

{nodo *aux; //Aux es un apuntadoraux=cab; //En esta line aux toma la direccion de cabwhile(aux!=NULL){printf("%d, ",aux->valor);aux=aux->sig;}}

void BorrarTodo(void)

{nodo *aux;aux=cab;while(aux!=NULL){cab=aux->sig;delete aux;aux=cab;}}

void Eliminar(int num) {

nodo *aux,*aux2, *prev;int c=0;aux=cab;

while(c!=1){if(aux->valor==num){c=1;}else{prev=aux;aux=aux->sig;

Page 58: 6.1 Definición

}}

if(c==1){if(prev->sig==NULL){cab=cab->sig;delete aux;}else{aux2 = aux->sig;prev->sig = aux2;delete aux;}}

}

void insertord(int num) {

nodo *aux,*nuevo,*aux2,*prev;int c=0;aux2=cab;nuevo=new nodo;// nuevo->valor=num;while(aux2!=NULL){if(aux2->valor>=num){

prev->sig=nuevo;nuevo->valor=num;nuevo->sig=aux2;cab=prev;aux2=NULL;

Page 59: 6.1 Definición

c=1;}prev=aux2;aux2=aux2->sig;}if(aux2==NULL)if(c==0){insertar(num);}else{delete aux2;}

}Categ