Ir al contenido principal

Diccionarios y conjuntos

Los diccionarios y conjuntos son estructuras de datos fundamentales que complementan a los arrays y listas al ofrecer formas diferentes de organizar y acceder a la información. Mientras que los arrays utilizan índices numéricos para acceder a sus elementos, los diccionarios emplean claves únicas que pueden ser de diversos tipos, proporcionando un acceso más intuitivo y eficiente en muchos escenarios. Los conjuntos, por su parte, se especializan en almacenar elementos únicos sin duplicados, siendo ideales para operaciones matemáticas de conjuntos.

Dominar estas estructuras de datos te permitirá crear aplicaciones más eficientes y elegantes, especialmente cuando necesites realizar búsquedas rápidas, mantener colecciones de elementos únicos o establecer relaciones clave-valor entre datos.

Diccionarios en C#

Un diccionario es una colección de pares clave-valor donde cada clave es única y se asocia con un valor específico. En C#, la clase Dictionary<TKey, TValue> es la implementación más común de esta estructura de datos.

Características principales de los diccionarios

Característica Descripción
Claves únicas Cada clave debe ser única dentro del diccionario
Acceso rápido Búsqueda, inserción y eliminación en tiempo O(1) promedio
Pares clave-valor Cada elemento es una asociación entre una clave y un valor
Tipos genéricos Permite especificar el tipo de las claves y valores
Orden no garantizado Los elementos no mantienen un orden específico

Declaración e inicialización

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        // Declaración de un diccionario vacío
        Dictionary<string, int> edades = new Dictionary<string, int>();
        
        // Declaración e inicialización con valores
        Dictionary<string, string> capitales = new Dictionary<string, string>
        {
            {"España", "Madrid"},
            {"Francia", "París"},
            {"Italia", "Roma"},
            {"Portugal", "Lisboa"}
        };
        
        // Inicialización usando la sintaxis de índices
        Dictionary<int, string> meses = new Dictionary<int, string>
        {
            [1] = "Enero",
            [2] = "Febrero",
            [3] = "Marzo",
            [4] = "Abril"
        };
        
        Console.WriteLine("Diccionarios creados correctamente");
    }
}

Operaciones básicas con diccionarios

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        Dictionary<string, double> productos = new Dictionary<string, double>();
        
        // Añadir elementos
        productos.Add("Pan", 1.20);
        productos.Add("Leche", 0.85);
        productos["Huevos"] = 2.50; // Forma alternativa
        
        // Acceder a elementos
        Console.WriteLine($"Precio del pan: {productos["Pan"]}€");
        
        // Verificar si existe una clave
        if (productos.ContainsKey("Leche"))
        {
            Console.WriteLine($"Precio de la leche: {productos["Leche"]}€");
        }
        
        // Intentar obtener un valor de forma segura
        if (productos.TryGetValue("Azúcar", out double precioAzucar))
        {
            Console.WriteLine($"Precio del azúcar: {precioAzucar}€");
        }
        else
        {
            Console.WriteLine("El azúcar no está en el catálogo");
        }
        
        // Modificar un valor
        productos["Pan"] = 1.35;
        Console.WriteLine($"Nuevo precio del pan: {productos["Pan"]}€");
        
        // Eliminar elementos
        productos.Remove("Huevos");
        Console.WriteLine($"Productos restantes: {productos.Count}");
    }
}

Recorrido de diccionarios

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        Dictionary<string, int> puntuaciones = new Dictionary<string, int>
        {
            {"Ana", 95},
            {"Luis", 87},
            {"María", 92},
            {"Carlos", 78}
        };
        
        Console.WriteLine("=== Recorrido de todo el diccionario ===");
        foreach (KeyValuePair<string, int> elemento in puntuaciones)
        {
            Console.WriteLine($"{elemento.Key}: {elemento.Value} puntos");
        }
        
        Console.WriteLine("\n=== Recorrido solo de las claves ===");
        foreach (string nombre in puntuaciones.Keys)
        {
            Console.WriteLine($"Estudiante: {nombre}");
        }
        
        Console.WriteLine("\n=== Recorrido solo de los valores ===");
        foreach (int puntuacion in puntuaciones.Values)
        {
            Console.WriteLine($"Puntuación: {puntuacion}");
        }
        
        Console.WriteLine("\n=== Usando var para simplificar ===");
        foreach (var item in puntuaciones)
        {
            Console.WriteLine($"{item.Key} obtuvo {item.Value} puntos");
        }
    }
}

Conjuntos en C#

Un conjunto es una colección de elementos únicos donde no se permiten duplicados. En C#, la clase HashSet<T> es la implementación principal para trabajar con conjuntos.

