Implementaciones (Algoritmos II)

De Cuba-Wiki

Conjuntos y Diccionarios[editar]

Conjuntos y Diccionarios sobre Árboles Binarios[editar]

Representación de conjuntos/diccionarios a través de ABB

Algoritmos:

  • Costo de la inserción: O(n) en peor caso. O(log n) en caso promedio.
  • Borrar(u, A): Tres casos: 1. u es una hoja, 2. u tiene un solo hijo, 3. u tiene dos hijos. Costo: O(n) en peor caso.
    1. Buscar al padre, eliminar la hoja.
    2. Buscar al padre. Unir al padre con el hijo de u. Eliminar u.
    3. Buscar al predecesor inmediato v, es decir al mayor elemento de su subárbol izquierdo. Para esto hay que ir lo más a la derecha posible. Poner a v en el lugar de u. Borrar a v y "enganchar" al hijo de v (si es que tenía) con el padre de v.

Representación de conjuntos/diccionarios a través de AVL (árboles balanceados)[editar]

Balanceo perfecto: cuando esto sucede un árbol tiene altura (log2 n + 1) y sus hojas son aproximadamente el 50% de los nodos.

Balanceo en altura: se da cuando |altura(der) - altura(izq)| <= 1. Estos son los AVL.

Algoritmos[editar]
  • Costo de borrado/búsqueda/inserción: O(log n)
  • Inserción:
    1. Insertar el nuevo elemento como si fuera un ABB.
    2. Actualizar los factores de balanceo de la rama en la que se insertó desde abajo hacia arriba.
    3. Si algún nodo quedó con factor +-2, hay que rebalancear. Casos de Rebalanceo (siempre respecto del nodo que se desbalanceó):
      • RR: subárbol derecho del hijo derecho. Rotación simple. Siendo P el desbalanceado y Q el hijo derecho, se pone a P como hijo izquierdo de Q y al hijo izquierdo de Q como hijo derecho de P.
      • LR: subárbol izquierdo del hijo derecho. Rotación doble. Siendo P el desbalanceado, Q el hijo derecho y R el hijo izquierdo de Q. Primera rotación: se pone a R como hijo derecho de P. Q pasa a ser hijo derecho de R. El hijo derecho de R pasa a ser hijo izquierdo de Q. Segunda Rotación: P pasa a ser hijo izquierdo de R, el hijo izquierdo de R pasa a ser el hijo derecho de P.
      • RL: subárbol derecho del hijo izquierdo. Rotación doble. Análogo al caso LR.
      • LL: subárbol izquierdo del hijo izquierdo. Rotación simple. Análogo al caso RR.
  • Borrado:
    1. Borrar como en un ABB.
    2. Idem paso 2 de inserción.
    3. Para cada nodo con factor +-2 hay que hacer rotaciones.

Conjuntos y Diccionarios sobre Hashing[editar]

Contexto de uso
Importantes para el acceso a datos en memoria secundaria, con aplicaciones en criptografía.
Direccionamiento directo
Se asocia a cada valor de la clave un índice del arreglo.
Ventaja: búsqueda en O(1).
Desventaja: desperdicio de memoria.
Tabla hash[editar]

Se representa al diccionario con una tupla <T, h> siendo T un arreglo de N posiciones y h una función que va del conjunto de claves posibles en {0, ..., N-1}. La función h me indicará dada una clave la posición del elemento que busco.

Colisiones[editar]

En la práctica, en general la cantidad de posibles claves es mucho menor que la cantidad de posiciones del array. Por esto se da que dos (o más) claves distintas puedan tener la misma posición asignada a través de h. Esto genera colisiones.

Resolución de colisiones
Dos familias de métodos: direccionamiento cerrado o concatenación y direccionamiento abierto.
Direccionamiento cerrado o concatenación
A cada posición i del arreglo se le asocia la lista de los elementos k tales que h(k) = i.
Complejidad:
  • inserción: O(1).
  • búsqueda: O(longitud de la lista asociada a h(k)).
  • borrado: O(longitud de la lista asociada a h(k)).
