269 lines
8.9 KiB
C
269 lines
8.9 KiB
C
/*
|
||
* УПРАЖНЕНИЯ ПО РАБОТЕ СО СТЕКОМ В C
|
||
* ===================================
|
||
*
|
||
* Теория:
|
||
* -------
|
||
* Стек - структура данных LIFO (Last In, First Out)
|
||
* Операции: push (добавить), pop (удалить), peek (посмотреть), isEmpty
|
||
*
|
||
* В C есть два типа памяти:
|
||
* - СТЕК (Stack) - автоматическая, быстрая, ограниченная, локальные переменные
|
||
* - КУЧА (Heap) - динамическая (malloc/free), медленная, большая
|
||
*/
|
||
|
||
#include <stdbool.h>
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
|
||
#define MAX_SIZE 100
|
||
|
||
/* ============================================================================
|
||
* ЗАДАЧА 1: Базовый стек на массиве
|
||
* ============================================================================
|
||
* Реализуй простой стек с операциями push, pop, peek, isEmpty
|
||
*/
|
||
|
||
typedef struct {
|
||
int data[MAX_SIZE];
|
||
int top; // индекс вершины стека
|
||
} Stack;
|
||
|
||
// Инициализировать стек
|
||
void stack_init(Stack *s) {
|
||
// TODO: реализуй инициализацию
|
||
}
|
||
|
||
// Проверить, пуст ли стек
|
||
bool stack_isEmpty(Stack *s) {
|
||
// TODO: реализуй проверку
|
||
return false;
|
||
}
|
||
|
||
// Проверить, полон ли стек
|
||
bool stack_isFull(Stack *s) {
|
||
// TODO: реализуй проверку
|
||
return false;
|
||
}
|
||
|
||
// Добавить элемент в стек
|
||
bool stack_push(Stack *s, int value) {
|
||
// TODO: реализуй push
|
||
// Проверь переполнение!
|
||
return false;
|
||
}
|
||
|
||
// Удалить и вернуть элемент из стека
|
||
bool stack_pop(Stack *s, int *value) {
|
||
// TODO: реализуй pop
|
||
// Проверь, не пуст ли стек!
|
||
return false;
|
||
}
|
||
|
||
// Посмотреть элемент на вершине без удаления
|
||
bool stack_peek(Stack *s, int *value) {
|
||
// TODO: реализуй peek
|
||
return false;
|
||
}
|
||
|
||
void test_task1() {
|
||
printf("\n=== ЗАДАЧА 1: Базовый стек ===\n");
|
||
Stack s;
|
||
stack_init(&s);
|
||
|
||
// Тест push
|
||
stack_push(&s, 10);
|
||
stack_push(&s, 20);
|
||
stack_push(&s, 30);
|
||
|
||
// Тест peek
|
||
int value;
|
||
if (stack_peek(&s, &value)) {
|
||
printf("Вершина стека: %d (ожидается 30)\n", value);
|
||
}
|
||
|
||
// Тест pop
|
||
while (!stack_isEmpty(&s)) {
|
||
stack_pop(&s, &value);
|
||
printf("Pop: %d\n", value);
|
||
}
|
||
}
|
||
|
||
/* ============================================================================
|
||
* ЗАДАЧА 2: Перевернуть строку используя стек
|
||
* ============================================================================
|
||
* Используй стек для переворачивания строки
|
||
*/
|
||
|
||
void reverse_string(char *str) {
|
||
// TODO: используй стек для переворота строки
|
||
// Подсказка: помести каждый символ в стек, затем извлеки обратно
|
||
}
|
||
|
||
void test_task2() {
|
||
printf("\n=== ЗАДАЧА 2: Перевернуть строку ===\n");
|
||
char str[] = "Hello, Stack!";
|
||
printf("До: %s\n", str);
|
||
reverse_string(str);
|
||
printf("После: %s\n", str);
|
||
printf("Ожидается: !kcatS ,olleH\n");
|
||
}
|
||
|
||
/* ============================================================================
|
||
* ЗАДАЧА 3: Проверка сбалансированности скобок
|
||
* ============================================================================
|
||
* Проверь, правильно ли расставлены скобки: (), [], {}
|
||
* Примеры:
|
||
* - "([])" -> true
|
||
* - "([)]" -> false
|
||
* - "{[()]}" -> true
|
||
*/
|
||
|
||
bool is_balanced(const char *expr) {
|
||
// TODO: используй стек для проверки скобок
|
||
// Алгоритм:
|
||
// 1. При открывающей скобке - push
|
||
// 2. При закрывающей - pop и проверь соответствие
|
||
// 3. В конце стек должен быть пуст
|
||
return false;
|
||
}
|
||
|
||
void test_task3() {
|
||
printf("\n=== ЗАДАЧА 3: Сбалансированность скобок ===\n");
|
||
const char *tests[] = {"()", "([])", "([)]", "{[()]}",
|
||
"(((", "))", "{[(])}", ""};
|
||
|
||
for (int i = 0; i < 8; i++) {
|
||
printf("\"%s\" -> %s\n", tests[i],
|
||
is_balanced(tests[i]) ? "сбалансированы" : "НЕ сбалансированы");
|
||
}
|
||
}
|
||
|
||
/* ============================================================================
|
||
* ЗАДАЧА 4: Калькулятор обратной польской нотации (RPN)
|
||
* ============================================================================
|
||
* Вычисли выражение в постфиксной записи
|
||
* Пример: "3 4 + 2 *" = (3 + 4) * 2 = 14
|
||
*
|
||
* Алгоритм:
|
||
* - Если число - push в стек
|
||
* - Если операция - pop два числа, выполни операцию, push результат
|
||
*/
|
||
|
||
int evaluate_rpn(const char *expr) {
|
||
// TODO: реализуй RPN калькулятор
|
||
// Подсказка: используй strtok для разбора строки
|
||
return 0;
|
||
}
|
||
|
||
void test_task4() {
|
||
printf("\n=== ЗАДАЧА 4: RPN Калькулятор ===\n");
|
||
const char *expressions[] = {
|
||
"3 4 +", // 7
|
||
"3 4 + 2 *", // 14
|
||
"5 1 2 + 4 * + 3 -", // 14
|
||
};
|
||
|
||
for (int i = 0; i < 3; i++) {
|
||
printf("\"%s\" = %d\n", expressions[i], evaluate_rpn(expressions[i]));
|
||
}
|
||
}
|
||
|
||
/* ============================================================================
|
||
* ЗАДАЧА 5: Стековая память vs Куча
|
||
* ============================================================================
|
||
* Изучи разницу между стековой памятью и кучей
|
||
*/
|
||
|
||
void demonstrate_stack_memory() {
|
||
printf("\n=== ЗАДАЧА 5: Стековая память ===\n");
|
||
|
||
// Локальные переменные - на стеке
|
||
int stack_var = 42;
|
||
int stack_array[10];
|
||
|
||
printf("Адрес stack_var: %p\n", (void *)&stack_var);
|
||
printf("Адрес stack_array: %p\n", (void *)stack_array);
|
||
|
||
// Динамическая память - в куче
|
||
int *heap_var = malloc(sizeof(int));
|
||
int *heap_array = malloc(10 * sizeof(int));
|
||
|
||
printf("Адрес heap_var: %p\n", (void *)heap_var);
|
||
printf("Адрес heap_array: %p\n", (void *)heap_array);
|
||
|
||
printf("\nСтек растет вниз (адреса уменьшаются)\n");
|
||
printf("Куча растет вверх (адреса увеличиваются)\n");
|
||
|
||
// TODO: попробуй создать рекурсивную функцию и посмотри
|
||
// как меняются адреса локальных переменных
|
||
|
||
free(heap_var);
|
||
free(heap_array);
|
||
}
|
||
|
||
/* ============================================================================
|
||
* ЗАДАЧА 6: Динамический стек (бонус)
|
||
* ============================================================================
|
||
* Реализуй стек, который растет при необходимости
|
||
*/
|
||
|
||
typedef struct {
|
||
int *data;
|
||
int top;
|
||
int capacity;
|
||
} DynamicStack;
|
||
|
||
void dynamic_stack_init(DynamicStack *s) {
|
||
// TODO: начальная емкость = 4
|
||
}
|
||
|
||
bool dynamic_stack_push(DynamicStack *s, int value) {
|
||
// TODO: если заполнен - удвой размер с помощью realloc
|
||
return false;
|
||
}
|
||
|
||
bool dynamic_stack_pop(DynamicStack *s, int *value) {
|
||
// TODO: можешь также уменьшать размер при малой заполненности
|
||
return false;
|
||
}
|
||
|
||
void dynamic_stack_free(DynamicStack *s) {
|
||
// TODO: освободи память
|
||
}
|
||
|
||
void test_task6() {
|
||
printf("\n=== ЗАДАЧА 6: Динамический стек ===\n");
|
||
DynamicStack s;
|
||
dynamic_stack_init(&s);
|
||
|
||
// Добавь больше элементов, чем начальная емкость
|
||
for (int i = 0; i < 10; i++) {
|
||
dynamic_stack_push(&s, i);
|
||
printf("Pushed %d, capacity: %d\n", i, s.capacity);
|
||
}
|
||
|
||
dynamic_stack_free(&s);
|
||
}
|
||
|
||
/* ============================================================================
|
||
* MAIN - Запуск всех тестов
|
||
* ============================================================================
|
||
*/
|
||
|
||
int main() {
|
||
printf("╔════════════════════════════════════════╗\n");
|
||
printf("║ УПРАЖНЕНИЯ ПО СТЕКУ В C ║\n");
|
||
printf("╚════════════════════════════════════════╝\n");
|
||
|
||
// Раскомментируй по мере выполнения задач
|
||
test_task1();
|
||
// test_task2();
|
||
// test_task3();
|
||
// test_task4();
|
||
// demonstrate_stack_memory();
|
||
// test_task6();
|
||
|
||
return 0;
|
||
}
|