OPE 2025 TFA INF. Tema 71. Tipos elementales de datos. Estructuras convencionales de datos. Estructuras dinámicas de datos. Ficheros. Tipos de ficheros: descripción, funcionalidad y clasificación. Organización de ficheros. Concepto y tipos: métodos de acceso en el tratamiento de un fichero.

Servicio Andaluz de Salud EXAMEN INFORMÁTICA JUNTA DE ANDALUCÍA TFA INFORMÁTICA (P) TFA INF (P) TFA INFORMÁTICA
Tema 71: Tipos de Datos, Estructuras de Datos y Ficheros
TEMA 71

Tipos de Datos, Estructuras de Datos y Ficheros

Tipos Elementales, Estructuras Convencionales y Dinámicas, Organización y Acceso a Ficheros

📌 Introducción

Los tipos de datos y las estructuras de datos constituyen los fundamentos esenciales de la programación y el desarrollo de software. Comprender cómo los datos se representan en memoria, cómo se organizan mediante estructuras apropiadas, y cómo se almacenan persistentemente en ficheros es crucial para diseñar algoritmos eficientes y sistemas informáticos robustos. Los tipos elementales de datos (enteros, reales, caracteres, booleanos) proporcionan los bloques de construcción básicos con los que operan los programas. Las estructuras de datos —tanto convencionales (arrays, registros) como dinámicas (listas enlazadas, árboles, grafos)— permiten organizar colecciones de datos de manera que faciliten operaciones específicas de inserción, eliminación, búsqueda y recorrido. Los ficheros, por su parte, posibilitan el almacenamiento persistente de información más allá de la ejecución del programa, con diferentes organizaciones (secuencial, directa, indexada) optimizadas para distintos patrones de acceso. Este tema proporciona una base conceptual sólida y práctica que todo profesional de informática debe dominar, independientemente del paradigma de programación o tecnología específica que utilice posteriormente.

1. Tipos Elementales de Datos

1.1. Concepto de Tipo de Dato

Un tipo de dato define el conjunto de valores que una variable puede tomar y las operaciones que se pueden realizar sobre esos valores. Los tipos de datos proporcionan:

  • Abstracción: Ocultan detalles de representación en memoria
  • Seguridad: Previenen operaciones inválidas mediante verificación de tipos
  • Eficiencia: Permiten al compilador/intérprete optimizar almacenamiento y operaciones
  • Legibilidad: Hacen el código más comprensible declarando qué representa cada variable

1.2. Tipos Elementales Fundamentales

Tipo Descripción Rango/Valores Tamaño Típico Operaciones
Entero (Integer) Números enteros sin parte decimal int: -2,147,483,648 a 2,147,483,647
long: ±9 quintillones aprox.
int: 4 bytes
long: 8 bytes
+, -, *, /, %, comparaciones
Real/Flotante (Float/Double) Números con parte decimal (coma flotante) float: ±3.4E38 (7 dígitos precisión)
double: ±1.7E308 (15 dígitos)
float: 4 bytes
double: 8 bytes
+, -, *, /, potencias, raíces, trascendentes
Carácter (Char) Símbolos individuales (letras, dígitos, signos) ASCII: 0-255
Unicode: 0-1,114,111
1 byte (ASCII)
2-4 bytes (Unicode)
Comparaciones, concatenación, conversiones
Booleano (Boolean) Valores lógicos true (verdadero)
false (falso)
1 bit (teórico)
1 byte (típico)
AND, OR, NOT, XOR, comparaciones
Cadena (String) Secuencia de caracteres Longitud variable (típicamente hasta límites de memoria) Variable Concatenación, subcadenas, búsqueda, comparación

1.3. Representación en Memoria

Enteros: Complemento a Dos

Los enteros con signo se representan típicamente usando complemento a dos, que permite aritmética eficiente:

Ejemplo 8 bits:
+5:  00000101
-5:  11111011  (invertir bits de +5 y sumar 1)

Ventaja: Suma/resta funcionan igual independientemente del signo
+5 + (-5) = 00000101 + 11111011 = 00000000 = 0

Reales: Formato IEEE 754

Los números de coma flotante se representan según el estándar IEEE 754:

ℹ️ Estructura IEEE 754 (32 bits float)

  • 1 bit de signo: 0 = positivo, 1 = negativo
  • 8 bits exponente: Con sesgo de 127
  • 23 bits mantisa: Parte fraccionaria normalizada (bit implícito 1)

Valor = (-1)^signo × 1.mantisa × 2^(exponente-127)

Ejemplo: 5.75 = 101.11₂ = 1.0111₂ × 2²

  • Signo: 0
  • Exponente: 2 + 127 = 129 = 10000001₂
  • Mantisa: 0111 0000 0000 0000 0000 000

Caracteres: ASCII y Unicode

  • ASCII (American Standard Code for Information Interchange): 7 bits (128 caracteres), extendido a 8 bits (256 caracteres). ‘A’ = 65, ‘a’ = 97, ‘0’ = 48
  • Unicode: Estándar universal con >1 millón de caracteres
    • UTF-8: Codificación variable 1-4 bytes, compatible con ASCII
    • UTF-16: 2 o 4 bytes
    • UTF-32: 4 bytes fijos

1.4. Tipado Fuerte vs. Débil

Característica Tipado Fuerte Tipado Débil
Definición Tipos estrictamente enforced, conversiones explícitas requeridas Conversiones implícitas automáticas entre tipos
Verificación Errores de tipo detectados en compilación/ejecución Conversiones automáticas pueden ocultar errores
Seguridad Mayor seguridad, menos bugs sutiles Más flexible pero potencialmente inseguro
Ejemplos Lenguajes Java, C#, Python, Haskell C, JavaScript, PHP
Ejemplo Código int x = 5;
String s = «hola»;
// x = s; // ERROR!
var x = 5;
var s = «hola»;
x = s; // OK en JS