Características principales de los conjuntos

Característica Descripción
Elementos únicos No permite elementos duplicados
Operaciones matemáticas Soporta unión, intersección, diferencia, etc.
Acceso rápido Búsqueda e inserción en tiempo O(1) promedio
Sin orden garantizado Los elementos no mantienen un orden específico
Comparación eficiente Permite comparar conjuntos de forma eficiente

Declaración e inicialización de conjuntos

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        // Conjunto vacío
        HashSet<string> colores = new HashSet<string>();
        
        // Conjunto con inicialización
        HashSet<int> numerosPares = new HashSet<int> {2, 4, 6, 8, 10};
        
        // Conjunto desde un array (elimina duplicados automáticamente)
        int[] numerosConDuplicados = {1, 2, 3, 2, 4, 3, 5, 1};
        HashSet<int> numerosUnicos = new HashSet<int>(numerosConDuplicados);
        
        Console.WriteLine("Números únicos del array:");
        foreach (int numero in numerosUnicos)
        {
            Console.Write($"{numero} ");
        }
        Console.WriteLine($"\nTotal de números únicos: {numerosUnicos.Count}");
    }
}

Operaciones básicas con conjuntos

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        HashSet<string> frutas = new HashSet<string>();
        
        // Añadir elementos
        frutas.Add("Manzana");
        frutas.Add("Pera");
        frutas.Add("Plátano");
        
        // Intentar añadir duplicado
        bool añadido = frutas.Add("Manzana");
        Console.WriteLine($"¿Se añadió 'Manzana' otra vez? {añadido}");
        
        // Verificar si contiene un elemento
        if (frutas.Contains("Pera"))
        {
            Console.WriteLine("El conjunto contiene peras");
        }
        
        // Eliminar elemento
        frutas.Remove("Plátano");
        
        Console.WriteLine($"Frutas en el conjunto: {frutas.Count}");
        foreach (string fruta in frutas)
        {
            Console.WriteLine($"- {fruta}");
        }
        
        // Limpiar el conjunto
        frutas.Clear();
        Console.WriteLine($"Frutas después de limpiar: {frutas.Count}");
    }
}

Operaciones matemáticas con conjuntos

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        HashSet<int> conjunto1 = new HashSet<int> {1, 2, 3, 4, 5};
        HashSet<int> conjunto2 = new HashSet<int> {4, 5, 6, 7, 8};
        
        Console.WriteLine("Conjunto 1: {1, 2, 3, 4, 5}");
        Console.WriteLine("Conjunto 2: {4, 5, 6, 7, 8}");
        
        // Unión (todos los elementos de ambos conjuntos)
        HashSet<int> union = new HashSet<int>(conjunto1);
        union.UnionWith(conjunto2);
        Console.Write("Unión: {");
        Console.Write(string.Join(", ", union));
        Console.WriteLine("}");
        
        // Intersección (elementos comunes)
        HashSet<int> interseccion = new HashSet<int>(conjunto1);
        interseccion.IntersectWith(conjunto2);
        Console.Write("Intersección: {");
        Console.Write(string.Join(", ", interseccion));
        Console.WriteLine("}");
        
        // Diferencia (elementos del primer conjunto que no están en el segundo)
        HashSet<int> diferencia = new HashSet<int>(conjunto1);
        diferencia.ExceptWith(conjunto2);
        Console.Write("Diferencia (1-2): {");
        Console.Write(string.Join(", ", diferencia));
        Console.WriteLine("}");
        
        // Diferencia simétrica (elementos que están en uno u otro, pero no en ambos)
        HashSet<int> diferenciaSimetrica = new HashSet<int>(conjunto1);
        diferenciaSimetrica.SymmetricExceptWith(conjunto2);
        Console.Write("Diferencia simétrica: {");
        Console.Write(string.Join(", ", diferenciaSimetrica));
        Console.WriteLine("}");
    }
}

Comparaciones entre conjuntos

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        HashSet<char> vocales = new HashSet<char> {'a', 'e', 'i', 'o', 'u'};
        HashSet<char> vocalesAbiertas = new HashSet<char> {'a', 'e', 'o'};
        HashSet<char> letras = new HashSet<char> {'a', 'b', 'c', 'd', 'e'};
        
        // Verificar si un conjunto es subconjunto de otro
        bool esSubconjunto = vocalesAbiertas.IsSubsetOf(vocales);
        Console.WriteLine($"¿Las vocales abiertas son subconjunto de las vocales? {esSubconjunto}");
        
        // Verificar si un conjunto es superconjunto de otro
        bool esSuperconjunto = vocales.IsSupersetOf(vocalesAbiertas);
        Console.WriteLine($"¿Las vocales son superconjunto de las vocales abiertas? {esSuperconjunto}");
        
        // Verificar si dos conjuntos se solapan
        bool seSolapan = vocales.Overlaps(letras);
        Console.WriteLine($"¿Las vocales y las letras se solapan? {seSolapan}");
        
        // Verificar si dos conjuntos son iguales
        HashSet<char> otrasVocales = new HashSet<char> {'u', 'o', 'i', 'e', 'a'};
        bool sonIguales = vocales.SetEquals(otrasVocales);
        Console.WriteLine($"¿Los conjuntos de vocales son iguales? {sonIguales}");
    }
}

