• No se han encontrado resultados

Cap´ıtulo 5: Busqueda en Memoria Principal

N/A
N/A
Protected

Academic year: 2020

Share "Cap´ıtulo 5: Busqueda en Memoria Principal"

Copied!
59
0
0

Texto completo

(1)

Cap´ıtulo 5: Busqueda en Memoria Principal

Del libro: Dise˜no de Algoritmos - Nivio Ziviani (UFMG) C y Pascal

Octubre - 2011

Ingenier´ıa de Software

(2)

´Indice

1 Conceptos b´asicos

2 B´usqueda Secuencial

3 Arboles de b´´ usqueda

´

Arboles de b´usqueda - No balanceados ´

Arboles binarios balanceados

4 Hashing

Transformaciones de clave Hashing Direccionamiento abierto

(3)

Algoritmos de b´

usqueda - TAD

• Es importante considerar los algoritmos de b´usqueda como un TAD, de tal manera que haya independencia en la implementaci´on de las operaciones. Las operaciones mas comunes son:

1 Inicializar la estructura de datos.

2 Buscar uno o mas registros con determinadas claves. 3 Insertar un nuevo registro.

4 Eliminar un registro espec´ıfico.

5 Ordenar un archivo, para tener todos los registros ordenados de acuerdo con la clave.

(4)

Diccionario

• Es el nombre utilizado comunmente para describir una estructura de datos de b´usqueda.

• Es decir, el diccionario es un TAD, que contiene b´asicamente las siguientes operaciones:

1 Inicializar 2 Buscar 3 Insertar 4 Eliminar

(5)

usqueda secuencial

• Es el m´etodo de b´usqueda mas simple, la b´usqueda se realiza de manera secuencial hasta encontrar la clave requerida.

(6)

usqueda Secuencial - Implementaci´

on

package Cap5; import Cap4.Item; import Cap4.MiItem;