1.5. Tipado Estático vs. Dinámico

  • Tipado Estático (Java, C++, C#, Go): Tipos determinados en compilación
    • Ventajas: Detección temprana de errores, optimización, documentación implícita, IDEs más potentes
    • Desventajas: Más verboso, menos flexible
  • Tipado Dinámico (Python, JavaScript, Ruby, PHP): Tipos determinados en ejecución
    • Ventajas: Código más conciso, mayor flexibilidad, desarrollo rápido
    • Desventajas: Errores de tipo solo se detectan al ejecutar, menor rendimiento

2. Estructuras Convencionales de Datos

2.1. Concepto de Estructura de Datos

Una estructura de datos es una forma particular de organizar y almacenar datos para que puedan ser accedidos y modificados eficientemente. La elección de la estructura de datos apropiada depende de:

  • Operaciones requeridas: Búsqueda, inserción, eliminación, ordenación, recorrido
  • Frecuencia de operaciones: Qué operaciones son más comunes
  • Restricciones de espacio: Memoria disponible
  • Restricciones de tiempo: Requisitos de rendimiento
  • Complejidad de implementación: Simplicidad vs. sofisticación

2.2. Arrays (Arreglos/Vectores)

✅ Características de Arrays

  • Definición: Colección de elementos del mismo tipo almacenados contiguamente en memoria
  • Acceso: Directo mediante índice en tiempo O(1)
  • Tamaño: Fijo en lenguajes estáticos (C, Java), dinámico en otros (Python)
  • Dimensiones: Unidimensionales (vectores), multidimensionales (matrices, tensores)
  • Memoria: Contigua, predecible, cache-friendly

Operaciones en Arrays

Operación Complejidad Descripción
Acceso por índice O(1) array[i] accede directamente al elemento i
Búsqueda (no ordenado) O(n) Recorrido secuencial hasta encontrar elemento
Búsqueda (ordenado) O(log n) Búsqueda binaria
Inserción al final O(1) Si hay espacio disponible
Inserción en posición O(n) Requiere desplazar elementos
Eliminación O(n) Requiere desplazar elementos para llenar hueco

Arrays Multidimensionales

Los arrays multidimensionales se pueden almacenar en memoria de dos formas:

  • Row-major order (fila principal): Elementos de una fila están contiguos. Usado en C, C++, Python
    Array 2D: [1 2 3]    Memoria: [1 2 3 4 5 6 7 8 9]
              [4 5 6]
              [7 8 9]
  • Column-major order (columna principal): Elementos de una columna están contiguos. Usado en Fortran, MATLAB
    Array 2D: [1 2 3]    Memoria: [1 4 7 2 5 8 3 6 9]
              [4 5 6]
              [7 8 9]

2.3. Registros (Records/Structs)

Un registro es una estructura de datos heterogénea que agrupa elementos de diferentes tipos bajo un único nombre.

            // Ejemplo en C
struct Empleado {
    int id;
    char nombre[50];
    float salario;
    struct Fecha fechaContratacion;
};

// Ejemplo en Python (usando dataclass)
from dataclasses import dataclass

@dataclass
class Empleado:
    id: int
    nombre: str
    salario: float
    fecha_contratacion: datetime

Características de Registros

  • Heterogeneidad: Campos pueden ser de diferentes tipos
  • Acceso por nombre: empleado.nombre, empleado.salario
  • Memoria: Campos almacenados secuencialmente (con posible padding para alineación)
  • Anidación: Registros pueden contener otros registros
  • Uso: Modelar entidades del mundo real con múltiples atributos

2.4. Cadenas de Caracteres (Strings)

Las cadenas son secuencias de caracteres. Su implementación varía según lenguaje:

  • C: Array de char terminado en '\0' (null-terminated)
    char saludo[] = "Hola";  // Internamente: ['H','o','l','a','\0']
  • Java/C#: Objetos inmutables con longitud explícita
    String saludo = "Hola";  // Objeto String inmutable
  • Python: Secuencias inmutables de caracteres Unicode
    saludo = "Hola"  # str inmutable, soporta Unicode nativo

Operaciones Comunes en Cadenas

  • Concatenación: "Hola" + " Mundo" → "Hola Mundo"
  • Subcadenas: "Hola"[0:2] → "Ho"
  • Longitud: len("Hola") → 4
  • Búsqueda: "Hola".find("la") → 2
  • Reemplazo: "Hola".replace("o", "a") → "Hala"
  • División: "uno,dos,tres".split(",") → ["uno", "dos", "tres"]

2.5. Enumeraciones (Enums)

Tipo de dato que consiste en un conjunto de constantes nombradas:

// Java
enum DiaSemana {
    LUNES, MARTES, MIERCOLES, JUEVES, VIERNES, SABADO, DOMINGO
}

// Python
from enum import Enum

class DiaSemana(Enum):
    LUNES = 1
    MARTES = 2
    MIERCOLES = 3
    JUEVES = 4
    VIERNES = 5
    SABADO = 6
    DOMINGO = 7

Ventajas de Enumeraciones:

  • Código más legible (nombres descriptivos vs. números mágicos)
  • Seguridad de tipos (solo valores válidos permitidos)
  • Facilita refactorización
  • Autodocumentación del conjunto de valores posibles

3. Estructuras Dinámicas de Datos

3.1. Concepto de Estructura Dinámica

Las estructuras dinámicas utilizan asignación dinámica de memoria (heap) y punteros/referencias para crecer y decrecer en tiempo de ejecución según necesidad. A diferencia de arrays con tamaño fijo, las estructuras dinámicas se adaptan a volúmenes de datos variables.

⚠️ Gestión de Memoria en Estructuras Dinámicas

  • Lenguajes con gestión manual (C, C++): Programador responsable de malloc/free o new/delete. Riesgo de memory leaks y dangling pointers
  • Lenguajes con garbage collection (Java, C#, Python): Recolector de basura automáticamente libera memoria no referenciada
  • Lenguajes con ownership (Rust): Sistema de tipos garantiza seguridad de memoria en compilación sin GC

3.2. Listas Enlazadas

Lista Simplemente Enlazada

Secuencia de nodos donde cada nodo contiene datos y referencia al siguiente nodo.

Estructura Nodo:
┌─────────┬──────┐
│ Dato: 5 │ Next │─── →
└─────────┴──────┘

Lista: [10] → [20] → [30] → NULL

Implementación Python:
class Nodo:
    def __init__(self, dato):
        self.dato = dato
        self.siguiente = None

class ListaEnlazada:
    def __init__(self):
        self.cabeza = None
    
    def insertar_inicio(self, dato):
        nuevo = Nodo(dato)
        nuevo.siguiente = self.cabeza
        self.cabeza = nuevo

Ventajas y Desventajas de Listas Enlazadas

Ventajas Desventajas
Inserción/eliminación O(1) conociendo posición Acceso secuencial O(n), no acceso directo
Tamaño dinámico, crece/decrece según necesidad Overhead de memoria por punteros
No requiere memoria contigua Cache-unfriendly, mala localidad espacial
Fácil implementación de pilas/colas No permite búsqueda binaria

Variantes de Listas Enlazadas

  • Lista doblemente enlazada: Cada nodo tiene punteros a siguiente Y anterior, permite recorrido bidireccional
    NULL ← [10] ↔ [20] ↔ [30] → NULL
  • Lista circular: Último nodo apunta al primero, no hay NULL
        ┌────────────────┐
    ↓ │ [10] → [20] → [30]┘
  • Lista con nodo centinela: Nodo especial ficticio simplifica lógica de inserción/eliminación

3.3. Pilas (Stacks)

Estructura LIFO (Last In, First Out - Último en Entrar, Primero en Salir)

ℹ️ Operaciones de Pila

  • push(x): Añadir elemento en el tope
  • pop(): Eliminar y retornar elemento del tope
  • peek()/top(): Ver elemento del tope sin eliminarlo
  • isEmpty(): Verificar si pila está vacía

Complejidad: Todas las operaciones son O(1)

Visualización:
      ┌───┐
      │ 3 │ ← tope (push 3)
      ├───┤
      │ 2 │
      ├───┤
      │ 1 │
      └───┘

Aplicaciones:
- Evaluación de expresiones (notación postfija)
- Gestión de llamadas a funciones (call stack)
- Deshacer/Rehacer en editores
- Navegación hacia atrás en navegadores
- Parsing de lenguajes

3.4. Colas (Queues)

Estructura FIFO (First In, First Out - Primero en Entrar, Primero en Salir)

ℹ️ Operaciones de Cola

  • enqueue(x): Añadir elemento al final
  • dequeue(): Eliminar y retornar elemento del frente
  • front(): Ver elemento del frente sin eliminarlo
  • isEmpty(): Verificar si cola está vacía

Complejidad: Todas las operaciones son O(1)

Visualización:
  frente                    final
    ↓                         ↓
   [1] → [2] → [3] → [4] → [5]
    
enqueue(6): añadir al final
dequeue(): eliminar desde frente (retorna 1)

Aplicaciones:
- Buffers de impresión
- Manejo de procesos en CPU (scheduling)
- Breadth-First Search (BFS) en grafos
- Gestión de tareas asíncronas
- Colas de mensajes en sistemas distribuidos

Variantes de Colas

  • Cola circular: Implementación eficiente usando array con índices wrap-around
  • Cola de prioridad: Elementos tienen prioridad, se extrae el de mayor prioridad (implementada típicamente con heap)
  • Deque (Double-Ended Queue): Permite inserción/eliminación en ambos extremos

3.5. Árboles

Estructura jerárquica donde cada nodo tiene un padre (excepto raíz) y cero o más hijos.

Terminología de Árboles

  • Raíz: Nodo superior sin padre
  • Hoja: Nodo sin hijos
  • Nivel: Distancia de un nodo a la raíz (raíz en nivel 0)
  • Altura: Nivel máximo en el árbol
  • Grado: Número de hijos de un nodo
  • Subárbol: Árbol formado por un nodo y sus descendientes

Árbol Binario

Cada nodo tiene como máximo dos hijos (izquierdo y derecho)

Representación:
        (10)
       /    \
     (5)    (15)
    /  \    /  \
  (3) (7)(12)(20)

Implementación:
class NodoArbol:
    def __init__(self, dato):
        self.dato = dato
        self.izquierdo = None
        self.derecho = None

Árbol Binario de Búsqueda (BST)

Árbol binario con propiedad de ordenación: para cada nodo, todos los valores en subárbol izquierdo son menores y todos en subárbol derecho son mayores.

✅ Operaciones en BST (promedio)

  • Búsqueda: O(log n) - comparar y descender por rama apropiada
  • Inserción: O(log n) - buscar posición y añadir hoja
  • Eliminación: O(log n) - casos: hoja, un hijo, dos hijos

Peor caso: O(n) si árbol degenerado (lista enlazada)

Solución: Árboles auto-balanceados (AVL, Red-Black Tree)

Recorridos de Árboles Binarios

Recorrido Orden de Visita Ejemplo (árbol anterior) Uso Típico
Preorden (Preorder) Raíz → Izquierda → Derecha 10, 5, 3, 7, 15, 12, 20 Copiar árbol, serialización
Inorden (Inorder) Izquierda → Raíz → Derecha 3, 5, 7, 10, 12, 15, 20 BST en orden ascendente
Postorden (Postorder) Izquierda → Derecha → Raíz 3, 7, 5, 12, 20, 15, 10 Eliminar árbol, calcular expresiones
Por Niveles (Level-order) Nivel por nivel, izq. a der. 10, 5, 15, 3, 7, 12, 20 BFS, imprimir por niveles

3.6. Grafos

Conjunto de vértices (nodos) conectados por aristas (edges). Generalizan árboles permitiendo ciclos y múltiples padres.

Tipos de Grafos

  • Dirigidos vs. No dirigidos: Aristas con/sin dirección
  • Ponderados vs. No ponderados: Aristas con/sin peso asociado
  • Conexos vs. Disconexos: Existe/no existe camino entre cualquier par de vértices
  • Cíclicos vs. Acíclicos: Contienen/no contienen ciclos

Representaciones de Grafos

Representación Descripción Espacio Ventajas Desventajas
Matriz de Adyacencia Matriz n×n donde M[i][j]=1 si hay arista i→j O(V²) Verificación de arista O(1), simple para grafos densos Ineficiente para grafos sparse, O(V²) espacio siempre
Lista de Adyacencia Array de listas, cada vértice tiene lista de vecinos O(V+E) Eficiente para grafos sparse, recorrer vecinos rápido Verificación de arista O(V) en peor caso
Lista de Aristas Lista de pares (u,v) representando aristas O(E) Simple, compacto Ineficiente para búsqueda de vecinos

Algoritmos Fundamentales en Grafos

  • Recorrido: DFS (Depth-First Search), BFS (Breadth-First Search)
  • Camino más corto: Dijkstra, Bellman-Ford, Floyd-Warshall
  • Árbol de Expansión Mínima: Kruskal, Prim
  • Ordenación Topológica: Para grafos dirigidos acíclicos (DAG)
  • Detección de Ciclos: Mediante DFS con colores
  • Componentes Conexas: Identificar subgrafos desconectados

3.7. Tablas Hash (Hash Tables)

Estructura que mapea claves a valores mediante función hash, proporcionando acceso en tiempo promedio O(1).

ℹ️ Componentes de Tabla Hash

  • Array subyacente: Almacena elementos
  • Función hash: h(key) → índice en array
  • Manejo de colisiones: Cuando dos claves tienen mismo hash

Resolución de Colisiones

Método Descripción Ventajas Desventajas
Encadenamiento (Chaining) Cada celda contiene lista enlazada de elementos con mismo hash Simple, factor de carga >1 posible, eliminación sencilla Memoria extra para punteros, cache-unfriendly
Direccionamiento Abierto (Open Addressing) Buscar siguiente posición libre mediante probing Mejor localidad cache, sin punteros extra Factor de carga debe mantenerse bajo, eliminación compleja
- Linear Probing Secuencia h(k), h(k)+1, h(k)+2, ... Simple, buena localidad Clustering primario
- Quadratic Probing Secuencia h(k), h(k)+1², h(k)+2², ... Reduce clustering primario Clustering secundario
- Double Hashing Secuencia h(k), h(k)+h2(k), h(k)+2*h2(k), ... Minimiza clustering Dos funciones hash necesarias

4. Ficheros

4.1. Concepto de Fichero

Un fichero (archivo) es una colección de información relacionada almacenada en dispositivo de almacenamiento secundario (disco duro, SSD, etc.) que persiste más allá de la ejecución del programa. Los ficheros permiten:

  • Persistencia: Datos sobreviven al cierre del programa
  • Compartición: Múltiples programas pueden acceder al mismo fichero
  • Almacenamiento masivo: Datos que no caben en memoria RAM
  • Backup y recuperación: Copias de seguridad de información crítica
  • Transferencia: Intercambio de datos entre sistemas

4.2. Atributos de Ficheros

Los sistemas de ficheros asocian metadatos a cada fichero:

Atributo Descripción Ejemplo
Nombre Identificador legible por humanos documento.txt, foto.jpg
Tipo/Extensión Indica formato del contenido .txt, .pdf, .exe, .csv
Tamaño Número de bytes 1,024 bytes (1 KB)
Ubicación Dirección en disco Bloques 1000-1005
Permisos Control de acceso (lectura, escritura, ejecución) rwxr-xr-- (Unix)
Propietario Usuario/grupo dueño del fichero usuario:grupo
Fechas Creación, modificación, último acceso 2023-10-15 14:30:00

4.3. Tipos de Ficheros según Contenido

Ficheros de Texto

  • Contenido: Secuencia de caracteres legibles (ASCII, UTF-8)
  • Estructura: Líneas separadas por caracteres newline (\n, \r\n)
  • Legibilidad: Pueden abrirse con editores de texto
  • Portabilidad: Alta entre sistemas operativos (aunque newline varía)
  • Ejemplos: .txt, .csv, .json, .xml, .html, código fuente
  • Ventajas: Legibles por humanos, fácil debugging, versionado amigable (git)
  • Desventajas: Mayor tamaño, parsing necesario, menor eficiencia

Ficheros Binarios

  • Contenido: Secuencia de bytes que representan datos en formato interno de la máquina
  • Estructura: Definida por especificación del formato
  • Legibilidad: No legibles en editores de texto, requieren software específico
  • Portabilidad: Potencialmente problemática (endianness, tamaños de tipos)
  • Ejemplos: .exe, .dll, .jpg, .mp3, .pdf, .docx, bases de datos
  • Ventajas: Compactos, acceso directo sin parsing, eficientes
  • Desventajas: No legibles por humanos, dependientes de formato/software

4.4. Organización de Ficheros

4.4.1. Ficheros Secuenciales

Registros almacenados uno tras otro en orden de entrada.

✅ Características de Ficheros Secuenciales

  • Estructura: Registros consecutivos, sin índices ni punteros
  • Acceso: Secuencial desde principio, no acceso directo
  • Inserción: Solo al final (append) sin reorganizar
  • Actualización: Requiere reescribir fichero completo típicamente
  • Búsqueda: Secuencial O(n), recorrer hasta encontrar
  • Ordenación: Puede mantenerse orden lógico pero no físico obligatorio
  • Uso: Logs, backups, archivos históricos, procesamiento batch
Representación:
┌─────────┬─────────┬─────────┬─────────┬─────────┐
│ Reg 1   │ Reg 2   │ Reg 3   │ Reg 4   │ Reg 5   │
└─────────┴─────────┴─────────┴─────────┴─────────┘

Para leer Registro 4: leer Reg 1, Reg 2, Reg 3, Reg 4

Ejemplo Python:
# Escritura
with open('empleados.txt', 'a') as f:
    f.write(f"{id},{nombre},{salario}\n")

# Lectura secuencial
with open('empleados.txt', 'r') as f:
    for linea in f:
        procesar(linea)

Ventajas y Desventajas de Ficheros Secuenciales

Ventajas Desventajas
Simple de implementar y entender Acceso lento a registros específicos
Eficiente para procesamiento completo del fichero Actualización/eliminación ineficiente
No requiere estructuras adicionales (índices) No adecuado para consultas aleatorias
Óptimo para datos con acceso secuencial natural Inserción solo al final

4.4.2. Ficheros Directos (Acceso Aleatorio)

Registros de tamaño fijo almacenados de modo que cualquier registro puede accederse directamente mediante su posición.

ℹ️ Características de Ficheros Directos

  • Estructura: Registros de tamaño fijo, posición calculable
  • Acceso: Directo a cualquier registro en tiempo O(1)
  • Cálculo posición: Posición_byte = registro_num × tamaño_registro
  • Inserción: En cualquier posición libre
  • Actualización: In-place, sin mover otros registros
  • Búsqueda: Directo si se conoce posición, secuencial si búsqueda por contenido
  • Uso: Bases de datos, índices, aplicaciones que requieren acceso aleatorio rápido
Representación (registros de 100 bytes cada uno):
Byte:  0       100     200     300     400     500
      ┌───────┬───────┬───────┬───────┬───────┐
      │ Reg 0 │ Reg 1 │ Reg 2 │ Reg 3 │ Reg 4 │
      └───────┴───────┴───────┴───────┴───────┘

Para leer Registro 3: seek(3 * 100 = 300), read(100 bytes)

Ejemplo Python:
import struct

# Definir formato registro (id: int, nombre: 50 chars, salario: float)
formato = 'i50sf'  # int, 50 char string, float
tam_registro = struct.calcsize(formato)

# Leer registro específico
def leer_registro(fichero, num_registro):
    with open(fichero, 'rb') as f:
        f.seek(num_registro * tam_registro)
        datos = f.read(tam_registro)
        return struct.unpack(formato, datos)

# Escribir registro en posición
def escribir_registro(fichero, num_registro, id, nombre, salario):
    with open(fichero, 'r+b') as f:
        f.seek(num_registro * tam_registro)
        datos = struct.pack(formato, id, nombre.encode(), salario)
        f.write(datos)

Ventajas y Desventajas de Ficheros Directos

Ventajas Desventajas
Acceso rápido a cualquier registro O(1) Registros deben ser tamaño fijo (desperdicio de espacio)
Actualización in-place eficiente Fragmentación interna por padding
Lectura/escritura aleatorias eficientes No adecuado para registros de longitud variable
Ideal para índices y tablas hash Gestión de registros eliminados (marcas de borrado)

4.4.3. Ficheros Indexados

Combinan datos almacenados secuencialmente con índices separados que permiten acceso directo.

✅ Estructura de Fichero Indexado

  • Fichero de datos: Registros almacenados (pueden ser tamaño variable)
  • Fichero índice: Tabla de pares (clave, dirección) ordenada por clave
  • Búsqueda:
    1. Buscar clave en índice (búsqueda binaria O(log n) si ordenado)
    2. Obtener dirección/puntero del registro
    3. Acceder directamente al registro en fichero de datos
  • Múltiples índices: Posibles índices por diferentes campos
Estructura:

Fichero Índice (ordenado por ID):
┌────────┬──────────┐
│  ID    │ Dirección│
├────────┼──────────┤
│  101   │   0      │
│  205   │   150    │
│  310   │   300    │
│  415   │   480    │
└────────┴──────────┘

Fichero de Datos:
Byte:  0           150         300         480
      ┌───────────┬───────────┬───────────┬───────────┐
      │ Reg ID101 │ Reg ID205 │ Reg ID310 │ Reg ID415 │
      └───────────┴───────────┴───────────┴───────────┘

Buscar empleado ID=310:
1. Búsqueda binaria en índice → encuentra dirección 300
2. Seek a posición 300 en fichero datos
3. Leer registro

Tipos de Índices

  • Índice primario: Ordenado según clave primaria, fichero datos también ordenado por esa clave
  • Índice secundario: Sobre campo no clave, permite búsquedas por campos alternativos
  • Índice denso: Entrada en índice por cada registro
  • Índice disperso: Entradas solo para algunos registros (fichero datos debe estar ordenado)
  • Índice multinivel: Índice sobre índice, árbol B+

Ventajas y Desventajas de Ficheros Indexados

Ventajas Desventajas
Acceso rápido por clave mediante índice Overhead de mantener índices actualizados
Registros pueden ser tamaño variable Espacio adicional para almacenar índices
Múltiples índices para diferentes consultas Inserción/eliminación más lenta (actualizar índices)
Acceso secuencial también posible Complejidad de implementación mayor

4.5. Métodos de Acceso a Ficheros

4.5.1. Acceso Secuencial

Lectura de registros en orden, desde el principio hasta el final.

# Python - Acceso secuencial
with open('datos.txt', 'r') as f:
    for linea in f:
        procesar(linea)

# Java - Acceso secuencial
BufferedReader br = new BufferedReader(new FileReader("datos.txt"));
String linea;
while ((linea = br.readLine()) != null) {
    procesar(linea);
}
br.close();

Características:

  • Simple y eficiente para procesar todo el fichero
  • Aprovecha lectura anticipada del sistema operativo (read-ahead)
  • Óptimo para ficheros en medios secuenciales (cintas magnéticas históricamente)
  • No requiere seek, posición avanza automáticamente

4.5.2. Acceso Directo/Aleatorio

Lectura/escritura en cualquier posición del fichero mediante seek.

# Python - Acceso aleatorio
with open('datos.bin', 'r+b') as f:
    # Leer byte en posición 1000
    f.seek(1000)
    dato = f.read(4)
    
    # Escribir en posición 5000
    f.seek(5000)
    f.write(nuevo_dato)
    
    # Posicionamiento relativo
    f.seek(100, 1)  # Avanzar 100 bytes desde posición actual
    f.seek(-50, 2)  # Retroceder 50 bytes desde final

# Java - Acceso aleatorio
RandomAccessFile raf = new RandomAccessFile("datos.bin", "rw");
raf.seek(1000);
int dato = raf.readInt();
raf.seek(5000);
raf.writeInt(nuevoDato);
raf.close();

Operaciones seek:

  • seek(offset, whence): Posicionar puntero de fichero
    • whence=0: Desde inicio (absoluto)
    • whence=1: Desde posición actual (relativo)
    • whence=2: Desde final
  • tell(): Obtener posición actual del puntero

4.5.3. Acceso Indexado

Uso de índices para localizar rápidamente registros por clave.

# Pseudocódigo acceso indexado
def buscar_por_clave(indice, fichero_datos, clave_buscada):
    # 1. Búsqueda en índice
    entrada = indice.buscar(clave_buscada)  # Búsqueda binaria O(log n)
    
    if entrada:
        # 2. Acceso directo a datos
        fichero_datos.seek(entrada.direccion)
        registro = fichero_datos.read(entrada.tamaño)
        return registro
    else:
        return None

4.5.4. Acceso Hash

Uso de función hash para calcular posición directa del registro.

# Pseudocódigo acceso hash
def buscar_por_hash(fich
ero, clave):
    # Calcular posición mediante función hash
    posicion = hash(clave) % tamaño_fichero
    
    # Acceder directamente a esa posición
    fichero.seek(posicion * tamaño_registro)
    registro = fichero.read(tamaño_registro)
    
    if registro.clave == clave:
        return registro
    else:
        # Manejo de colisiones (linear probing, chaining, etc.)
        return resolver_colision(fichero, clave, posicion)

Ventajas del acceso hash:

  • Acceso en tiempo O(1) promedio
  • No requiere índices separados
  • Muy eficiente para grandes volúmenes de datos

Desventajas del acceso hash:

  • Colisiones requieren resolución
  • Espacio desperdiciado si factor de carga bajo
  • No mantiene orden de claves
  • Búsquedas por rango ineficientes

4.6. Operaciones sobre Ficheros

Operaciones Básicas

Operación Descripción Ejemplo (Python)
Abrir Establecer conexión con el fichero f = open('datos.txt', 'r')
Leer Obtener datos del fichero contenido = f.read()
Escribir Grabar datos en el fichero f.write('Hola mundo')
Cerrar Liberar recursos y asegurar escritura f.close()
Posicionar Mover puntero a posición específica f.seek(100)
Eliminar Borrar fichero del sistema os.remove('datos.txt')
Renombrar Cambiar nombre del fichero os.rename('viejo.txt', 'nuevo.txt')

Modos de Apertura de Ficheros

Modo Significado Descripción
'r' Read (Lectura) Solo lectura, fichero debe existir
'w' Write (Escritura) Escritura, crea nuevo o sobrescribe existente
'a' Append (Añadir) Escritura al final, crea si no existe
'r+' Read/Write Lectura y escritura, fichero debe existir
'w+' Write/Read Lectura y escritura, crea nuevo o sobrescribe
'a+' Append/Read Lectura y añadir al final
'rb', 'wb', 'ab' Modo binario Añadir 'b' para ficheros binarios

Gestión de Recursos: Context Managers

# Python - Uso recomendado con context manager (with)

# Cierra automáticamente el fichero incluso si hay excepción


with open('datos.txt', 'r') as f:

    contenido = f.read()

    procesar(contenido)
    
# Fichero cerrado automáticamente aquí

# Equivalente sin context manager (menos recomendado)
f = open('datos.txt', 'r')
try:
    contenido = f.read()
    procesar(contenido)
finally:
    f.close()  # Asegurar cierre incluso con excepción

5. Mapa Conceptual

Mapa Conceptual: Tipos de Datos, Estructuras de Datos y Ficheros

ORGANIZACIÓN Y ALMACENAMIENTO DE DATOS
1. TIPOS ELEMENTALES DE DATOS
1.1 Tipos Básicos
  • Entero: números sin decimales (int, long)
  • Real: coma flotante (float, double - IEEE 754)
  • Carácter: símbolos (char - ASCII/Unicode)
  • Booleano: valores lógicos (true/false)
  • Cadena: secuencias de caracteres (string)
1.2 Sistemas de Tipos
  • Tipado fuerte vs. débil
  • Tipado estático vs. dinámico
  • Representación en memoria: complemento a 2, IEEE 754
2. ESTRUCTURAS CONVENCIONALES
2.1 Arrays/Arreglos
  • Colección homogénea, tamaño fijo
  • Acceso directo O(1) por índice
  • Memoria contigua, cache-friendly
  • Multidimensionales: row-major, column-major
2.2 Registros/Structs
  • Colección heterogénea (diferentes tipos)
  • Acceso por nombre de campo
  • Modelado de entidades complejas
2.3 Enumeraciones
  • Conjunto de constantes nombradas
  • Seguridad de tipos, legibilidad
3. ESTRUCTURAS DINÁMICAS
3.1 Listas Enlazadas
  • Simple, doble, circular
  • Inserción/eliminación O(1) conociendo posición
  • Acceso secuencial O(n)
3.2 Pilas y Colas
  • Pila: LIFO (push, pop, peek) O(1)
  • Cola: FIFO (enqueue, dequeue, front) O(1)
  • Aplicaciones: call stack, BFS, buffers
3.3 Árboles
  • Jerárquicos: raíz, nodos, hojas
  • Árbol Binario de Búsqueda (BST)
  • Recorridos: preorden, inorden, postorden, por niveles
  • Operaciones O(log n) promedio
3.4 Grafos
  • Vértices y aristas
  • Representación: matriz adyacencia, lista adyacencia
  • Algoritmos: DFS, BFS, Dijkstra
3.5 Tablas Hash
  • Función hash: clave → índice
  • Acceso O(1) promedio
  • Colisiones: chaining, open addressing
4. FICHEROS
4.1 Tipos de Ficheros
  • Texto: legibles, ASCII/UTF-8, mayor tamaño
  • Binarios: compactos, eficientes, específicos
4.2 Organización
  • Secuencial: registros consecutivos, acceso secuencial
  • Directo: registros tamaño fijo, acceso aleatorio O(1)
  • Indexado: índice + datos, búsqueda O(log n)
4.3 Métodos de Acceso
  • Secuencial: inicio a fin
  • Aleatorio: seek a posición específica
  • Indexado: búsqueda en índice + acceso directo
  • Hash: función hash para calcular posición

Leyenda del Mapa Conceptual:

Nivel 1: Concepto principal Nivel 2: Categorías principales Nivel 3: Componentes y detalles

6. Preguntas de Evaluación

Pregunta 1

¿Cuál es la principal ventaja de los arrays sobre las listas enlazadas?

A) Inserción y eliminación más rápidas

B) Acceso directo a elementos mediante índice en tiempo O(1)

