💻 Информатика 11 класс

Структурированные типы данных. Массивы

Массивы — это фундаментальная структура данных, которая используется повсюду: от алгоритмов рекомендаций в YouTube до обработки пикселей в играх. Сегодня мы разберёмся, как устроен этот механизм и как использовать его для решения реальных задач на языке C.

Метафора массива: упорядоченное хранилище данных, где каждый элемент имеет свой адрес-индекс

Что такое массив и зачем он нужен?

Массив — это структура данных, представляющая собой упорядоченную коллекцию элементов одного типа, каждый из которых идентифицируется своим индексом (номером позиции).

💡 Зачем нужны массивы?

Представь, что тебе нужно обработать результаты 1000 студентов. Создавать 1000 отдельных переменных (score1, score2, ..., score1000) — абсурд. Массив позволяет хранить все данные под одним именем и обращаться к каждому элементу по индексу.

Объявление массива в языке C

В C массив объявляется следующим образом:

тип_данных имя_массива[размер];

Примеры объявления массивов:

int temperatures[365];      // массив из 365 целых чисел (температуры по дням года)
double prices[100];         // массив из 100 вещественных чисел (цены товаров)
char password[20];          // массив из 20 символов (пароль)

⚠️ Критически важно!

В C индексация массивов начинается с 0. Это значит, что первый элемент имеет индекс 0, второй — индекс 1, и так далее. Массив из 10 элементов имеет индексы от 0 до 9.

Инициализация массива

Массив можно инициализировать при объявлении:

int scores[5] = {95, 87, 76, 92, 88};
double weights[] = {65.5, 72.3, 58.1};  // размер определяется автоматически

Или заполнить в цикле:

int numbers[100];
for (int i = 0; i < 100; i++) {
    numbers[i] = i * 2;  // заполняем чётными числами
}

Основные операции с массивами

Рассмотрим базовые операции, которые выполняются с массивами в подавляющем большинстве задач.

Обработка массива: последовательный доступ к элементам через индексацию

Обработка массива: последовательный доступ к элементам через индексацию

Операция 1

📥 Ввод и вывод элементов

Пример: Ввод оценок студента и вывод в обратном порядке.

#include <stdio.h>

int main() {
    int grades[5];
    
    printf("Введите 5 оценок:\n");
    for (int i = 0; i < 5; i++) {
        printf("Оценка %d: ", i + 1);
        scanf("%d", &grades[i]);
    }
    
    printf("\nОценки в обратном порядке:\n");
    for (int i = 4; i >= 0; i--) {
        printf("%d ", grades[i]);
    }
    printf("\n");
    
    return 0;
}

Что происходит? Первый цикл for последовательно заполняет массив с индекса 0 до 4. Второй цикл выводит элементы в обратном порядке — с индекса 4 до 0.

Операция 2

🔍 Поиск элемента

Задача: Найти, есть ли в массиве заданное число.

Линейный поиск — самый простой алгоритм: последовательно проверяем каждый элемент.

#include <stdio.h>

int main() {
    int data[10] = {23, 45, 12, 67, 34, 89, 21, 56, 78, 90};
    int target, found = 0;
    
    printf("Введите число для поиска: ");
    scanf("%d", &target);
    
    for (int i = 0; i < 10; i++) {
        if (data[i] == target) {
            printf("Число %d найдено на позиции %d\n", target, i);
            found = 1;
            break;  // прерываем поиск
        }
    }
    
    if (!found) {
        printf("Число %d не найдено\n", target);
    }
    
    return 0;
}

Анализ сложности: В худшем случае (элемент в конце или отсутствует) нужно проверить все n элементов. Сложность: O(n) — линейная.

Операция 3

📊 Поиск максимума и минимума

Подход: Принимаем первый элемент за текущий максимум/минимум, затем сравниваем с ним все остальные элементы.

#include <stdio.h>

