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.