C) Tamaño dinámico sin limitaciones

D) Menor uso de memoria

✓ Respuesta correcta: B) Acceso directo a elementos mediante índice en tiempo O(1)

Explicación: La principal ventaja de los arrays es el acceso directo (aleatorio) a cualquier elemento mediante su índice en tiempo constante O(1). Dado que los elementos están almacenados contiguamente en memoria, se puede calcular la dirección de cualquier elemento simplemente como: dirección_base + (índice × tamaño_elemento). Las listas enlazadas requieren recorrido secuencial O(n) para acceder a un elemento. Las opciones A y C son incorrectas porque las listas enlazadas tienen ventajas en inserción/eliminación y tamaño dinámico. La opción D es parcialmente incorrecta: aunque los arrays no tienen overhead de punteros, ambas estructuras pueden desperdiciar memoria de formas diferentes.

Pregunta 2

¿Qué estructura de datos sigue el principio LIFO (Last In, First Out)?

A) Cola

B) Pila

C) Lista enlazada

D) Árbol binario

✓ Respuesta correcta: B) Pila

Explicación: La pila (stack) es la estructura de datos que implementa el principio LIFO (Last In, First Out - Último en Entrar, Primero en Salir), donde el último elemento añadido es el primero en ser extraído. Las operaciones fundamentales son push (apilar elemento en el tope) y pop (desapilar elemento del tope). La cola (opción A) sigue FIFO (First In, First Out). Las listas enlazadas (opción C) y árboles binarios (opción D) no siguen inherentemente ninguno de estos principios, aunque pueden implementarlos. Aplicaciones comunes de pilas incluyen: gestión de llamadas a funciones (call stack), evaluación de expresiones, navegación hacia atrás en navegadores, y operaciones deshacer/rehacer.

