From 94ff9315d7a78cfe9e6854ee4d0ce35787e66277 Mon Sep 17 00:00:00 2001 From: Rui Ribeiro <42305006+ruiribeiro04@users.noreply.github.com> Date: Thu, 25 Sep 2025 08:16:02 +0100 Subject: [PATCH] Replaced subject resolutions of level-2 with backtracking --- level-2/n_queens/n_queens.c | 141 ++++++++++++------- level-2/n_queens/n_queens.h | 13 -- level-2/n_queens/n_queens_clean.c | 93 +++++++++++++ level-2/permutations/permutation_clean.c | 94 +++++++++++++ level-2/permutations/permutations.c | 166 ++++++++++++++--------- level-2/permutations/permutations.h | 14 -- level-2/powerset/powerset.c | 111 +++++++++------ level-2/powerset/powerset.h | 12 -- level-2/powerset/powerset_clean.c | 68 ++++++++++ 9 files changed, 521 insertions(+), 191 deletions(-) delete mode 100644 level-2/n_queens/n_queens.h create mode 100644 level-2/n_queens/n_queens_clean.c create mode 100644 level-2/permutations/permutation_clean.c delete mode 100644 level-2/permutations/permutations.h delete mode 100644 level-2/powerset/powerset.h create mode 100644 level-2/powerset/powerset_clean.c diff --git a/level-2/n_queens/n_queens.c b/level-2/n_queens/n_queens.c index 45d7340..6412583 100644 --- a/level-2/n_queens/n_queens.c +++ b/level-2/n_queens/n_queens.c @@ -1,84 +1,131 @@ -#include "n_queens.h" +#include // write +#include // malloc, free, atoi +#include // fprintf, stderr (permitido para erros) -void print_number(int n) +// Função auxiliar para escrever um único caractere no stdout +void ft_putchar(char c) { - char c; - - if (n >= 10) - print_number(n / 10); - c = '0' + (n % 10); write(1, &c, 1); } -void print_solution(int *queens, int n) +// Função auxiliar para escrever um número inteiro no stdout +void ft_putnbr(int n) { - int i; - - i = 0; - while (i < n) + if (n >= 10) { - if (i > 0) - write(1, " ", 1); - print_number(queens[i]); - i++; + ft_putnbr(n / 10); } - write(1, "\n", 1); + ft_putchar((n % 10) + '0'); } -int is_safe(int *queens, int row, int col, int n) +// Imprime a solução no formato "p1 p2 p3 ..." +void print_solution(int *board, int n) { - int i; - - (void)n; - i = 0; - while (i < row) + int i = 0; + while (i < n) { - if (queens[i] == col) + ft_putnbr(board[i]); + if (i < n - 1) + ft_putchar(' '); + i++; + } + ft_putchar('\n'); +} + + +int ft_abs(int nbr) +{ + if (nbr < 0) + return (nbr * -1); + else + return (nbr); +} +// Verifica se é seguro colocar uma rainha na posição (row, col) +// Só precisamos verificar as colunas à esquerda da coluna atual +int is_safe(int *board, int n, int row, int col) +{ + int i; + + i = 0; + (void)n; + while (i < col) + { + // 1. Verifica se há outra rainha na mesma linha + if (board[i] == row) return (0); - if (queens[i] - i == col - row) - return (0); - if (queens[i] + i == col + row) + + // 2. Verifica as diagonais + // abs(board[i] - row) == abs(i - col) + // Como col > i, abs(i - col) é (col - i) + if (ft_abs(board[i] - row) == (col - i)) return (0); i++; } + // Se passou por todas as verificações, a posição é segura return (1); } -void solve_nqueens(int *queens, int row, int n) +// A função principal de backtracking +void solve(int *board, int n, int col) { - int col; - - if (row == n) + // Caso base: Se todas as rainhas foram colocadas (chegamos ao final do tabuleiro) + if (col == n) { - print_solution(queens, n); - return ; + print_solution(board, n); + return; } - col = 0; - while (col < n) + + // Tenta colocar uma rainha em cada linha da coluna atual + int row = 0; + while (row < n) { - if (is_safe(queens, row, col, n)) + // Verifica se a posição (row, col) é segura + if (is_safe(board, n, row, col)) { - queens[row] = col; - solve_nqueens(queens, row + 1, n); + // Coloca a rainha + board[col] = row; + + // Chama a função recursivamente para a próxima coluna + solve(board, n, col + 1); + + // O backtracking é implícito: na próxima iteração do loop, + // board[col] será sobrescrito com um novo valor de 'row'. + // Não precisamos de "remover" a rainha explicitamente. } - col++; + row++; } } -int main(int argc, char **argv) +int main(int argc, char **argv) { - int n; - int *queens; + int *board; + int n; if (argc != 2) + { + // Usar fprintf com stderr é permitido para mensagens de erro + fprintf(stderr, "Usage: %s n\n", argv[0]); return (1); + } + n = atoi(argv[1]); if (n <= 0) + { + return (0); // Não faz nada para n <= 0, como esperado + } + + // Aloca memória para o tabuleiro. board[col] = row + board = (int *)malloc(sizeof(int) * n); + if (!board) + { + fprintf(stderr, "Memory allocation failed\n"); return (1); - queens = (int *)malloc(sizeof(int) * n); - if (!queens) - return (1); - solve_nqueens(queens, 0, n); - free(queens); + } + + // Inicia o processo de backtracking a partir da primeira coluna (col = 0) + solve(board, n, 0); + + // Liberta a memória alocada + free(board); return (0); } \ No newline at end of file diff --git a/level-2/n_queens/n_queens.h b/level-2/n_queens/n_queens.h deleted file mode 100644 index 47d8053..0000000 --- a/level-2/n_queens/n_queens.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef N_QUEENS_H -# define N_QUEENS_H - -# include -# include -# include - -int is_safe(int *queens, int row, int col, int n); -void solve_nqueens(int *queens, int row, int n); -void print_solution(int *queens, int n); -void print_number(int n); - -#endif \ No newline at end of file diff --git a/level-2/n_queens/n_queens_clean.c b/level-2/n_queens/n_queens_clean.c new file mode 100644 index 0000000..2a8e608 --- /dev/null +++ b/level-2/n_queens/n_queens_clean.c @@ -0,0 +1,93 @@ +#include +#include +#include + +void ft_putchar(char c) +{ + write(1, &c, 1); +} + +void ft_putnbr(int n) +{ + if (n >= 10) + ft_putnbr(n / 10); + ft_putchar((n % 10) + '0'); +} + +void print_solution(int *board, int n) +{ + int i = 0; + while (i < n) + { + ft_putnbr(board[i]); + if (i < n - 1) + ft_putchar(' '); + i++; + } + ft_putchar('\n'); +} + +int ft_abs(int nbr) +{ + if (nbr < 0) + return (nbr * -1); + else + return (nbr); +} + +int is_safe(int *board, int n, int row, int col) +{ + int i; + + i = 0; + (void)n; + while (i < col) + { + if (board[i] == row) + return (0); + if (ft_abs(board[i] - row) == (col - i)) + return (0); + i++; + } + return (1); +} + +void solve(int *board, int n, int col) +{ + if (col == n) + return(print_solution(board, n)); + + int row = 0; + while (row < n) + { + if (is_safe(board, n, row, col)) + { + board[col] = row; + solve(board, n, col + 1); + } + row++; + } +} + +int main(int argc, char **argv) +{ + int *board; + int n; + + if (argc != 2) + return (fprintf(stderr, "Usage: %s n\n", argv[0]), 1); + + n = atoi(argv[1]); + if (n <= 0) + return (0); + + board = (int *)malloc(sizeof(int) * n); + if (!board) + { + fprintf(stderr, "Memory allocation failed\n"); + return (1); + } + solve(board, n, 0); + free(board); + return (0); +} \ No newline at end of file diff --git a/level-2/permutations/permutation_clean.c b/level-2/permutations/permutation_clean.c new file mode 100644 index 0000000..204383a --- /dev/null +++ b/level-2/permutations/permutation_clean.c @@ -0,0 +1,94 @@ +#include // write +#include // malloc, free + +int ft_strlen(const char *s) +{ + int len = 0; + while (s[len]) + len++; + return (len); +} + +void print_solution(const char *s) +{ + write(1, s, ft_strlen(s)); + write(1, "\n", 1); +} + +void ft_swap(char *a, char *b) +{ + char temp = *a; + *a = *b; + *b = temp; +} + +void sort_string(char *str, int n) +{ + int i; + int j; + + i = 0; + while (i < n - 1) + { + j = 0; + while (j < n - i - 1) + { + if (str[j] > str[j + 1]) + ft_swap(&str[j], &str[j + 1]); + j++; + } + i++; + } +} + +void generate_permutations(char *original_str, char *current_perm, int *used, int n, int k) +{ + if (k == n) + { + print_solution(current_perm); + return; + } + + int i = 0; + while (i < n) + { + if (used[i] == 0) + { + current_perm[k] = original_str[i]; + used[i] = 1; + generate_permutations(original_str, current_perm, used, n, k + 1); + used[i] = 0; + } + i++; + } +} + +int main(int argc, char **argv) +{ + if (argc != 2 || !argv[1][0]) + return (0); + + int n = ft_strlen(argv[1]); + char *original_str = argv[1]; + + sort_string(original_str, n); + + char *current_perm = (char *)malloc(sizeof(char) * (n + 1)); + if (!current_perm) + return (1); + current_perm[n] = '\0'; + + int *used = (int *)calloc(sizeof(int) * n); + if (!used) + { + free(current_perm); + return (1); + } + + generate_permutations(original_str, current_perm, used, n, 0); + + free(current_perm); + free(used); + + return (0); +} \ No newline at end of file diff --git a/level-2/permutations/permutations.c b/level-2/permutations/permutations.c index 7903081..73d8236 100644 --- a/level-2/permutations/permutations.c +++ b/level-2/permutations/permutations.c @@ -1,98 +1,132 @@ -#include "permutations.h" +#include // write +#include // malloc, free -int ft_strlen(char *str) +// Função auxiliar para obter o comprimento de uma string +int ft_strlen(const char *s) { - int len; - - len = 0; - while (str[len]) + int len = 0; + while (s[len]) len++; return (len); } -void ft_swap(char *a, char *b) +// Função auxiliar para imprimir uma string seguida de uma nova linha +void print_solution(const char *s) { - char temp; + write(1, s, ft_strlen(s)); + write(1, "\n", 1); +} - temp = *a; +// Função auxiliar para trocar dois caracteres (usada na ordenação) +void ft_swap(char *a, char *b) +{ + char temp = *a; *a = *b; *b = temp; } -void ft_sort_string(char *str) +// Função para ordenar a string de entrada (Bubble Sort simples) +// Isso é crucial para garantir que as permutações sejam geradas em ordem alfabética. +void sort_string(char *str, int n) { - int i; - int j; - int len; + int i; + int j; - len = ft_strlen(str); i = 0; - while (i < len - 1) + while (i < n - 1) { - j = i + 1; - while (j < len) + j = 0; + while (j < n - i - 1) { - if (str[i] > str[j]) - ft_swap(&str[i], &str[j]); + if (str[j] > str[j + 1]) + ft_swap(&str[j], &str[j + 1]); j++; } i++; } } -void ft_reverse(char *str, int start, int end) +// A função principal de backtracking, análoga à sua função `solve` +// Parâmetros: +// - original_str: A string original, já ordenada +// - current_perm: A permutação que estamos construindo (o "tabuleiro") +// - used: Um array que marca quais caracteres da string original já foram usados +// - n: O tamanho da string +// - k: A posição atual que estamos preenchendo em `current_perm` (análogo a `col`) +void generate_permutations(char *original_str, char *current_perm, int *used, int n, int k) { - while (start < end) + // Caso base: Se todas as posições foram preenchidas (k == n), + // análogo a `if (col == n)` no N-Queens. + if (k == n) { - ft_swap(&str[start], &str[end]); - start++; - end--; + print_solution(current_perm); + return; } -} -int next_permutation(char *str, int len) -{ - int i; - int j; - - i = len - 2; - while (i >= 0 && str[i] >= str[i + 1]) - i--; - if (i < 0) - return (0); - j = len - 1; - while (str[j] <= str[i]) - j--; - ft_swap(&str[i], &str[j]); - ft_reverse(str, i + 1, len - 1); - return (1); -} - -int main(int argc, char **argv) -{ - char *str; - char *copy; - int len; - int i; - - if (argc != 2) - return (1); - str = argv[1]; - len = ft_strlen(str); - copy = malloc(sizeof(char) * (len + 1)); - if (!copy) - return (1); - i = 0; - while (i < len) + // Tenta colocar cada caractere da string original na posição 'k' atual + int i = 0; + while (i < n) { - copy[i] = str[i]; + // Verifica se a "jogada" é válida, análogo ao `is_safe`. + // A condição é: o i-ésimo caractere da string original ainda não foi usado? + if (used[i] == 0) + { + // "Coloca a peça no tabuleiro": + // 1. Coloca o caractere na permutação atual + current_perm[k] = original_str[i]; + // 2. Marca este caractere como usado + used[i] = 1; + + // Chama a função recursivamente para a próxima posição (k + 1), + // análogo a `solve(board, n, col + 1)` + generate_permutations(original_str, current_perm, used, n, k + 1); + + // Backtracking: Desfaz a jogada para que este caractere possa ser + // usado em uma posição diferente na próxima ramificação da recursão. + // Isso é crucial e um pouco mais explícito que no seu N-Queens. + used[i] = 0; + } i++; } - copy[len] = '\0'; - ft_sort_string(copy); - puts(copy); - while (next_permutation(copy, len)) - puts(copy); - free(copy); +} + +int main(int argc, char **argv) +{ + if (argc != 2 || !argv[1][0]) + { + // Não faz nada se não houver exatamente um argumento ou se for uma string vazia. + return (0); + } + + int n = ft_strlen(argv[1]); + char *original_str = argv[1]; // Não precisamos de cópia, podemos ordenar o argumento + + // 1. Ordena a string de entrada para garantir a saída em ordem alfabética + sort_string(original_str, n); + + // 2. Aloca memória para a permutação que será construída + char *current_perm = (char *)malloc(sizeof(char) * (n + 1)); + if (!current_perm) + return (1); + current_perm[n] = '\0'; // Garante a terminação da string + + // 3. Aloca memória para o array de "usados" e o inicializa com 0 + // `calloc` é útil aqui pois inicializa a memória com zeros. + int *used = (int *)malloc(sizeof(int) * n); + if (!used) + { + free(current_perm); + return (1); + } + for (int i = 0; i < n; i++) + used[i] = 0; + + // Inicia o processo de backtracking a partir da primeira posição (k = 0) + generate_permutations(original_str, current_perm, used, n, 0); + + // Liberta a memória alocada + free(current_perm); + free(used); + return (0); } \ No newline at end of file diff --git a/level-2/permutations/permutations.h b/level-2/permutations/permutations.h deleted file mode 100644 index 1ee8c38..0000000 --- a/level-2/permutations/permutations.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef PERMUTATIONS_H -# define PERMUTATIONS_H - -# include -# include -# include - -void ft_swap(char *a, char *b); -void ft_sort_string(char *str); -void ft_reverse(char *str, int start, int end); -int next_permutation(char *str, int len); -int ft_strlen(char *str); - -#endif \ No newline at end of file diff --git a/level-2/powerset/powerset.c b/level-2/powerset/powerset.c index 851f4af..3e8a697 100644 --- a/level-2/powerset/powerset.c +++ b/level-2/powerset/powerset.c @@ -1,5 +1,8 @@ -#include "powerset.h" +#include +#include +// Função auxiliar para imprimir um subconjunto encontrado. +// A ordem dos elementos é mantida porque os adicionamos na ordem original. void print_subset(int *subset, int size) { int i; @@ -15,59 +18,89 @@ void print_subset(int *subset, int size) printf("\n"); } -void find_subsets(int *set, int set_size, int target, int *subset, - int subset_size, int index) +/** + * Função recursiva de backtracking para encontrar os subconjuntos. + * + * @param target A soma restante que precisamos alcançar. + * @param set O conjunto original de números. + * @param set_size O tamanho do conjunto original. + * @param index O índice do elemento atual que estamos considerando no conjunto. + * @param current_path O subconjunto que estamos construindo atualmente. + * @param path_len O número de elementos no subconjunto atual. + */ +void find_subsets(int target, int *set, int set_size, int index, int *current_path, int path_len) { - int current_sum; - int i; - - if (index == set_size) + // Caso base 1: Encontramos um subconjunto cuja soma é igual ao alvo original. + if (target == 0) { - current_sum = 0; - i = 0; - while (i < subset_size) - { - current_sum += subset[i]; - i++; - } - if (current_sum == target) - print_subset(subset, subset_size); - return ; + print_subset(current_path, path_len); + // Retornamos porque encontramos uma solução válida para este caminho. + return; } - find_subsets(set, set_size, target, subset, subset_size, index + 1); - subset[subset_size] = set[index]; - find_subsets(set, set_size, target, subset, subset_size + 1, index + 1); + + // Caso base 2: Se já consideramos todos os números, não há mais nada a fazer. + if (index >= set_size) + { + return; + } + + // --- Decisão Recursiva --- + + // Escolha 1: INCLUIR o elemento atual (set[index]) no subconjunto. + // Adicionamos o elemento ao nosso caminho. + current_path[path_len] = set[index]; + // Chamamos a função para o próximo elemento, com o alvo reduzido. + find_subsets(target - set[index], set, set_size, index + 1, current_path, path_len + 1); + + // Escolha 2: NÃO INCLUIR o elemento atual (set[index]). + // Este é o passo de "backtrack". Nós simplesmente ignoramos o elemento atual + // e passamos para o próximo, sem alterar o caminho ou o alvo. + find_subsets(target, set, set_size, index + 1, current_path, path_len); } int main(int argc, char **argv) { - int *set; - int *subset; - int target; - int set_size; - int i; + int target; + int *numbers; + int *path; + int size; + int i; + // Deve haver pelo menos um alvo (n). O conjunto (s) pode ser vazio. if (argc < 2) - return (1); + return (0); + target = atoi(argv[1]); - set_size = argc - 2; - set = malloc(sizeof(int) * set_size); - if (!set) - return (1); - subset = malloc(sizeof(int) * set_size); - if (!subset) + size = argc - 2; + + // Aloca memória para o conjunto de números de entrada. + numbers = (int *)malloc(sizeof(int) * size); + if (!numbers) + return (1); // Erro de malloc + + // Aloca memória para o array que armazenará o subconjunto atual. + // O tamanho máximo de um subconjunto é o tamanho do conjunto original. + path = (int *)malloc(sizeof(int) * size); + if (!path) { - free(set); - return (1); + free(numbers); + return (1); // Erro de malloc } + + // Converte os argumentos da linha de comando em um array de inteiros. i = 0; - while (i < set_size) + while (i < size) { - set[i] = atoi(argv[i + 2]); + numbers[i] = atoi(argv[i + 2]); i++; } - find_subsets(set, set_size, target, subset, 0, 0); - free(set); - free(subset); + + // Inicia a busca recursiva a partir do primeiro elemento (índice 0). + find_subsets(target, numbers, size, 0, path, 0); + + // Libera a memória alocada. + free(numbers); + free(path); + return (0); } \ No newline at end of file diff --git a/level-2/powerset/powerset.h b/level-2/powerset/powerset.h deleted file mode 100644 index 1fe88c2..0000000 --- a/level-2/powerset/powerset.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef POWERSET_H -# define POWERSET_H - -# include -# include -# include - -void find_subsets(int *set, int set_size, int target, int *subset, - int subset_size, int index); -void print_subset(int *subset, int size); - -#endif \ No newline at end of file diff --git a/level-2/powerset/powerset_clean.c b/level-2/powerset/powerset_clean.c new file mode 100644 index 0000000..31498d8 --- /dev/null +++ b/level-2/powerset/powerset_clean.c @@ -0,0 +1,68 @@ +#include +#include + +void print_subset(int *subset, int size) +{ + int i; + + i = 0; + while (i < size) + { + printf("%d", subset[i]); + if (i < size - 1) + printf(" "); + i++; + } + printf("\n"); +} + +void find_subsets(int target, int *set, int set_size, int index, int *current_path, int path_len) +{ + if (target == 0) + return (print_subset(current_path, path_len)); + + if (index >= set_size) + return; + + current_path[path_len] = set[index]; + find_subsets(target - set[index], set, set_size, index + 1, current_path, path_len + 1); + find_subsets(target, set, set_size, index + 1, current_path, path_len); +} + +int main(int argc, char **argv) +{ + int target; + int *numbers; + int *path; + int size; + int i; + + if (argc < 2) + return (0); + + target = atoi(argv[1]); + size = argc - 2; + + numbers = (int *)malloc(sizeof(int) * size); + if (!numbers) + return (1); + + path = (int *)malloc(sizeof(int) * size); + if (!path) + { + free(numbers); + return (1); + } + i = 0; + while (i < size) + { + numbers[i] = atoi(argv[i + 2]); + i++; + } + find_subsets(target, numbers, size, 0, path, 0); + + free(numbers); + free(path); + + return (0); +} \ No newline at end of file