Conjunto disperso

Cómo hacer las siguientes operaciones de manera eficiente si hay una gran cantidad de consultas para ellas. 

  1. Inserción
  2. Supresión
  3. buscando
  4. Limpiar/Quitar todos los elementos.

Una solución es utilizar un árbol de búsqueda binario autoequilibrado como el árbol rojo-negro, el árbol AVL, etc. La complejidad temporal de esta solución para la inserción, eliminación y búsqueda es O (Inicio de sesión).
También podemos usar Hashing. Con hashing, la complejidad temporal de las tres primeras operaciones es O(1). Pero la complejidad temporal de la cuarta operación es O(n). 
También podemos usar el vector de bits (o tabla de acceso directo), pero el vector de bits también requiere un tiempo O(n) para la limpieza.

Sparse Set supera a todos los BST, Hashing y bit vector. Suponemos que tenemos un rango de datos (o el valor máximo que puede tener un elemento) y el número máximo de elementos que se pueden almacenar en el conjunto. La idea es mantener dos arreglos: disperso[] y denso[]. 

dense[]   ==> Stores the actual elements
sparse[]  ==> This is like bit-vector where 
              we use elements as index. Here 
              values are not binary, but
              indexes of dense array.
maxVal    ==> Maximum value this set can 
              store. Size of sparse[] is
              equal to maxVal + 1.
capacity  ==> Capacity of Set. Size of sparse
              is equal to capacity.  
n         ==> Current number of elements in
              Set.

insert(x): Sea x el elemento a insertar. Si x es mayor que maxVal o n (número actual de elementos) es mayor que igual a la capacidad, regresamos. 
Si ninguna de las condiciones anteriores es verdadera, insertamos x en dense[] en el índice n (posición después del último elemento en una array indexada basada en 0), incrementamos n en uno (número actual de elementos) y almacenamos n (índice de x en denso[]) en escaso[x]. 
search(x): para buscar un elemento x, usamos x como índice en sparse[]. El valor sparse[x] se usa como índice en dense[]. Y si el valor de dense[sparse[x]] es igual a x, devolvemos dense[x]. De lo contrario, devolvemos -1.
borrar(x):Para eliminar un elemento x, lo reemplazamos con el último elemento en dense[] y actualizamos el índice del último elemento en sparse[]. Finalmente, disminuya n en 1.
clear(): Establezca n = 0.
print(): Podemos imprimir todos los elementos simplemente recorriendo dense[]. 

Ilustración: 

Let there be a set with two elements {3, 5}, maximum
value as 10 and capacity as 4. The set would be 
represented as below.

Initially:
maxVal   = 10  // Size of sparse
capacity = 4  // Size of dense
n = 2         // Current number of elements in set

// dense[] Stores actual elements
dense[]  = {3, 5, _, _}

// Uses actual elements as index and stores
// indexes of dense[]
sparse[] = {_, _, _, 0, _, 1, _, _, _, _,}

'_' means it can be any value and not used in 
sparse set


Insert 7:
n        = 3
dense[]  = {3, 5, 7, _}
sparse[] = {_, _, _, 0, _, 1, _, 2, _, _,}

Insert 4:
n        = 4
dense[]  = {3, 5, 7, 4}
sparse[] = {_, _, _, 0, 3, 1, _, 2, _, _,}

Delete 3:
n        = 3
dense[]  = {4, 5, 7, _}
sparse[] = {_, _, _, _, 0, 1, _, 2, _, _,}

Clear (Remove All):
n        = 0
dense[]  = {_, _, _, _}
sparse[] = {_, _, _, _, _, _, _, _, _, _,}

A continuación se muestra la implementación en C++ de las funciones anteriores. 

C

/* A C program to implement Sparse Set and its operations */
#include<bits/stdc++.h>
using namespace std;
 
// A structure to hold the three parameters required to
// represent a sparse set.
class SSet
{
    int *sparse;   // To store indexes of actual elements
    int *dense;    // To store actual set elements
    int n;         // Current number of elements
    int capacity;  // Capacity of set or size of dense[]
    int maxValue;  /* Maximum value in set or size of
                     sparse[] */
 
public:
    // Constructor
    SSet(int maxV, int cap)
    {
        sparse = new int[maxV+1];
        dense  = new int[cap];
        capacity = cap;
        maxValue = maxV;
        n = 0;  // No elements initially
    }
 
    // Destructor
    ~SSet()
    {
        delete[] sparse;
        delete[] dense;
    }
 
    // If element is present, returns index of
    // element in dense[]. Else returns -1.
    int search(int x);
 
    // Inserts a new element into set
    void insert(int x);
 
    // Deletes an element
    void deletion(int x);
 
    // Prints contents of set
    void print();
 
    // Removes all elements from set
    void clear() { n = 0; }
 
    // Finds intersection of this set with s
    // and returns pointer to result.
    SSet* intersection(SSet &s);
 
    // A function to find union of two sets
    // Time Complexity-O(n1+n2)
    SSet *setUnion(SSet &s);
};
 
// If x is present in set, then returns index
// of it in dense[], else returns -1.
int SSet::search(int x)
{
    // Searched element must be in range
    if (x > maxValue)
        return -1;
 
    // The first condition verifies that 'x' is
    // within 'n' in this set and the second
    // condition tells us that it is present in
    // the data structure.
    if (sparse[x] < n && dense[sparse[x]] == x)
        return (sparse[x]);
 
    // Not found
    return -1;
}
 
// Inserts a new element into set
void SSet::insert(int x)
{
    //  Corner cases, x must not be out of
    // range, dense[] should not be full and
    // x should not already be present
    if (x > maxValue)
        return;
    if (n >= capacity)
        return;
    if (search(x) != -1)
        return;
 
    // Inserting into array-dense[] at index 'n'.
    dense[n] = x;
 
    // Mapping it to sparse[] array.
    sparse[x] = n;
 
    // Increment count of elements in set
    n++;
}
 