Pregunta 3

¿Cuál es la complejidad temporal promedio de búsqueda en un árbol binario de búsqueda (BST) balanceado?

A) O(1)

B) O(log n)

C) O(n)

D) O(n log n)

✓ Respuesta correcta: B) O(log n)

Explicación: En un árbol binario de búsqueda (BST) balanceado, la búsqueda tiene complejidad O(log n) porque en cada paso se descarta aproximadamente la mitad de los elementos restantes (similar a búsqueda binaria). La altura del árbol balanceado es log n, y la búsqueda desciende nivel por nivel. O(1) (opción A) sería tabla hash. O(n) (opción C) es el peor caso de BST degenerado (similar a lista enlazada) o búsqueda secuencial. O(n log n) (opción D) es típico de algoritmos de ordenación como mergesort o heapsort. Es importante notar que "balanceado" es clave: árboles como AVL o Red-Black garantizan balance automático manteniendo complejidad logarítmica.

Pregunta 4

¿Qué método de resolución de colisiones en tablas hash almacena múltiples elementos con mismo hash en una lista enlazada?

A) Linear probing

B) Quadratic probing

C) Chaining (Encadenamiento)

D) Double hashing

✓ Respuesta correcta: C) Chaining (Encadenamiento)

Explicación: El encadenamiento (chaining) resuelve colisiones haciendo que cada celda de la tabla hash contenga una lista enlazada de todos los elementos que tienen el mismo valor hash. Cuando hay colisión, simplemente se añade el nuevo elemento a la lista correspondiente. Ventajas: simple, permite factor de carga >1, eliminación sencilla. Desventajas: memoria extra para punteros, cache-unfriendly. Linear probing, quadratic probing y double hashing (opciones A, B, D) son técnicas de direccionamiento abierto (open addressing) donde se busca otra posición libre en la propia tabla cuando hay colisión, sin usar estructuras auxiliares.