Ejemplo práctico: sistema de gestión de estudiantes

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        // Diccionario para almacenar estudiantes y sus asignaturas
        Dictionary<string, HashSet<string>> estudiantes = new Dictionary<string, HashSet<string>>();
        
        // Añadir estudiantes con sus asignaturas
        estudiantes["Ana"] = new HashSet<string> {"Matemáticas", "Física", "Historia"};
        estudiantes["Luis"] = new HashSet<string> {"Matemáticas", "Química", "Inglés"};
        estudiantes["María"] = new HashSet<string> {"Física", "Química", "Historia", "Inglés"};
        estudiantes["Carlos"] = new HashSet<string> {"Matemáticas", "Historia"};
        
        Console.WriteLine("=== Sistema de Gestión de Estudiantes ===\n");
        
        // Mostrar todos los estudiantes y sus asignaturas
        foreach (var estudiante in estudiantes)
        {
            Console.WriteLine($"{estudiante.Key}:");
            foreach (string asignatura in estudiante.Value)
            {
                Console.WriteLine($"  - {asignatura}");
            }
            Console.WriteLine();
        }
        
        // Encontrar estudiantes que cursan una asignatura específica
        string asignaturaBuscada = "Matemáticas";
        Console.WriteLine($"Estudiantes que cursan {asignaturaBuscada}:");
        foreach (var estudiante in estudiantes)
        {
            if (estudiante.Value.Contains(asignaturaBuscada))
            {
                Console.WriteLine($"- {estudiante.Key}");
            }
        }
        
        // Encontrar asignaturas comunes entre dos estudiantes
        Console.WriteLine("\nAsignaturas comunes entre Ana y María:");
        HashSet<string> comunesAnaMaria = new HashSet<string>(estudiantes["Ana"]);
        comunesAnaMaria.IntersectWith(estudiantes["María"]);
        foreach (string asignatura in comunesAnaMaria)
        {
            Console.WriteLine($"- {asignatura}");
        }
        
        // Contar estudiantes por asignatura
        Dictionary<string, int> contadorAsignaturas = new Dictionary<string, int>();
        foreach (var estudiante in estudiantes)
        {
            foreach (string asignatura in estudiante.Value)
            {
                if (contadorAsignaturas.ContainsKey(asignatura))
                {
                    contadorAsignaturas[asignatura]++;
                }
                else
                {
                    contadorAsignaturas[asignatura] = 1;
                }
            }
        }
        
        Console.WriteLine("\nNúmero de estudiantes por asignatura:");
        foreach (var asignatura in contadorAsignaturas)
        {
            Console.WriteLine($"{asignatura.Key}: {asignatura.Value} estudiantes");
        }
    }
}

Cuándo usar diccionarios vs conjuntos

Usar diccionarios cuando:

  • Necesites asociar claves con valores específicos
  • Requieras búsquedas rápidas por clave
  • Manejes datos con relaciones clave-valor
  • Implementes cachés o índices

Usar conjuntos cuando:

  • Necesites garantizar elementos únicos
  • Realices operaciones matemáticas de conjuntos
  • Elimines duplicados de una colección
  • Compruebes pertenencia de elementos de forma eficiente

Resumen

Los diccionarios y conjuntos son estructuras de datos poderosas que amplían significativamente tus capacidades para organizar y manipular información en C#. Los diccionarios te permiten crear asociaciones eficientes entre claves y valores, siendo ideales para búsquedas rápidas y almacenamiento de datos relacionados. Los conjuntos, por su parte, garantizan la unicidad de elementos y proporcionan operaciones matemáticas útiles para el análisis de datos.

Dominar estas estructuras te permitirá escribir código más eficiente y elegante, especialmente en aplicaciones que manejan grandes volúmenes de datos o requieren búsquedas y comparaciones frecuentes. En el siguiente tema exploraremos las pilas y colas, estructuras que siguen principios de acceso específicos y son fundamentales en muchos algoritmos y aplicaciones.