int main() {
    int numbers[8] = {42, 15, 73, 8, 91, 27, 64, 35};
    int max = numbers[0];
    int min = numbers[0];
    int max_index = 0;
    int min_index = 0;
    
    for (int i = 1; i < 8; i++) {
        if (numbers[i] > max) {
            max = numbers[i];
            max_index = i;
        }
        if (numbers[i] < min) {
            min = numbers[i];
            min_index = i;
        }
    }
    
    printf("Максимум: %d (индекс %d)\n", max, max_index);
    printf("Минимум: %d (индекс %d)\n", min, min_index);
    
    return 0;
}

Почему начинаем с i=1? Потому что numbers[0] уже стал начальным значением для max и min. Это классическая оптимизация.

Модификация массивов: вставка и удаление

Рассмотрим операции, которые изменяют структуру массива.

Операции вставки и удаления: реорганизация элементов массива

Операции вставки и удаления: реорганизация элементов массива

Удаление элемента

Задача: Удалить элемент с индексом k и сдвинуть все последующие элементы влево.

Логика: Элементы с индексами k+1, k+2, ..., n-1 перемещаются на позиции k, k+1, ..., n-2.

#include <stdio.h>

int main() {
    int arr[7] = {10, 20, 30, 40, 50, 60, 70};
    int n = 7;  // текущий размер массива
    int k = 3;  // индекс удаляемого элемента
    
    printf("Массив до удаления:\n");
    for (int i = 0; i < n; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
    
    // Сдвигаем элементы влево
    for (int i = k; i < n - 1; i++) {
        arr[i] = arr[i + 1];
    }
    n--;  // уменьшаем размер
    
    printf("Массив после удаления элемента с индексом %d:\n", k);
    for (int i = 0; i < n; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
    
    return 0;
}

Результат: 10 20 30 50 60 70 — элемент 40 (индекс 3) удалён.

Вставка элемента

Задача: Вставить новое значение на позицию с индексом k.

Логика: Элементы с индексами k, k+1, ..., n-1 сдвигаются вправо на позиции k+1, k+2, ..., n.

⚠️ Критично!

Сдвиг должен идти справа налево, иначе данные перезапишутся!

#include <stdio.h>

int main() {
    int arr[10] = {10, 20, 30, 40, 50, 60, 70};  // объявляем с запасом
    int n = 7;  // текущий размер
    int k = 3;  // позиция вставки
    int value = 35;  // вставляемое значение
    
    printf("Массив до вставки:\n");
    for (int i = 0; i < n; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
    
    // Сдвигаем элементы вправо (ВАЖНО: справа налево!)
    for (int i = n; i > k; i--) {
        arr[i] = arr[i - 1];
    }
    arr[k] = value;
    n++;
    
    printf("Массив после вставки %d на позицию %d:\n", value, k);
    for (int i = 0; i < n; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
    
    return 0;
}

Результат: 10 20 30 35 40 50 60 70

Реверс массива

Задача: Перевернуть массив (первый элемент становится последним, и так далее).

Алгоритм: Меняем местами элементы с индексами i и n-1-i, где i идёт от 0 до n/2.

#include <stdio.h>

int main() {
    int arr[6] = {1, 2, 3, 4, 5, 6};
    int n = 6;
    
    printf("Исходный массив: ");
    for (int i = 0; i < n; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
    
    // Реверс
    for (int i = 0; i < n / 2; i++) {
        int temp = arr[i];
        arr[i] = arr[n - 1 - i];
        arr[n - 1 - i] = temp;
    }
    
    printf("Перевёрнутый массив: ");
    for (int i = 0; i < n; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
    
    return 0;
}

Результат: 6 5 4 3 2 1

💡 Почему n/2?

Если массив содержит 6 элементов, достаточно 3 обменов: (0↔5), (1↔4), (2↔3). Центральный элемент (если n нечётное) остаётся на месте автоматически.

Сортировка массивов

Зачем сортировать? Упорядоченные данные обрабатываются эффективнее. Например, двоичный поиск в отсортированном массиве имеет сложность O(log n) вместо O(n).

Сортировка: упорядочивание хаотичных данных в структурированную последовательность

Сортировка: упорядочивание хаотичных данных в структурированную последовательность

Алгоритм 1

💧 Сортировка пузырьком (Bubble Sort)

Принцип: Сравниваем соседние элементы и меняем их местами, если они в неправильном порядке. "Тяжёлые" элементы "тонут" вниз, "лёгкие" "всплывают" вверх.

#include <stdio.h>

void bubbleSort(int arr[], int n) {
    for (int i = 0; i < n - 1; i++) {
        for (int j = 0; j < n - i - 1; j++) {
            if (arr[j] > arr[j + 1]) {
                // Обмен элементов
                int temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
            }
        }
    }
}

int main() {
    int data[6] = {64, 34, 25, 12, 22, 11};
    int n = 6;
    
    printf("До сортировки: ");
    for (int i = 0; i < n; i++) printf("%d ", data[i]);
    printf("\n");
    
    bubbleSort(data, n);
    
    printf("После сортировки: ");
    for (int i = 0; i < n; i++) printf("%d ", data[i]);
    printf("\n");
    
    return 0;
}

Анализ:

  • Внешний цикл (i): n-1 проход
  • Внутренний цикл (j): на каждом проходе сравниваем всё меньше элементов
  • Сложность: O(n²) — квадратичная

Почему n-i-1? После каждого прохода самый большой элемент оказывается в конце, его больше не нужно проверять.

Алгоритм 2

🎯 Сортировка выбором (Selection Sort)

Принцип: Находим минимальный элемент в неотсортированной части массива и меняем его местами с первым неотсортированным элементом.

#include <stdio.h>

void selectionSort(int arr[], int n) {
    for (int i = 0; i < n - 1; i++) {
        int min_index = i;
        
        // Находим индекс минимального элемента
        for (int j = i + 1; j < n; j++) {
            if (arr[j] < arr[min_index]) {
                min_index = j;
            }
        }
        
        // Обмен
        if (min_index != i) {
            int temp = arr[i];
            arr[i] = arr[min_index];
            arr[min_index] = temp;
        }
    }
}

int main() {
    int data[6] = {64, 25, 12, 22, 11, 34};
    int n = 6;
    
    printf("До сортировки: ");
    for (int i = 0; i < n; i++) printf("%d ", data[i]);
    printf("\n");
    
    selectionSort(data, n);
    
    printf("После сортировки: ");
    for (int i = 0; i < n; i++) printf("%d ", data[i]);
    printf("\n");
    
    return 0;
}

Преимущество: Минимальное количество обменов — всего n-1. Полезно, когда операция обмена дорогая.

Сложность: O(n²) по сравнениям, O(n) по обменам.

Практическое применение: реальные задачи

Рассмотрим задачи, которые демонстрируют практическую ценность работы с массивами.

Эффективность алгоритмов: от линейного к логарифмическому поиску

Эффективность алгоритмов: от линейного к логарифмическому поиску

🌡️ Задача 1: Анализ метеоданных

Условие: Даны температуры за 30 дней июня. Найти среднюю температуру и дни с отклонением более чем на 5°C.

#include <stdio.h>

int main() {
    double temp[30];
    double sum = 0, avg;
    
    printf("Введите температуры за 30 дней июня:\n");
    for (int i = 0; i < 30; i++) {
        printf("День %d: ", i + 1);
        scanf("%lf", &temp[i]);
        sum += temp[i];
    }
    
    avg = sum / 30;
    printf("\nСредняя температура: %.2f°C\n", avg);
    
    printf("\nДни с отклонением >5°C от средней:\n");
    for (int i = 0; i < 30; i++) {
        double deviation = temp[i] - avg;
        if (deviation > 5 || deviation < -5) {
            printf("День %d: %.2f°C (отклонение: %+.2f°C)\n", 
                   i + 1, temp[i], deviation);
        }
    }
    
    return 0;
}

🔐 Задача 2: Криптографическая головоломка Буратино

Условие: Шифр замка — двузначное число. Сумма цифр + произведение цифр = само число. Найти все варианты.

Математический подход: Пусть число 10a + b, где a и b — цифры.
Условие: (a + b) + (a * b) = 10a + b
Упрощаем: a + a*b = 10aa*b = 9ab = 9 (при a ≠ 0)

#include <stdio.h>

int main() {
    printf("Возможные коды замка:\n");
    
    for (int num = 10; num <= 99; num++) {
        int a = num / 10;      // первая цифра
        int b = num % 10;      // вторая цифра
        
        if ((a + b) + (a * b) == num) {
            printf("%d ", num);
        }
    }
    printf("\n");
    
    return 0;
}

Результат: 19, 29, 39, 49, 59, 69, 79, 89, 99

Проверка для 19: (1+9) + (1×9) = 10 + 9 = 19 ✓

✨ Ключевые выводы

Подведём итоги изученного материала.

Массивы — фундамент структур данных. Понимание массивов критически важно для изучения списков, стеков, очередей, деревьев.
Индексация с 0 в C — не каприз, а convention. Это связано с тем, как адресуется память на низком уровне.
Алгоритмическая сложность имеет значение. Разница между O(n) и O(n²) может означать часы вычислений на больших данных.
Простые алгоритмы — основа сложных. Сортировка пузырьком неэффективна для миллионов элементов, но её принцип лежит в основе более продвинутых методов.
Массивы везде: обработка изображений (массив пикселей), аудио (массив сэмплов), игры (массив игровых объектов), машинное обучение (тензоры — многомерные массивы).

🤔 Проверь себя

Мини-кейсы для проверки понимания материала.

Мини-кейс 1: У тебя есть массив ID пользователей, вошедших в систему за последний час. Как быстро проверить, входил ли конкретный пользователь, если массив неотсортирован? Отсортирован?

Подумай о:

  • Какой алгоритм поиска использовать в неотсортированном массиве?
  • Можно ли ускорить поиск в отсортированном массиве?
  • Какова будет сложность каждого подхода?
Мини-кейс 2: В игре персонажи хранятся в массиве. При смерти персонажа его нужно удалить. Какая операция более эффективна: сдвиг всех элементов или просто пометка ячейки как "пустая"?

Рассмотри:

  • Сколько операций требует каждый подход?
  • Как это повлияет на обход массива?
  • Какие есть плюсы и минусы каждого метода?
Мини-кейс 3: Тебе нужно найти медиану (среднее значение при упорядочивании) массива из 1001 элемента. Какой первый шаг?

Подсказка:

  • Что нужно сделать с массивом перед поиском медианы?
  • На какой позиции будет находиться медиана после сортировки?
  • Нужно ли полностью сортировать массив?
Задача: Напиши функцию, которая проверяет, является ли массив палиндромом (читается одинаково слева направо и справа налево).

Алгоритм:

  • Сравнивай элементы с индексами i и n-1-i
  • Достаточно проверить до середины массива
  • Если хотя бы одна пара не совпадает — не палиндром

💻 Практические задания

Примени полученные знания на практике!

📝 Задание 1: Работа с температурами

Создай программу, которая:

  • Вводит температуры за неделю
  • Находит самую высокую и низкую температуру
  • Вычисляет среднюю температуру
  • Определяет, сколько дней было теплее/холоднее средней

🔢 Задание 2: Обработка чисел

Напиши программу, которая:

  • Заполняет массив из 20 случайных чисел от 1 до 100
  • Находит сумму чётных элементов
  • Подсчитывает количество элементов, кратных 3
  • Выводит элементы в обратном порядке

🎲 Задание 3: Анализ оценок

Разработай программу для анализа оценок класса:

  • Ввод оценок 10 учеников (от 2 до 5)
  • Подсчёт количества каждой оценки
  • Нахождение средней оценки класса
  • Определение количества отличников (5) и двоечников (2)

🔄 Задание 4: Циклический сдвиг

Реализуй программу циклического сдвига массива:

  • Создай массив из 8 элементов
  • Реализуй сдвиг всех элементов на 1 позицию вправо
  • Последний элемент должен стать первым
  • Например: {1,2,3,4,5} → {5,1,2,3,4}
Массивы — мышление в терминах коллекций данных

Массивы — мышление в терминах коллекций данных

🚀 Отличная работа! Массивы — это не просто учебная тема. Это мышление в терминах коллекций данных, понимание того, как компьютер организует и обрабатывает информацию в памяти. Освоив массивы, ты делаешь шаг к пониманию алгоритмов, которые меняют мир — от поисковых систем до искусственного интеллекта.

Информатика — твой билет в цифровое будущее