Pregunta 5

¿Cuál es la característica principal de un fichero de organización secuencial?

A) Permite acceso directo a cualquier registro en tiempo O(1)

B) Los registros se almacenan consecutivamente y se acceden en orden

C) Utiliza índices para búsqueda rápida

D) Los registros tienen direcciones calculadas mediante función hash

✓ Respuesta correcta: B) Los registros se almacenan consecutivamente y se acceden en orden

Explicación: Los ficheros de organización secuencial almacenan registros uno tras otro en orden de entrada, y el acceso es típicamente secuencial desde el principio. Para encontrar un registro específico, hay que recorrer secuencialmente desde el inicio hasta encontrarlo. Ventajas: simple, eficiente para procesamiento completo del fichero, ideal para logs y backups. Desventajas: búsqueda lenta O(n), actualización ineficiente. La opción A describe ficheros directos. La opción C describe ficheros indexados. La opción D describe acceso hash. El acceso secuencial es apropiado cuando se necesita procesar todos los registros (procesamiento batch) o cuando los datos tienen acceso secuencial natural (cronológico).

Pregunta 6

¿Qué tipo de dato se utiliza típicamente para representar valores lógicos verdadero/falso?

A) Integer

B) Float

C) Boolean

D) Char

✓ Respuesta correcta: C) Boolean

Explicación: El tipo de dato booleano (boolean) está específicamente diseñado para representar valores lógicos binarios: verdadero (true) o falso (false). Teóricamente requiere solo 1 bit, aunque típicamente se almacena en 1 byte por razones de alineación de memoria. Soporta operaciones lógicas: AND, OR, NOT, XOR. Es fundamental en estructuras de control (if, while) y expresiones condicionales. Algunos lenguajes (C) no tienen tipo boolean nativo y usan enteros (0=falso, no-cero=verdadero), pero lenguajes modernos (Java, Python, C#) tienen tipo boolean explícito para mayor claridad y seguridad de tipos.

Pregunta 7

¿Cuál es la ventaja principal de una lista doblemente enlazada sobre una lista simplemente enlazada?

A) Usa menos memoria

B) Permite recorrido bidireccional