// A function that deletes 'x' if present in this data
// structure, else it does nothing (just returns).
// By deleting 'x', we unset 'x' from this set.
void SSet::deletion(int x)
{
    // If x is not present
    if (search(x) == -1)
        return;
 
    int temp = dense[n-1];  // Take an element from end
    dense[sparse[x]] = temp;  // Overwrite.
    sparse[temp] = sparse[x]; // Overwrite.
 
    // Since one element has been deleted, we
    // decrement 'n' by 1.
    n--;
}
 
// prints contents of set which are also content
// of dense[]
void SSet::print()
{
    for (int i=0; i<n; i++)
        printf("%d ", dense[i]);
    printf("\n");
}
 
// A function to find intersection of two sets
SSet* SSet::intersection(SSet &s)
{
    // Capacity and max value of result set
    int iCap    = min(n, s.n);
    int iMaxVal = max(s.maxValue, maxValue);
 
    // Create result set
    SSet *result = new SSet(iMaxVal, iCap);
 
    // Find the smaller of two sets
    // If this set is smaller
    if (n < s.n)
    {
        // Search every element of this set in 's'.
        // If found, add it to result
        for (int i = 0; i < n; i++)
            if (s.search(dense[i]) != -1)
                result->insert(dense[i]);
    }
    else
    {
        // Search every element of 's' in this set.
        // If found, add it to result
        for (int i = 0; i < s.n; i++)
            if (search(s.dense[i]) != -1)
                result->insert(s.dense[i]);
    }
 
    return result;
}
 
// A function to find union of two sets
// Time Complexity-O(n1+n2)
SSet* SSet::setUnion(SSet &s)
{
    // Find capacity and maximum value for result
    // set.
    int uCap    = s.n + n;
    int uMaxVal = max(s.maxValue, maxValue);
 
    // Create result set
    SSet *result =  new SSet(uMaxVal, uCap);
 
    // Traverse the first set and insert all
    // elements of it in result.
    for (int i = 0; i < n; i++)
        result->insert(dense[i]);
 
    // Traverse the second set and insert all
    // elements of it in result (Note that sparse
    // set doesn't insert an entry if it is already
    // present)
    for (int i = 0; i < s.n; i++)
        result->insert(s.dense[i]);
 
    return result;
}
 
 
// Driver program
int main()
{
    // Create a set set1 with capacity 5 and max
    // value 100
    SSet s1(100, 5);
 
    // Insert elements into the set set1
    s1.insert(5);
    s1.insert(3);
    s1.insert(9);
    s1.insert(10);
 
    // Printing the elements in the data structure.
    printf("The elements in set1 are\n");
    s1.print();
 
    int index = s1.search(3);
 
    //  'index' variable stores the index of the number to
    //  be searched.
    if (index != -1)  // 3 exists
        printf("\n3 is found at index %d in set1\n",index);
    else            // 3 doesn't exist
        printf("\n3 doesn't exists in set1\n");
 
    // Delete 9 and print set1
    s1.deletion(9);
    s1.print();
 
    // Create a set with capacity 6 and max value
    // 1000
    SSet s2(1000, 6);
 
    // Insert elements into the set
    s2.insert(4);
    s2.insert(3);
    s2.insert(7);
    s2.insert(200);
 
    // Printing set 2.
    printf("\nThe elements in set2 are\n");
    s2.print();
 
    // Printing the intersection of the two sets
    SSet *intersect = s2.intersection(s1);
    printf("\nIntersection of set1 and set2\n");
    intersect->print();
 
    // Printing the union of the two sets
    SSet *unionset = s1.setUnion(s2);
    printf("\nUnion of set1 and set2\n");
    unionset->print();
 
    return 0;
}

Producción : 

The elements in set1 are
5 3 9 10 

3 is found at index 1 in set1
5 3 10 

The elements in set2 are-
4 3 7 200 

Intersection of set1 and set2
3 

Union of set1 and set2
5 3 10 4 7 200 

Operaciones adicionales: 
las siguientes son operaciones que también se implementan de manera eficiente utilizando un conjunto disperso. Supera todas las soluciones discutidas aquí y la solución basada en vectores de bits, bajo el supuesto de que se conocen el rango y el número máximo de elementos. 

union(): 
1) Cree un conjunto disperso vacío, digamos resultado. 
2) Recorra el primer conjunto e inserte todos sus elementos en el resultado. 
3) Atraviese el segundo conjunto e inserte todos sus elementos en el resultado (tenga en cuenta que el conjunto disperso no inserta una entrada si ya está presente) 
4) Devuelve el resultado. 

intersection(): 
1) Cree un conjunto disperso vacío, digamos resultado. 
2) Sea el menor de dos conjuntos dados el primero y el mayor el segundo. 
3) Considere el conjunto más pequeño y busque cada elemento del mismo en segundo lugar. Si se encuentra el elemento, agréguelo al resultado. 
4) Devolver resultado.
Un uso común de esta estructura de datos es con algoritmos de asignación de registros en compiladores, que tienen un universo fijo (la cantidad de registros en la máquina) y se actualizan y borran con frecuencia (al igual que las consultas Q) durante una sola ejecución de procesamiento.

Referencias:  
http://research.switch.com/sparse  
http://codingplayground.blogspot.in/2009/03/sparse-sets-with-o1-insert-delete.html
Este artículo es una contribución de Rachit Belwariar . Escriba comentarios si encuentra algo incorrecto o si desea compartir más información sobre el tema tratado anteriormente. 
 

Publicación traducida automáticamente

Artículo escrito por GeeksforGeeks-1 y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *