Revisando conceptos fundamentales de C con ejercicios prácticos. El entorno de desarrollo es Windows 7 con Visual Studio 2010.
Ejercicio 1: Inversión de una Lista Enlazada Implementar una función que invierta el orden de los nodos en una lista enlazada.
#include <stdio.h>
#include <stdlib.h>
struct Nodo {
int dato;
struct Nodo *siguiente;
};
void invertirLista(struct Nodo **cabeza) {
struct Nodo *anterior = NULL;
struct Nodo *actual = *cabeza;
struct Nodo *temporal = NULL;
while (actual != NULL) {
temporal = actual->siguiente;
actual->siguiente = anterior;
anterior = actual;
actual = temporal;
}
*cabeza = anterior;
}
void mostrarLista(struct Nodo **cabeza) {
struct Nodo *actual = *cabeza;
while (actual != NULL) {
printf("%d ", actual->dato);
actual = actual->siguiente;
}
printf("\n");
}
int main() {
struct Nodo *cabeza = NULL;
struct Nodo *nodo1 = NULL;
struct Nodo *nodo2 = NULL;
struct Nodo *nodo3 = NULL;
cabeza = (struct Nodo *)malloc(sizeof(struct Nodo));
nodo1 = (struct Nodo *)malloc(sizeof(struct Nodo));
nodo2 = (struct Nodo *)malloc(sizeof(struct Nodo));
nodo3 = (struct Nodo *)malloc(sizeof(struct Nodo));
cabeza->dato = 1;
cabeza->siguiente = nodo1;
nodo1->dato = 2;
nodo1->siguiente = nodo2;
nodo2->dato = 3;
nodo2->siguiente = nodo3;
nodo3->dato = 4;
nodo3->siguiente = NULL;
struct Nodo **referencia = &cabeza;
mostrarLista(referencia);
invertirLista(referencia);
mostrarLista(referencia);
free(cabeza);
free(nodo1);
free(nodo2);
free(nodo3);
return 0;
}
Al trabjaar con estructuras para listas enlazadas, es necesario asignar memoria dinámicamente. El uso de un puntero doble (como struct Nodo **cabeza) permite modificar el puntero original de la lista dentro de la función, ya que la cabeza puede cambiar durante la inversión. Si solo se pasara un puntero simple, se perdería la referencia a los nodos.
Ejercicio 2: Reemplazo de Espacios por Comas en Cadenas Dada una cadena de caracteres, reemplazar todos los espacios consecutivos por una sola coma.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void procesarCadena(const char *entrada, int longitud, char *salida) {
int indiceEntrada = 0, indiceSalida = 0;
while (indiceEntrada < longitud && entrada[indiceEntrada] == ' ') {
indiceEntrada++;
}
while (indiceEntrada < longitud) {
if (entrada[indiceEntrada] != ' ') {
salida[indiceSalida++] = entrada[indiceEntrada++];
} else if (indiceEntrada + 1 < longitud && entrada[indiceEntrada + 1] != ' ') {
salida[indiceSalida++] = ',';
indiceEntrada++;
} else {
indiceEntrada++;
}
}
salida[indiceSalida++] = ',';
salida[indiceSalida] = '\0';
}
int main() {
char *cadenaEntrada = (char *)malloc(1024 * sizeof(char));
char *cadenaSalida = (char *)malloc(1024 * sizeof(char));
if (fgets(cadenaEntrada, 1024, stdin) != NULL) {
int longitud = strlen(cadenaEntrada);
if (longitud > 0 && cadenaEntrada[longitud - 1] == '\n') {
cadenaEntrada[longitud - 1] = '\0';
longitud--;
}
procesarCadena(cadenaEntrada, longitud, cadenaSalida);
printf("%s\n", cadenaSalida);
}
free(cadenaEntrada);
free(cadenaSalida);
return 0;
}
Para leer cadanas con espacios, se puede usar fgets en lugar de scanf para evitar problemas de delimitación. La cadena de entrada debe terminar con el carácter nulo \0.
Ejercicio 3: Eliminación de Duplicados y Ordenamiento en Cadenas Dada una cadena, eliminar caracteres duplicados y ordenar los caracteres restantes.
#include <stdio.h>
#include <string.h>
void eliminarDuplicadosYOrdenar(char *cadena, int longitud) {
int unicos = 0;
for (int i = 0; i < longitud; i++) {
int esDuplicado = 0;
for (int j = 0; j < unicos; j++) {
if (cadena[i] == cadena[j]) {
esDuplicado = 1;
break;
}
}
if (!esDuplicado) {
cadena[unicos++] = cadena[i];
}
}
for (int i = 0; i < unicos - 1; i++) {
for (int j = i + 1; j < unicos; j++) {
if (cadena[i] > cadena[j]) {
char temp = cadena[i];
cadena[i] = cadena[j];
cadena[j] = temp;
}
}
}
cadena[unicos] = '\0';
}
int main() {
char buffer[1024];
while (fgets(buffer, 1024, stdin) != NULL) {
int longitud = strlen(buffer);
if (longitud > 0 && buffer[longitud - 1] == '\n') {
buffer[--longitud] = '\0';
}
eliminarDuplicadosYOrdenar(buffer, longitud);
printf("%s\n", buffer);
}
return 0;
}
El algoritmo utiliza dos pasos: primero elimina duplicados manteniendo el orden, luego ordana los caracteres. Se puede optimizar con métodos de ordenación más eficientes, pero este enfoque es sencillo para fines didácticos.
Ejercicio 4: Expresiones Aritméticas con Recursión Dado un resultado numérico, encontrar cuántas expresiones usando los dígitos 1 a 9, con operadores +, - o concatenación, producen ese resultado.
#include <stdio.h>
int contadorSoluciones = 0;
int objetivo;
char expresion[] = {'1',' ','2',' ','3',' ','4',' ','5',' ','6',' ','7',' ','8',' ','9','='};
void explorar(int nivel, char operadorAnterior, int valorAnterior, int sumaActual) {
if (nivel > 1) {
expresion[nivel * 2 - 3] = operadorAnterior;
}
int numeroActual = 0;
if (operadorAnterior == '+') {
numeroActual = nivel;
} else if (operadorAnterior == '-') {
numeroActual = -nivel;
} else if (operadorAnterior == ' ') {
if (valorAnterior >= 0) {
numeroActual = valorAnterior * 10 + nivel;
} else {
numeroActual = valorAnterior * 10 - nivel;
}
}
if (nivel == 9) {
if (sumaActual + numeroActual == objetivo) {
contadorSoluciones++;
printf("%s%d\n", expresion, objetivo);
}
return;
}
explorar(nivel + 1, '+', 0, sumaActual + numeroActual);
explorar(nivel + 1, '-', 0, sumaActual + numeroActual);
explorar(nivel + 1, ' ', numeroActual, sumaActual);
}
int main() {
while (scanf("%d", &objetivo) != EOF) {
contadorSoluciones = 0;
explorar(1, '+', 0, 0);
printf("Total: %d\n", contadorSoluciones);
while (getchar() != '\n');
}
return 0;
}
Este problema se resuelve mediante recursión, explorando todas las combinaciones posibles de operadores. La recursión gestiona los límites mediante casos base para el primer y último dígito. Enfoques similares son útiles para problemas de búsqueda exhaustiva con decisiones secuenciales.