C) Inserción más rápida

D) Acceso directo por índice

✓ Respuesta correcta: B) Permite recorrido bidireccional

Explicación: La lista doblemente enlazada tiene cada nodo con punteros tanto al siguiente como al anterior nodo, permitiendo recorrido en ambas direcciones. Esto facilita: eliminación de un nodo conociendo solo su referencia (sin necesitar el anterior), navegación hacia atrás, implementación de estructuras como deques. La opción A es incorrecta: usa MÁS memoria (dos punteros por nodo vs. uno). La opción C es incorrecta: inserción tiene misma complejidad O(1) si se conoce posición. La opción D es incorrecta: ambos tipos de listas requieren recorrido secuencial, no acceso directo. El trade-off es memoria adicional por mayor flexibilidad de operaciones.

Pregunta 8

¿Qué recorrido de árbol binario visita los nodos en orden ascendente en un BST?

A) Preorden

B) Inorden

C) Postorden

D) Por niveles

✓ Respuesta correcta: B) Inorden

Explicación: El recorrido inorden (in-order traversal) visita nodos en secuencia: subárbol izquierdo → raíz → subárbol derecho. En un árbol binario de búsqueda (BST), esta secuencia produce valores en orden ascendente debido a la propiedad de ordenación del BST (izquierda < raíz < derecha). Ejemplo: BST con valores {3, 5, 7, 10, 12, 15, 20}, recorrido inorden produce exactamente esa secuencia ordenada. Preorden (opción A) visita raíz primero, usado para copiar árbol. Postorden (opción C) visita raíz al final, usado para eliminar árbol. Por niveles (opción D) usa BFS, no garantiza orden ascendente.

Pregunta 9

¿Qué estructura de representación de grafos es más eficiente en espacio para grafos sparse (dispersos)?

A) Matriz de adyacencia

B) Lista de adyacencia

C) Matriz de incidencia

D) Todas usan el mismo espacio

✓ Respuesta correcta: B) Lista de adyacencia

Explicación: La lista de adyacencia usa O(V+E) espacio (V=vértices, E=aristas), almacenando solo las aristas existentes. En grafos sparse donde E << V², esto es mucho más eficiente que matriz de adyacencia que siempre usa O(V²) independientemente del número de aristas. Ejemplo: grafo de 1000 vértices con 5000 aristas (sparse): lista de adyacencia ≈6000 entradas, matriz de adyacencia = 1,000,000 celdas. La lista de adyacencia también facilita recorrer vecinos de un vértice. La matriz de adyacencia (opción A) es mejor para grafos densos o cuando necesitamos verificación rápida O(1) de existencia de arista específica.

Pregunta 10

¿Cuál es la diferencia principal entre un fichero de texto y un fichero binario?

A) Los ficheros de texto son más grandes

B) Los ficheros binarios no pueden almacenar texto

C) Los ficheros de texto son legibles por humanos, los binarios contienen datos en formato interno de máquina

D) Los ficheros binarios son más seguros

✓ Respuesta correcta: C) Los ficheros de texto son legibles por humanos, los binarios contienen datos en formato interno de máquina

Explicación: Los ficheros de texto contienen secuencias de caracteres legibles (ASCII, UTF-8) que pueden abrirse con editores de texto comunes. Los ficheros binarios contienen bytes que representan datos en formato interno de la máquina (enteros, flotantes en su representación binaria directa), no legibles en editores de texto. La opción A es parcialmente cierta pero no la diferencia "principal": ficheros de texto suelen ser más grandes porque números se almacenan como caracteres (ej: 12345 = 5 bytes en texto vs. 4 bytes como int binario), pero esta es consecuencia, no la diferencia fundamental. La opción B es incorrecta: binarios pueden contener texto. La opción D no es característica distintiva: seguridad depende de permisos y cifrado, no del tipo de fichero.

Pregunta 11

¿Qué método de acceso a ficheros permite leer/escribir en cualquier posición mediante la operación seek?

A) Acceso secuencial

B) Acceso directo o aleatorio

C) Acceso indexado

D) Acceso hash

✓ Respuesta correcta: B) Acceso directo o aleatorio

Explicación: El acceso directo (random access) utiliza la operación seek para posicionar el puntero del fichero en cualquier byte específico, permitiendo lectura/escritura en cualquier ubicación sin recorrer registros previos. Ejemplo: seek(1000) posiciona en byte 1000, luego read(100) lee 100 bytes desde esa posición. Es fundamental para ficheros con registros de tamaño fijo donde posición = número_registro × tamaño_registro. El acceso secuencial (opción A) lee desde inicio sin seek. El acceso indexado (opción C) usa seek pero tras buscar en índice primero. El acceso hash (opción D) calcula posición con función hash antes de usar seek. Todos excepto secuencial pueden usar seek, pero "acceso directo/aleatorio" es el término específico para esta capacidad.

Pregunta 12

¿Cuál de las siguientes es una operación válida en una cola (queue)?

A) push y pop

B) enqueue y dequeue

C) insert y delete

D) add y remove del medio

✓ Respuesta correcta: B) enqueue y dequeue

Explicación: Las operaciones estándar de una cola (queue) son enqueue (añadir elemento al final) y dequeue (eliminar elemento del frente), implementando el principio FIFO. También típicamente front() para ver elemento del frente sin eliminarlo, e isEmpty() para verificar si está vacía. Push y pop (opción A) son operaciones de pila (stack). Insert y delete (opción C) son genéricas. Add y remove del medio (opción D) violaría el principio FIFO de la cola. Las colas se usan en: gestión de procesos CPU (scheduling), buffers de impresión, BFS en grafos, sistemas de mensajería, y cualquier escenario donde orden de llegada debe preservarse (primero en llegar, primero en ser atendido).

Pregunta 13

¿Qué ventaja tiene un fichero indexado sobre un fichero secuencial?

A) Ocupa menos espacio en disco

B) Es más simple de implementar

C) Permite búsqueda rápida por clave mediante el índice

D) No requiere mantenimiento

✓ Respuesta correcta: C) Permite búsqueda rápida por clave mediante el índice

Explicación: Los ficheros indexados mantienen uno o más índices (típicamente ordenados) que permiten búsqueda rápida O(log n) mediante búsqueda binaria en el índice, seguida de acceso directo al registro. Esto es mucho más eficiente que búsqueda secuencial O(n) en ficheros secuenciales. Además, pueden tener múltiples índices por diferentes campos. Las opciones A, B y D son incorrectas: ficheros indexados ocupan MÁS espacio (índices adicionales), son MÁS complejos de implementar, y requieren mantenimiento constante (actualizar índices en cada inserción/eliminación). El trade-off es: espacio y complejidad adicionales a cambio de búsquedas mucho más rápidas. Apropiados cuando consultas frecuentes por clave justifican el overhead.

Pregunta 14

¿Cuál es la representación estándar de números de coma flotante en la mayoría de los lenguajes de programación?

A) Complemento a dos

B) BCD (Binary Coded Decimal)

C) IEEE 754

D) ASCII

✓ Respuesta correcta: C) IEEE 754

Explicación: El estándar IEEE 754 define la representación de números de coma flotante (float, double) usado universalmente en hardware y software moderno. Especifica formato con signo (1 bit), exponente (8 bits float, 11 bits double), y mantisa (23 bits float, 52 bits double). Valor = (-1)^signo × 1.mantisa × 2^(exponente-sesgo). También define valores especiales: ±∞, NaN (Not a Number). Complemento a dos (opción A) es para enteros con signo. BCD (opción B) codifica dígitos decimales, usado en algunos sistemas financieros. ASCII (opción D) codifica caracteres. IEEE 754 permite aritmética de coma flotante eficiente en hardware, aunque tiene limitaciones de precisión inherentes (errores de redondeo).

Pregunta 15

¿Qué estructura de datos es más apropiada para implementar un sistema de deshacer/rehacer en un editor de texto?

A) Cola

B) Árbol binario

C) Dos pilas

D) Tabla hash

✓ Respuesta correcta: C) Dos pilas

Explicación: Un sistema de deshacer/rehacer se implementa típicamente con dos pilas: pila de deshacer y pila de rehacer. Cuando usuario realiza acción, se apila en pila de deshacer. Al deshacer (Ctrl+Z), se desapila de pila de deshacer y se apila en pila de rehacer. Al rehacer (Ctrl+Y), se desapila de pila de rehacer y se apila en pila de deshacer. El comportamiento LIFO de las pilas es perfecto: la acción más reciente es la primera en deshacerse. Una cola (opción A) sería FIFO, incorrecto. Árbol binario (opción B) es complejo innecesario. Tabla hash (opción D) no mantiene orden temporal. Algunas implementaciones sofisticadas usan árbol de estados para ramificación, pero dos pilas es la solución estándar y eficiente.