La idea es que, si elijo bien la función hash y el tamaño del arreglo, la búsqueda y el borrado deberían quedar O(1).
Direccionamiento abierto
Todos los elementos se guardan en el arreglo. Si la posición obtenida a priori está ocupada, tengo que buscar otra libre dentro del arreglo. Ahora la función h pasa a depender de la cantidad de intentos, además de la clave.
Barrido
Hay que recorrer todas las posiciones del arreglo. Tipos de h(k, i): barrido linear, barrido cuadrático, hashing doble.
Barrido Linear
h(k, i) = (h'(k) + i) mod T
Problema: aglomeración primaria. Si dos elementos colisionan, siguen colisionando en los siguientes intentos, y se van armando aglomeraciones en posiciones consecutivas.
Barrido Cuadrático
h(k, i) = (h'(k) + c1*i + c2*i^2) mod T
Problema: aglomeración secundaria. Si dos elementos colisionan, siguen colisionando en los siguientes intentos, pero a diferencia de la aglomeración primaria se "esparcen" más.
Hashing doble
h(k, i) = (h1(k) + i*h2(k)) mod T
Acá la idea es que el barrido también depende de la clave. Esto no produce aglomeración primaria, y reduce la aglomeración secundaria.
Funciones de hash[editar]
División
h(k) mod T
Ventajas: fácil.
Desventajas: aglomeración primaria, muchas colisiones, la función no depende de todas las cifras de la clave.
Tip: conviene elegir T primo no demasiado cercano a una potencia de 2.
Partición
La idea es partir la clave en secuencias de cifras con algún criterio.
Extracción
Parecido a partición, pero solo se usa una porción de la clave.

523270635399669218484834



Mature Sex
Mature Women
Mature Porn

Mature Moms
Mature Lesbians
Mature Ladies
Gay Porn
Gay Teens
Gay Fuck
Teen Webcam
Webcam Sex
Webcam Chat



Free Porn
Free Porn Movies
Free Porn Videos

Free Porn Movies
Free porn sample movies
Hardcore Porn
Free Porn Clips
Teen Porn Videos
Teen Girls
Teen Sex
Teen Porn
Sexy Teens
Nude Teens
Naked Teens



Buy Viagra
Buy Cialis

Colas de prioridad sobre Heaps[editar]

  • Max-Heap: en la raíz tengo al elemento más grande.
  • Min-Heap: en la raíz tengo al elemento más chico.

Representación con Arrays[editar]

También se puede hacer con árboles binarios

Siendo la p(v) la posición del nodo v en el array:

p(v) =  0                       Si v es la raíz
2p(u) + 1       Si v es el hijo izquierdo de u
2p(u) + 2       Si v es el hijo derecho de u
  • Ventajas: muy eficientes, fáciles de navegar.
  • Desventaja: es una implementación estática. Si tengo pocos elementos, estoy usando mucho espacio, si tengo muchos, me puedo llegar a quedar corto.

Algoritmos[editar]

Próximo
es simplemente devolver la raíz. O(1).
Encolar
insertar el elemento al final del heap. Subirlo, comparándolo sucesivamente con su padre.
Desencolar
reemplazar la raíz con la última hoja y eliminar la última hoja. Bajarla, comparándola con los hijos. Si pudiera bajar por cualquiera de los dos, se elige el de mayor prioridad.
Array2Heap
algoritmo de Floyd. Arranco del penúltimo nivel (o sea, donde no hay hojas). Voy transformando a los subárboles que tienen como raíz al nodo actual en heaps (alcanza con bajar la raíz). En un array, se itera decrementando el índice del arreglo, es decir: primero tomo como nodo actual al que está en la posición k, después al k-1, después al k-2, etc. hasta llegar a la posición 0.

Tries[editar]

Propiedades[editar]

  • El peor caso es mucho mejor que el de los ABB si el número de claves es grande y las claves no son largas.
  • La longitud del camino más largo es igual al mayor número de bits sucesivos iguales de dos claves a partir del bit de más a la izquierda (mayor prefijo común entre dos claves cualesquiera).
  • Búsqueda o inserción en un árbol de n claves de b bits necesita en promedio log n comparaciones de clave completa y b en el peor caso.
  • Cada subárbol representa al conjunto de claves con las etiquetas de los ejes que llevan hasta él.
  • Los nodos no contienen claves.
  • Los punteros a la info se almacenan en las hojas.
  • A lo sumo una comparación de clave completa en cada búsqueda.
  • Para un conjunto de claves hay un único trie.

Implementaciones posibles[editar]

  • En cada nodo, un arreglo de punteros:
    • Muy eficiente en términos de tiempo.
    • Algoritmo simple.
    • Puede ser muy ineficiente en cuanto a memoria (sobre todo cuando el alfabeto es grande y el diccionario chico) .
    • Una manera de zafarla un poco es agrupar los caracteres menos frecuentes (por ejemplo, en castellano agruparíamos k, w, y, etc.).
  • En cada nodo, representar a los con una lista encadenada:
    • Eficiente en cuanto a tiempo sólo si hay pocas claves.
    • Requiere algoritmos sobre listas.
    • Mucho más eficiente en cuanto a memoria.
Tries Compactos
Lo mismo, pero si una rama me lleva a una sóla hoja, "colapso" toda la rama. Si llega a aparecer otra hoja a la que corresponda esa rama, la tendré que "descolapsar".
Patricia
La misma idea que los tries compactos, pero colapsando a gusto e piacere.