import Cap4.PermutacionRandomica; public class Tabla {

private Item registros[]; private int n;

public Tabla (int maxN) {

this.registros = new Item[maxN+1]; this.n = 0;

}

public int Busqueda (Item reg) { this.registros[0] = reg; // centinela int i = this.n;

while (this.registros[i].comparar (reg) != 0) i--; return i;

(7)

Busqueda Secuencial - Implementaci´

on

• El m´etodos b´usqueda retorna el ´ındice del registro que contiene la clave a buscar. En caso no se encuentre retorna cero.

• Esta implementaci´on no soporta mas de un registro con la misma clave.

(8)

Busqueda Secuencial - An´

alisis

• B´usquedas con ´exito: mejor caso: C(n) = 1 peor caso: C(n) = n caso medio: C(n) = n+12

• En caso de una b´usqueda no exitosa, tendremos:

C(n) = n + 1

(9)

usqueda Binaria

• La b´usqueda puede ser eficiente, si los registros se mantienen en orden.

• Para buscar un registro debemos realizar lo siguiente:

1 Comparar la clave con el registro que se encuentra en el medio de la tabla.

2 Si el registro es menor, entonces se encuentra en la primera mitad de la tabla.

3 Si la clave es mayor, entonces se encuentra en la segunda mitad de la tabla.

4 Se repite el proceso hasta que la clave sea encontrada, o quede apenas un registro cuya clave sea diferente a la requerida, lo cual significa que la b´usqueda no tuvo ´exito.

(10)

usqueda binaria - Ejemplo

(11)

usqueda binaria - Implementaci´

on

public int binaria (Item clave) { if (this.n == 0) return 0; int izq = 1, der = this.n, i; do {

i = (izq + der) / 2;

if (clave.comparar (this.registros[i]) > 0) izq = i + 1; else der = i - 1;

} while ((clave.comparar (this.registros[i]) != 0) && (izq <= der)); if (clave.comparar (this.registros[i]) == 0) return i;

else return 0; }

(12)

usqueda binaria - An´

alisis

• En cada iteraci´on el tama˜no de la tabla es divido en dos.

• El n´umero de veces que la tabla se divide en 2 se encuentra en orden log n.

• Sin embargo, el costro de mantener una tabla ordenada es alto. Cada inserci´on en una posici´on p implica que los registros se desordenen a partir de la posici´on p.

(13)

´

Arboles de b´

usqueda

• Son estructuras de datos eficientes para almacenar informaci´on.

• Ventajas:

• Acceso directo y secuencial eficientes.

• Facilidad de inserci´on y eliminaci´on de registros.

• Buena tasa de utilizaci´on de memoria.

(14)

´

(15)

´

Arboles de b´

usqueda no balanceados - Ejemplo

• La ra´ız esta en nivel 0.

• S´ı un nodo esta en nivel i entonces la ra´ız de sus subarboles se encuentran en el nivel i + 1.

• La altura de un nodo es la longitud del camino mas largo desde dicho nodo hasta un nodo hoja.

(16)

Arbol binario - Implementaci´

on

package Cap5; import Cap4.Item;

public class ArbolBinario { private static class Nodo {

Item reg; Nodo izq, der; }

private Nodo raiz;

public ArbolBinario () { this.raiz = null; }

public Item busqueda (Item reg) { return this.busqueda (reg, this.raiz); }

public void insertar (Item reg) {

this.raiz = this.insertar (reg, this.raiz); }

public void elimina (Item reg) {

this.raiz = this.retira (reg, this.raiz); }

(17)

Arbol binario - Implementaci´

on

Operaci´on de b´usqueda:

private Item busqueda (Item reg, Nodo p) { if (p == null) return null;

else if (reg.comparar (p.reg) < 0) return busqueda (reg, p.izq); else if (reg.comparar (p.reg) > 0) return busqueda (reg, p.der); else return p.reg;

(18)

Arbol binario - Implementaci´

on

Operaci´on Insertar:

private Nodo insertar (Item reg, Nodo p) { if (p == null) {

p = new Nodo (); p.reg = reg; p.izq = null; p.der = null; }

else if (reg.comparar (p.reg) < 0) p.izq = insertar (reg, p.izq); else if (reg.comparar (p.reg) > 0) p.der = insertar (reg, p.der); else System.out.println ("Error: Registro ya existente");

return p; }

(19)

Arbol binario - Implementaci´

on

Creaci´on del ´arbol: package Cap5; import java.io.*; import Cap4.MiItem; public class CrearArbol {

public static void main (String[] args) throws Exception { ArbolBinario dicionario = new ArbolBinario ();

BufferedReader in = new BufferedReader (

new InputStreamReader (System.in)); int clave = Integer.parseInt (in.readLine());

while (clave > 0) {

MiItem item = new MiItem (clave); dicionario.insertar (item);

clave = Integer.parseInt (in.readLine()); }

} }

(20)

Arbol Binario - Implementaci´

on

Comentarios sobre la eliminaci´on:

• Si el nodo a eliminar, contiene un ´unico descendiente entonces la operaci´on es sencilla.

• En el caso de tener dos descendientes el registro a eliminar se debe sustituir por el registro que se encuentra mas a la derecha del sub-´arbol izquierdo, o por el registro que se encuentra m´as a la izquierda del sub-´arbol derecho.

(21)

Arbol binario - Implementaci´

on

Eliminaci´on de un nodo:

private Nodo antecesor (Nodo q, Nodo r) {

if (r.der != null) r.der = antecesor (q, r.der); else { q.reg = r.reg; r = r.izq; }

return r; }

private Nodo eliminar (Item reg, Nodo p) {

if (p == null) System.out.println ("Error: Registro no encontrado"); else if (reg.comparar (p.reg) < 0) p.izq = eliminar (reg, p.izq); else if (reg.comparar (p.reg) > 0) p.der = eliminar (reg, p.der); else {

if (p.der == null) p = p.izq; else if (p.izq == null) p = p.der; else p.izq = antecesor (p, p.izq); }

return p; }

(22)

Arbol binario - Implementaci´

on

(23)

Arbol binario - Implementaci´

on

Recorrido de los nodos: private void central (Nodo p) {

if (p != null) { central (p.izq); System.out.println (p.reg.toString()); central (p.der); } }

public void imprime () { this.central (this.raiz); }

(24)

Arbol binario - An´

alisis

• N´umero de comparaciones en una b´usqueda con ´exito: Mejor caso: C(n) = O(1)

Peor caso: C(n) = O(n) Caso medio: C(n) = O(log n)

• El tiempo de ejecuci´on de los algoritmos de b´usqueda en ´arboles binarios depende del formato de los ´arboles.

• Para obtener el peor caso, basta que las llaves sean insertadas en orden creciente o decreciente. En este caso el ´arbol resultante es una lista lineal, cuyo n´umero medio de comparaciones es: (n + 1)/2.

(25)

Arboles balanceados

• Si quisieramos insertar el valor 1 sobre el arbol anterior y que quede balanceado tuvieramos que realizar muchos movimientos.

• Lo que se puede hacer es tener una soluci´on intermedia de tal manera que el ´arbol quede casi balanceado.

(26)

´

Arboles SBB

• Arboles B, estructura para memoria secundaria propuesta por Bayer y´ McCreight en 1972.

• Arbol 2 − 3, es un caso especial de ´´ arbol B, donde cada nodo tiene 2 o 3 sub ´arboles.

(27)

´

Arboles SBB

• Arbol 2 − 3:´

• Las referencias a la izquierda apuntan a los nodos de un nivel mas abajo.

• Las referencias a la derecha pueden ser verticales u horizontales.

• Se elimina la asimetr´ıa en los ´arboles B binarios, y se obtienen ´arboles B Binarios Sim´etricos (SBB)s.

• Arbol SBB, Es un ´´ arbol binario con dos tipos de referencias: verticales y horizontales, tal que:

• Todos los caminos de la ra´ız hasta cada nodo externo poseen el mismo n´umero de referencias verticales.

• No puden existir dos referencias horizontales sucesivas.

(28)

´

(29)

´

Arboles SBB

• Se utilizan transformaciones para mantener el balanceamiento luego de una inserci´on o eliminaci´on.

• La clave a ser insertada (o eliminada), siempre es insertada (o eliminada) despu´es de la referencia vertical mas baja en el ´arbol.

• Dependiendo de la situaci´on anterior, pueden aparecer dos referencias horizontales sucesivas.

(30)

´

(31)

´

Arboles SBB - Ejemplo

(32)

´

Arboles SBB - Estructura del diccionario

package Cap5; import Cap4.Item; public class ArbolSBB {

private static class Nodo { Item reg;

Nodo izq, der; byte indI, indD; }

private static final byte Horizontal = 0; private static final byte Vertical = 1; private Nodo raiz;

private boolean propSBB; public ArbolSBB () {

this.raiz = null; this.propSBB = true; }

(33)

´

Arboles SBB - M´

etodos para mantener la propiedad de

SBB

private Nodo ii (Nodo ap) {

Nodo ap1 = ap.izq; ap.izq = ap1.der; ap1.der = ap; ap1.indI = Vertical; ap.indI = Vertical; ap = ap1; return ap;

}

private Nodo id (Nodo ap) {

Nodo ap1 = ap.izq; Nodo ap2 = ap1.der; ap1.indD = Vertical; ap.indI = Vertical; ap1.der = ap2.izq; ap2.izq = ap1; ap.izq = ap2.der; ap2.der = ap; ap = ap2;

return ap; }

private Nodo dd (Nodo ap) {

Nodo ap1 = ap.der; ap.der = ap1.izq; ap1.izq = ap; ap1.indD = Vertical; ap.indD = Vertical; ap = ap1; return ap;

}

private Nodo di (Nodo ap) {

Nodo ap1 = ap.der; Nodo ap2 = ap1.izq; ap1.indI = Vertical; ap.indD = Vertical; ap1.izq = ap2.der; ap2.der = ap1; ap.der = ap2.izq; ap2.izq = ap; ap = ap2;

return ap; }

(34)

´

Arboles SBB - M´

etodo Insertar

private Nodo insertar (Item reg, Nodo padre, Nodo hijo, boolean hijoIzq) { if (hijo == null) {

hijo = new Nodo (); hijo.reg = reg; hijo.indI = Vertical; hijo.indD = Vertical; hijo.izq = null; hijo.der = null; if (padre != null)

if (hijoIzq) padre.indI = Horizontal; else padre.indD = Horizontal; this.propSBB = false;

}

else if (reg.comparar (hijo.reg) < 0) {

hijo.izq = insertar (reg, hijo, hijo.izq, true); if (!this.propSBB)

if (hijo.indI == Horizontal) { if (hijo.izq.indI == Horizontal) {

hijo = this.ii (hijo); // transformaci´on izquierda-izquierda if (padre != null)

if (hijoIzq) padre.indI=Horizontal; else padre.indD=Horizontal; }

else if (hijo.izq.indD == Horizontal) {

hijo = this.id (hijo); // transformaci´on izquierda-derecha if (padre != null)

if (hijoIzq) padre.indI=Horizontal; else padre.indD=Horizontal; }

}

else this.propSBB = true; }

(35)

´

Arboles SBB - M´

etodo Insertar

else if (reg.comparar (hijo.reg) > 0) {

hijo.der = insertar (reg, hijo, hijo.der, false); if (!this.propSBB)

if (hijo.indD == Horizontal) { if (hijo.der.indD == Horizontal) {

hijo = this.dd (hijo); // transformacion derecha-derecha if (padre != null)

if (hijoIzq) padre.indI=Horizontal; else padre.indD=Horizontal; }

else if (hijo.der.indI == Horizontal) {

hijo = this.di (hijo); // transformaci´on derecha-izquierda if (padre != null)

if (hijoIzq) padre.indI=Horizontal; else padre.indD=Horizontal; }

}

else this.propSBB = true; }

else {

System.out.println ("Error: Registro ya existente"); this.propSBB = true;

}

return hijo; }

(36)

Procedimiento Eliminar

El procedimiento eliminar, presenta 3 m´etodos auxiliares:

• cortaIzq (cortaDer), es llamado cuando un nodo hoja (que es

referenciado por un apuntador vertical), es retirado del subarbol de la izquierda (derecha), diminuyendo la altura luego de la eliminaci´on.

• Cuando el nodo a ser retirado tiene dos descendientes, el m´etodo antecesor localiza el nodo antecesor a ser intercambiarlo con el nodo que se va a eliminar.

(37)

´

Arboles SBB - M´

etodo cortaIzq para eliminar nodo

private Nodo cortaIzq (Nodo ap) { if (ap.indI == Horizontal) {

ap.indI = Vertical; this.propSBB = true; }

else if (ap.indD == Horizontal) {

Nodo ap1 = ap.der; ap.der = ap1.izq; ap1.izq = ap; ap = ap1; if (ap.izq.der.indI == Horizontal) {

ap.izq = this.di (ap.izq); ap.indI = Horizontal; }

else if (ap.izq.der.indD == Horizontal) { ap.izq = this.dd (ap.izq); ap.indI = Horizontal; } this.propSBB = true; } else { ap.indD = Horizontal; if (ap.der.indI == Horizontal) { ap = this.di (ap); this.propSBB = true; }

else if (ap.der.indD == Horizontal) { ap = this.dd (ap); this.propSBB = true; }

} return ap; }

(38)

´

Arboles SBB - M´

etodo cortaDer para eliminar nodo

private Nodo cortaDer (Nodo ap) { if (ap.indD == Horizontal) {

ap.indD = Vertical; this.propSBB = true; }

else if (ap.indI == Horizontal) {

Nodo ap1 = ap.izq; ap.izq = ap1.der; ap1.der = ap; ap = ap1; if (ap.der.izq.indD == Horizontal) {

ap.der = this.id (ap.der); ap.indD = Horizontal; }

else if (ap.der.izq.indI == Horizontal) { ap.der = this.ii (ap.der); ap.indD = Horizontal; } this.propSBB = true; } else { ap.indI = Horizontal; if (ap.izq.indD == Horizontal) { ap = this.id (ap); this.propSBB = true; }

else if (ap.izq.indI == Horizontal) { ap = this.ii (ap); this.propSBB = true; }

} return ap; }

(39)

´

Arboles SBB - M´

etodo antecesor para eliminar nodo

private Nodo antecesor (Nodo q, Nodo r) { if (r.der != null) {

r.der = antecesor (q, r.der);

if (!this.propSBB) r = this.cortaDer (r); }

else { q.reg = r.reg; r = r.izq;

if (r != null) this.propSBB = true; }

return r; }

(40)

´

Arboles SBB - M´

etodo eliminar para retirar un nodo

private Nodo eliminar (Item reg, Nodo ap) { if (ap == null) {

System.out.println ("Error: Registro no encontrado"); this.propSBB = true;

}

else if (reg.comparar (ap.reg) < 0) { ap.izq = eliminar (reg, ap.izq);

if (!this.propSBB) ap = this.cortaIzq (ap); //Cuando el nodo es hoja }

else if (reg.comparar (ap.reg) > 0) { ap.der = eliminar (reg, ap.der);

if (!this.propSBB) ap = this.cortaDer (ap); //Cuando el nodo es hoja }

else { // registro encontrado this.propSBB = false; if (ap.der == null) {

ap = ap.izq;

if (ap != null) this.propSBB = true; }

else if (ap.izq == null) { ap = ap.der;

if (ap != null) this.propSBB = true; }

else {

ap.izq = antecesor (ap, ap.izq);

if (!this.propSBB) ap = this.cortaIzq (ap); }

} return ap; }

(41)

Hashing

• Es un m´etodo de b´usqueda con transformaci´on de clave que esta conformado por dos etapas principales:

1 Calcular el valor de la funci´on de transformaci´on o funci´on hashing, la cual transforma la clave de b´usqueda en una direcci´o o entrada a la tabla.

2 Como dos o mas claves pueden transformarse en la misma direcci´on, es necesario disponer de una t´ecnica para manejar las colisiones.

(42)

Fuciones de transformaci´

on

• Una funci´on de transformaci´on debe mapear claves en enteros dentro de un intervalo [0, . . . , M ], donde M es el tama˜no de la tabla.

• La funci´on de transformaci´on ideal es aquella que:

1 Es simple de calcular.

2 Para cada clave de entrada, cada una de las salidas es igualmente problable de ocurrir.

• C´omo las transformaciones sobre las claves son aritm´eticas, se deben transformar las claves no num´ericas en n´umeros.

• En Java basta con convertir cada caracter de la clave no num´erica en un entero.

(43)

Fuciones de transformaci´

on - M´

etodo mas usado

• Es el resto de la divisi´on por M :

h(K) = KmodM

donde K es el n´umero entero correspondiente a la clave.

• Se debe tener cuidado con la elecci´on del valor de M . M debe ser un n´umero primo, mas no cualquier n´umero primo: deben ser evitados los n´umeros primos obtenidos a partir de:

bi± j

• Donde b es la base de un conjunto de caracteres (generalmente b = 64 para BCD, 128 para ASCII, 256 para EBCDIC, y 100 para algunos c´odigos decimales, e i y j son enteros peque˜nos.

(44)

Transformaci´

on de claves no num´

ericas

• Las claves no num´ericas deben ser transformadas en n´umeros:

K = n−1 X i=0 clave[i] × p[i] .

• Donde, n es el n´umero de caracteres de la clave.

• clave[i] corresponde a la representaci´on ASCII o UNICODE del i-´esimo caracter de la clave.

• p[i] es un entero de un conjunto de pesos generados de manera aleatoria para 0 ≤ i ≤ n − 1.

• Una de las ventajas de usar pesos, es que dos conjuntos diferentes de pesos p1[i] y p2[i], 0 ≤ i ≤ n − 1, llevan a dos funciones de

(45)

Transformaci´

on de claves no num´

ericas

• El programa genera un peso para cada caracter de una cadena de n

caracteres.

private int[] generaPesos (int n) { int p[] = new int[n];

java.util.Random rand = new java.util.Random (); for (int i = 0; i < n; i++) p[i] = rand.nextInt(M) + 1; return p;

}

• Implementaci´on de funci´on de transformaci´on:

private int h (String clave, int[] pesos) { int suma = 0;

for (int i = 0; i < clave.length(); i++)

suma = suma + ((int)clave.charAt (i)) * pesos[i]; return suma % this.M;

(46)

Listas encadenadas

• Una de las formas de resolver la colisi´on es utilizar una lista lineal encadenada (con apuntadores).

• Ejemplo: sea la i-´esima letra del alfabeto y es representada por el numero i y una funci´on de transformaci´on h(clave) = clave m´od M es utilizada para M = 7, el resultado de la inserci´on de las claves P ESQU ISA en la tabla es la siguiente:

• h(A) = h(1) = 1, h(E) = h(5) = 5, h(S) = h(19) = 5 y as´ı en

(47)

Estructura de operaciones del diccionario

• En cada entrada de la lista debe ser almacenados una clave y un registro de datos cuyos datos depende de la aplicaci´on.

• La clase interna celda es utilizada para representar una entrada en una lista de claves que son mapeadas a una direcci´on i de la tabla, siendo 0 ≤ i ≤ M − 1

• El m´etodo equals de la clase celda es usado para verificar si dos celdas son iguales (si poseen la misma clave).

• La operaci´on inicializa es implementada por el constructor de la clase T ablaHash

(48)

Estructura de operaciones del diccionario

package Cap5;

import Cap3.apuntadores.Lista; public class TablaHash {

private static class Celda { String clave;

Object item;

public Celda (String clave, Object item) { this.clave = clave; this.item = item; }

public boolean equals (Object obj) { Celda cel = (Celda)obj;

return clave.equals (cel.clave); }

public String toString () { return " " + item.toString (); }

}

private int M; private Lista tabla[]; private int pesos[];

public TablaHash (int m, int maxTamClave) { this.M = m; this.tabla = new Lista[this.M];

for (int i = 0; i < this.M; i++) this.tabla[i] = new Lista (); this.pesos = this.generaPesos (maxTamClave);

(49)

Estructura de las operaciones de un diccionario

public Object busqueda (String clave) { int i = this.h (clave, this.pesos); if (this.tabla[i].vacia()) return null; else {

Celda cel=(Celda)this.tabla[i].busqueda(new Celda(clave,null)); if (cel == null) return null;

else return cel.item; }

}

public void insertar (String clave, Object item) { if (this.busqueda (clave) == null) {

int i = this.h (clave, this.pesos);

this.tabla[i].insertar (new Celda (clave, item)); }

else System.out.println ("Registro ya se encuentra"); }

public void eliminar (String clave) throws Exception { int i = this.h (clave, this.pesos);

Celda cel = (Celda)this.tabla[i].retirar (new Celda (clave,null)); if (cel == null) System.out.println ("Registro a eliminar no encontrado"); }

(50)

An´

alisis

• Asumiendo que cualquier item del conjunto tiene igual probabilidad de ser direccionado para cualquier entrada de la tabla, entonces la longitud esperada de cada lista es de N/M , donde N representa el n´umero de registros en la tabla y M el tama˜no de la tabla.

• Luego, las operaciones de b´usqueda, inserci´on y eliminaci´on tienen un costro de O(1 + N/M ) operaciones en promedio. La constante 1 representa el tiempo para encontrar la entrada de la tabla, y N/M el tiempo para prerecorrer la lista. Para valores de M pr´oximos a N , el tiempo se vuelve constante, esto es independiente de N .

(51)

Direccionamiento abierto

• Cuando el n´umero de registros a ser almacenados en la tabla puede ser previamente estimado, entonces no habr´a necesidad de usar listas enlazadas para almacenar los registros.

• Existen diferentes m´etodos para almacenar N registros en una tabla de tama˜no M > N , los cuales utilizan las entradas vacias para resolver las colisiones.

• En el direccionamiento abierto todas las claves son almacenadas en la propia tabla, sin la necesidad de utilizar listas enlazadas.

• Existen diferentes propuestas para la elecci´on de las ubicaciones alternativas. La mas simple es el hashing lineal, donde la posici´on hj en la tabla esta dada por:

(52)

Direccionamiento abierto - Ejemplo

• Se la i-´esima letra del alfabeto es represenada por el n´umero i y una funci´on de transformaci´on h(clave) = clave m´od M es utilizada para M = 7.

• Para la palabra LU N ES en la tabla, utilizando el hashing lineal se muestra a continuaci´on.

• h(L) = h(12) = 5, h(U ) = h(21) = 0, h(N ) = h(14) = 0,

(53)

Direccionamiento abierto - Estructura de operaciones

• La tabla esta constitu´ıda por un vector de celdas.

• La clase interna Celda es utilizada para representar una celda de la tabla.

• La operaci´on de inicializaci´on se encuentra implementada por el constructor de la clase T ablaHash.

• Los m´etodos utilizan algunas operaciones auxiliares durante la ejecuci´on.

(54)

Direccionamiento abierto - Estructura de operaciones

package Cap5.Direccionamiento; public class TablaHash {

private static class Celda {

String clave; Object item; boolean eliminado; public Celda (String clave, Object item) {

this.clave = clave; this.item = item; this.eliminado = false;

}

public boolean equals (Object obj) { Celda cel = (Celda)obj;

return clave.equals (cel.clave); }

public String toString () { return " " + item.toString (); }

}

private int M; // tama~no de la tabla private Celda tabla[];

private int pesos[];

public TablaHash (int m, int maxTamChave) { this.M = m; this.tabla = new Celda[this.M];

for (int i = 0; i < this.M; i++) this.tabla[i] = null; this.pesos = this.generaPesos (maxTamChave); }

(55)

Direccionamiento abierto - Estructura de operaciones

public Object busqueda (String clave) { int indice = this.busquedaIndice (clave);

if (indice < this.M) return this.tabla[indice].item; else return null;

}

public void insertar (String clave, Object item) { if (this.busqueda (clave) == null) {

int inicial = this.h (clave, this.pesos); int indice = inicial; int i = 0; while (this.tabla[indice] != null &&

!this.tabla[indice].eliminado &&

i < this.M) indice = (inicial + (++i)) % this.M; if (i < this.M) this.tabla[indice] = new Celda (clave, item); else System.out.println ("Tabla llena");

} else System.out.println ("Registro ya existente"); }

(56)

Direccionamiento abierto - Estructura de operaciones

public void eliminar (String clave) throws Exception { int i = this.busquedaIndice (clave);

if (i < this.M) {

this.tabla[i].eliminado = true; this.tabla[i].clave = null; } else System.out.println ("Registro no esta encontrado"); }

private int busquedaIndice (String clave) { int inicial = this.h (clave, this.pesos); int indice = inicial; int i = 0; while (this.tabla[indice] != null &&

!clave.equals (this.tabla[indice].clave) && i < this.M) indice = (inicial + (++i)) % this.M; if (this.tabla[indice] != null &&

clave.equals (this.tabla[indice].clave)) return indice; else return this.M; // b´usqueda sin ´exito

(57)

An´

alisis

• Sea α = N/M el factor de carga de la tabla, seg´un Knuth(1973), el costo de una b´usqueda con ´exito es:

C(n) = 1

2(1 + 1 1 − α)

• El hashing lineal sufre de un mal llamado agrupamiento (clustering)

• Este fen´omeno ocurre a medida que la tabla se comienza a llenar, pues una inserci´on de una nueva clave tiende a ocupar una posici´on en la tabla que esta contigua a otras posiciones ya ocupadas, lo que impacta sobre el tiempo de b´usqueda.

• A pesar de que el hashing lineal es un m´etodo relativamente pobre para resolver las colisiones, los resultados presentados son buenos.

(58)

Ventajas y desventajas

• Ventajas:

• Alta eficiencia del costo de la busqueda O(1) para el caso medio.

• F´acil implementaci´on.

• Desventajas:

• Tiene un costo alto para recuperar los registros en orden lexicogr´afico de las llaves, es necesario ordenar el archivo.

(59)

Hashing Perfecto

• Si h(xi) = h(xj) si y solamente s´ı i = j, entonces no hay colisiones en la funci´on de transformaci´on. Es llamada funci´on de

transformaci´on perfecta o funci´on de hashing perfecta (hp).

• Si el n´umero de claves N y el tama˜no de la tabla M son iguales (α = N/M = 1), entonces tenemos una funci´on de transformaci´on perfecta m´ınima.

• Si xi ≤ xj y hp(xi) ≤ hp(xj), entonces el orden lexicografico es mantenido. Tenemos una funci´on de transformaci´on perfecta m´ınima preservando el orden.

Referencias

Documento similar

Fuente de emisión secundaria que afecta a la estación: Combustión en sector residencial y comercial Distancia a la primera vía de tráfico: 3 metros (15 m de ancho)..

que hasta que llegue el tiempo en que su regia planta ; | pise el hispano suelo... que hasta que el

E Clamades andaua sienpre sobre el caua- 11o de madera, y en poco tienpo fue tan lexos, que el no sabia en donde estaña; pero el tomo muy gran esfuergo en si, y pensó yendo assi

Sanz (Universidad Carlos III-IUNE): &#34;El papel de las fuentes de datos en los ranking nacionales de universidades&#34;.. Reuniones científicas 75 Los días 12 y 13 de noviembre

(Banco de España) Mancebo, Pascual (U. de Alicante) Marco, Mariluz (U. de València) Marhuenda, Francisco (U. de Alicante) Marhuenda, Joaquín (U. de Alicante) Marquerie,

La campaña ha consistido en la revisión del etiquetado e instrucciones de uso de todos los ter- mómetros digitales comunicados, así como de la documentación técnica adicional de

d) que haya «identidad de órgano» (con identidad de Sala y Sección); e) que haya alteridad, es decir, que las sentencias aportadas sean de persona distinta a la recurrente, e) que

Las manifestaciones musicales y su organización institucional a lo largo de los siglos XVI al XVIII son aspectos poco conocidos de la cultura alicantina. Analizar el alcance y