Pregunta 16

¿Qué representa el "grado" de un nodo en un árbol?

A) Su distancia a la raíz

B) El número de hijos que tiene

C) Su posición en el recorrido inorden

D) El número total de nodos en su subárbol

✓ Respuesta correcta: B) El número de hijos que tiene

Explicación: El grado de un nodo es el número de hijos (descendientes directos) que tiene ese nodo. En árbol binario, el grado máximo es 2. El grado del árbol completo es el grado máximo entre todos sus nodos. Una hoja tiene grado 0 (sin hijos). La distancia a la raíz (opción A) se llama "nivel" o "profundidad" del nodo. La posición en recorrido inorden (opción C) no tiene nombre especial establecido. El número total de nodos en subárbol (opción D) se llama "tamaño del subárbol". El grado es importante para clasificar árboles: árbol n-ario permite grado hasta n, árbol binario grado máximo 2, etc.

Pregunta 17

¿Cuál es la principal diferencia entre tipado estático y tipado dinámico?

A) Tipado estático es más lento

B) Tipado dinámico determina tipos en compilación

C) Tipado estático determina tipos en compilación, dinámico en ejecución

D) No hay diferencia práctica

✓ Respuesta correcta: C) Tipado estático determina tipos en compilación, dinámico en ejecución

Explicación: Tipado estático (Java, C++, C#): tipos de variables determinados y verificados en tiempo de compilación. Ventajas: detección temprana de errores, optimización, documentación implícita, mejor soporte IDE. Desventajas: más verboso, menos flexible. Tipado dinámico (Python, JavaScript, Ruby): tipos determinados en tiempo de ejecución. Ventajas: código más conciso, mayor flexibilidad, desarrollo rápido. Desventajas: errores de tipo solo en ejecución, menor rendimiento. La opción A es incorrecta: el rendimiento depende de múltiples factores, aunque lenguajes dinámicos suelen ser más lentos. La opción B invierte la definición. La opción D ignora diferencias significativas en desarrollo, debugging y rendimiento.

Pregunta 18

¿Qué modo de apertura en Python permite leer y escribir en un fichero existente sin sobrescribirlo completamente?

A) 'w'

B) 'r+'

C) 'a'

D) 'x'

✓ Respuesta correcta: B) 'r+'

Explicación: El modo 'r+' abre fichero existente para lectura Y escritura sin truncarlo (mantiene contenido existente), permitiendo modificaciones in-place usando seek. El fichero debe existir. Modo 'w' (opción A) abre para escritura pero SOBRESCRIBE/trunca contenido existente. Modo 'a' (opción C) abre para añadir al final, pero escrituras siempre van al final independientemente de seek. Modo 'x' (opción D) crea fichero nuevo exclusivamente, falla si ya existe. Para actualizar registro específico en fichero: open('datos.bin', 'r+b'), seek(posicion), write(nuevo_dato). También existe 'w+' que crea nuevo/sobrescribe pero permite lectura, y 'a+' que añade y permite lectura.

Pregunta 19

¿Cuál es la complejidad temporal de inserción al inicio de una lista enlazada?

A) O(1)

B) O(log n)

C) O(n)

D) O(n²)

✓ Respuesta correcta: A) O(1)

Explicación: Insertar al inicio de lista enlazada es O(1) porque solo requiere: crear nuevo nodo, hacer que su puntero siguiente apunte a la actual cabeza, y actualizar cabeza para apuntar al nuevo nodo. No requiere recorrer la lista ni mover elementos existentes. Pseudocódigo: nuevo.siguiente = cabeza; cabeza = nuevo. Esta es una ventaja clave de listas enlazadas sobre arrays, donde inserción al inicio requiere desplazar todos los elementos O(n). Sin embargo, inserción en posición arbitraria en lista enlazada es O(n) porque primero hay que encontrar esa posición mediante recorrido secuencial. Arrays tienen ventaja en acceso aleatorio O(1) pero desventaja en inserción O(n).

Pregunta 20

¿Qué problema fundamental intentan resolver los ficheros indexados?

A) Reducir el tamaño del fichero

B) Acelerar la búsqueda de registros específicos

C) Permitir registros de diferentes tipos

D) Mejorar la seguridad de los datos

✓ Respuesta correcta: B) Acelerar la búsqueda de registros específicos

Explicación: Los ficheros indexados resuelven el problema de búsqueda lenta en ficheros secuenciales. Manteniendo índice ordenado separado (clave → dirección), la búsqueda se reduce de O(n) secuencial a O(log n) mediante búsqueda binaria en índice, seguida de acceso directo al registro. Esto es crucial para bases de datos y aplicaciones con consultas frecuentes por clave. La opción A es incorrecta: índices AUMENTAN tamaño total. La opción C es incorrecta: tipo de registros no está relacionado con indexación. La opción D es incorrecta: seguridad se maneja con permisos/cifrado. El trade-off de indexación es: espacio y complejidad adicionales (mantener índices) a cambio de búsquedas mucho más rápidas. Múltiples índices permiten búsquedas eficientes por diferentes campos.

Pregunta 21

¿Qué representación de grafo es más eficiente para verificar rápidamente si existe una arista entre dos vértices específicos?

A) Lista de adyacencia

B) Matriz de adyacencia

C) Lista de aristas

D) Todas son igual de eficientes

✓ Respuesta correcta: B) Matriz de adyacencia

Explicación: La matriz de adyacencia permite verificación de arista en O(1): simplemente acceder matriz[i][j] y ver si es 1/true (existe arista de i a j). Con lista de adyacencia (opción A), hay que recorrer la lista de vecinos de i buscando j, que es O(grado del vértice), pudiendo ser O(V) en peor caso. Con lista de aristas (opción C), hay que buscar el par (i,j) en la lista, O(E) en peor caso. Sin embargo, matriz de adyacencia tiene desventaja de usar siempre O(V²) espacio, mientras lista de adyacencia usa O(V+E), más eficiente para grafos sparse. La elección depende del patrón de operaciones: matriz mejor para grafos densos con muchas consultas de existencia de arista.

Pregunta 22

¿Cuál es la principal ventaja de usar Unicode UTF-8 sobre ASCII para codificar texto?

A) Es más rápido de procesar

B) Soporta caracteres de múltiples idiomas y símbolos especiales

C) Usa menos memoria

D) Es más antiguo y probado

✓ Respuesta correcta: B) Soporta caracteres de múltiples idiomas y símbolos especiales

Explicación: UTF-8 es codificación de Unicode que soporta más de 1 millón de caracteres, incluyendo todos los idiomas del mundo (chino, árabe, cirílico, etc.), símbolos matemáticos, emojis, caracteres históricos, etc. ASCII solo cubre 128 caracteres (7 bits): inglés básico, números, signos de puntuación. UTF-8 es retrocompatible con ASCII: caracteres ASCII ocupan 1 byte idéntico en UTF-8. Caracteres no-ASCII ocupan 2-4 bytes en UTF-8. La opción A es incorrecta: procesamiento puede ser más complejo por longitud variable. La opción C es incorrecta: UTF-8 usa misma o más memoria que ASCII. La opción D es incorrecta: ASCII es más antiguo (1963) que UTF-8 (1993). UTF-8 es estándar moderno obligatorio para aplicaciones globales e internacionalización.

Pregunta 23

¿Qué estructura de datos es más apropiada para implementar un algoritmo de búsqueda en anchura (BFS) en un grafo?

A) Pila

B) Cola

C) Árbol binario

D) Tabla hash

✓ Respuesta correcta: B) Cola

Explicación: BFS (Breadth-First Search) utiliza una cola para explorar el grafo nivel por nivel: visitar nodo, encolar todos sus vecinos no visitados, dequeue siguiente nodo y repetir. El comportamiento FIFO de la cola garantiza que nodos más cercanos (mismo nivel) se procesen antes que nodos más lejanos. Pseudocódigo: encolar raíz, mientras cola no vacía: dequeue nodo, procesar, encolar vecinos no visitados. Una pila (opción A) implementaría DFS (Depth-First Search), que explora en profundidad. Árbol binario (opción C) y tabla hash (opción D) no son estructuras auxiliares para BFS. BFS se usa para: camino más corto en grafos no ponderados, recorrido por niveles de árbol, encontrar componentes conexas.

Pregunta 24

¿Cuál es la diferencia principal entre un registro (struct) y un array?

A) Los registros son dinámicos, los arrays estáticos

B) Los registros agrupan datos heterogéneos, los arrays datos homogéneos

C) Los arrays son más rápidos

D) No hay diferencia real

✓ Respuesta correcta: B) Los registros agrupan datos heterogéneos, los arrays datos homogéneos

Explicación: La diferencia fundamental es: registros (structs) agrupan campos de DIFERENTES tipos bajo un nombre (heterogéneo), accedidos por nombre de campo. Arrays agrupan elementos del MISMO tipo (homogéneo), accedidos por índice numérico. Ejemplo registro: struct Empleado {int id; string nombre; float salario;} - tres tipos diferentes. Ejemplo array: int numeros[100] - todos enteros. Registros modelan entidades con múltiples atributos. Arrays modelan colecciones de elementos similares. La opción A es incorrecta: ambos pueden ser estáticos o dinámicos según lenguaje. La opción C es incorrecta: rendimiento depende del uso específico. Los registros son fundamentales para programación estructurada, permitiendo crear tipos de datos complejos que representan conceptos del dominio del problema.

Pregunta 25

¿Qué ventaja proporciona el uso de context managers (with statement en Python) al trabajar con ficheros?

A) Hace el código más rápido

B) Cierra automáticamente el fichero incluso si hay excepciones

C) Permite acceso concurrente

D) Elimina la necesidad de permisos

✓ Respuesta correcta: B) Cierra automáticamente el fichero incluso si hay excepciones

Explicación: El context manager (with statement) garantiza que el fichero se cierre correctamente incluso si ocurre una excepción durante su uso, implementando patrón RAII (Resource Acquisition Is Initialization). Código: with open('datos.txt', 'r') as f: contenido = f.read(). Al salir del bloque with (normalmente o por excepción), el fichero se cierra automáticamente. Sin context manager, hay que usar try/finally para garantizar cierre, más verboso y propenso a errores. La opción A es incorrecta: no hay beneficio de rendimiento significativo. La opción C es incorrecta: concurrencia requiere locks/semáforos. La opción D es incorrecta: permisos son independientes. Context managers previenen memory leaks, corrupción de datos, y agotamiento de file descriptors.

7. Referencias Bibliográficas

Libros Clásicos

  • Cormen, T.H., Leiserson, C.E., Rivest, R.L., & Stein, C. (2009). Introduction to Algorithms, 3rd Edition. MIT Press
  • Weiss, M.A. (2011). Data Structures and Algorithm Analysis in C++, 4th Edition. Pearson
  • Goodrich, M.T., Tamassia, R., & Goldwasser, M.H. (2013). Data Structures and Algorithms in Python. Wiley
  • Sedgewick, R., & Wayne, K. (2011). Algorithms, 4th Edition. Addison-Wesley
  • Knuth, D.E. (1997). The Art of Computer Programming, Volume 1: Fundamental Algorithms, 3rd Edition. Addison-Wesley

Libros en Español

  • Joyanes Aguilar, L. & Zahonero Martínez, I. (2010). Estructuras de Datos en Java. McGraw-Hill
  • Cairó, O. (2005). Estructuras de Datos, 3ª Edición. McGraw-Hill
  • Deitel, P. & Deitel, H. (2016). Java: Cómo Programar, 10ª Edición. Pearson

Recursos Online

  • VisuAlgo: https://visualgo.net - Visualización interactiva de estructuras de datos y algoritmos
  • GeeksforGeeks: https://www.geeksforgeeks.org/data-structures/ - Tutoriales exhaustivos con ejemplos
  • Coursera - Algorithms Specialization: Universidad de Stanford
  • MIT OpenCourseWare - Introduction to Algorithms: Curso completo gratuito

Documentación Oficial

  • Python Documentation: https://docs.python.org - Estructuras de datos integradas
  • Java Collections Framework: Documentación Oracle
  • C++ STL (Standard Template Library): cppreference.com

Resumen Final

Los tipos de datos, estructuras de datos y ficheros constituyen los fundamentos esenciales que todo profesional de informática debe dominar profundamente. Los tipos elementales (enteros, reales, caracteres, booleanos) proporcionan los bloques de construcción básicos, con representaciones en memoria específicas (complemento a dos para enteros, IEEE 754 para flotantes) que determinan sus rangos, precisión y operaciones posibles. Comprender estas representaciones es crucial para evitar errores sutiles de overflow, truncamiento y precisión que pueden tener consecuencias graves en sistemas críticos.

Las estructuras de datos —tanto convencionales como dinámicas— permiten organizar información de manera que optimice operaciones específicas. Los arrays proporcionan acceso directo O(1) ideal para datos con tamaño conocido y acceso frecuente por posición. Las listas enlazadas ofrecen flexibilidad de tamaño dinámico e inserción/eliminación eficiente a costa de acceso secuencial. Pilas y colas implementan disciplinas específicas de acceso (LIFO/FIFO) fundamentales en algoritmos y gestión de recursos. Los árboles proporcionan organización jerárquica con búsqueda logarítmica en BST balanceados. Los grafos modelan relaciones complejas en redes, sistemas de transporte, redes sociales. Las tablas hash permiten acceso casi constante mediante funciones hash bien diseñadas.

La elección de estructura de datos apropiada no es académica sino práctica y tiene impacto directo en rendimiento, escalabilidad y mantenibilidad del software. Un array para búsquedas frecuentes por índice, lista enlazada para inserciones/eliminaciones frecuentes, BST para datos ordenados con búsquedas, tabla hash para acceso por clave, grafo para relaciones complejas. El análisis de complejidad temporal (O-notation) guía estas decisiones, pero también factores como localidad de cache, overhead de memoria, y complejidad de implementación.

Los ficheros extienden capacidades de almacenamiento más allá de memoria RAM, proporcionando persistencia. La organización (secuencial, directa, indexada) y métodos de acceso (secuencial, aleatorio, indexado, hash) deben alinearse con patrones de uso: ficheros secuenciales para logs y procesamiento batch, ficheros directos para acceso aleatorio con registros fijos, ficheros indexados para búsquedas frecuentes por clave. Gestión correcta de ficheros (apertura, cierre, manejo de errores) es crítica para integridad de datos y prevención de corrupciones.

Para el opositor preparando acceso a cuerpos técnicos de administración pública, este conocimiento es instrumental. Diseñarás sistemas de gestión de datos, optimizarás aplicaciones existentes, implementarás algoritmos eficientes, y tomarás decisiones arquitectónicas fundamentadas. Cada estructura de datos que dominas, cada complejidad que calculas, cada organización de fichero que comprendes, es herramienta concreta para resolver problemas reales en sistemas de información de la administración. Este no es conocimiento abstracto de informática teórica sino ingeniería práctica aplicable inmediatamente en desarrollo de sistemas reales.

📅 Documento actualizado: Octubre 2025

🎯 Nivel: Técnico Superior - Oposiciones Administración Pública

📚 Área: Fundamentos de Programación y Estructuras de Datos

💪 Motivación para el Opositor

Los fundamentos de tipos de datos, estructuras de datos y ficheros son el alfabeto con el que se escribe todo el software moderno. Mientras que tecnologías específicas van y vienen (frameworks, librerías, lenguajes de moda), estos conceptos permanecen constantes y universales. Dominar estructuras de datos te convierte en programador competente capaz de elegir la herramienta correcta para cada problema, no solo el martillo cuando todo parece clavo. Comprender complejidad algorítmica te permite predecir y optimizar rendimiento antes de escribir código. Conocer organización de ficheros te capacita para diseñar sistemas de persistencia eficientes. Este conocimiento trasciende lenguajes: un árbol binario de búsqueda funciona igual en Python, Java, C++ o cualquier lenguaje futuro. Las entrevistas técnicas en administración pública y sector privado evalúan precisamente estos fundamentos porque revelan pensamiento computacional profundo vs. conocimiento superficial de sintaxis. Invierte tiempo dominando estos conceptos: son inversión que se amortiza toda tu carrera profesional. Cada estructura que implementes desde cero, cada complejidad que analices, cada fichero que organices eficientemente, fortalece tu capacidad de diseñar software robusto, escalable y mantenible. Estás construyendo fundamentos sólidos sobre los que se edificará toda tu expertise técnica futura. ¡Adelante con determinación y rigor!

Deja una respuesta

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