Массивы и циклы: типовые задачи с решениями
Обход массива, поиск минимума и максимума, суммы и средние, реверс и сдвиг, работа с двумерными массивами, вложенные циклы и типовые ошибки индексации.
Первая лабораторная почти по любому языку — обработка массива. Задачи повторяются из года в год, и все они собираются из четырёх базовых схем: накопление, поиск, фильтрация, перестановка.
Накопление
a = [4, -2, 7, 0, 15, -8, 3]
total = 0
count = 0
for x in a:
if x > 0:
total += x
count += 1
avg = total / count if count else 0
print(f'сумма положительных {total}, среднее {avg:.2f}')
Поиск минимума и максимума
a = [4, -2, 7, 0, 15, -8, 3]
min_val, min_idx = a[0], 0
for i in range(1, len(a)):
if a[i] < min_val:
min_val, min_idx = a[i], i
print(f'минимум {min_val} в позиции {min_idx}')
Начальное значение берут из первого элемента, а не из нуля или большого числа. Инициализация нулём даст неверный ответ на массиве из одних положительных чисел при поиске минимума — классическая ошибка.
// То же на C++
#include <iostream>
using namespace std;
int main() {
const int N = 7;
int a[N] = {4, -2, 7, 0, 15, -8, 3};
int minVal = a[0], minIdx = 0;
for (int i = 1; i < N; ++i) {
if (a[i] < minVal) { minVal = a[i]; minIdx = i; }
}
cout << "минимум " << minVal << " в позиции " << minIdx << endl;
return 0;
}
Перестановки элементов
# Реверс на месте, без дополнительного массива
a = [1, 2, 3, 4, 5]
for i in range(len(a) // 2):
a[i], a[-1 - i] = a[-1 - i], a[i]
print(a) # [5, 4, 3, 2, 1]
# Циклический сдвиг влево на k позиций
def rotate_left(a, k):
k %= len(a)
return a[k:] + a[:k]
print(rotate_left([1, 2, 3, 4, 5], 2)) # [3, 4, 5, 1, 2]
# Удаление элементов без создания нового массива
def remove_negatives(a):
write = 0
for read in range(len(a)):
if a[read] >= 0:
a[write] = a[read]
write += 1
del a[write:]
return a
Приём с двумя индексами — чтения и записи — стандартный для удаления элементов за один проход. Он же лежит в основе алгоритмов сжатия массивов и работает за O(n) без дополнительной памяти.
Двумерные массивы
m = [[1, 2, 3],
[4, 5, 6],
[7, 8, 9]]
# Сумма по строкам
for i, row in enumerate(m):
print(f'строка {i}: {sum(row)}')
# Сумма по столбцам
for j in range(len(m[0])):
s = sum(m[i][j] for i in range(len(m)))
print(f'столбец {j}: {s}')
# Главная и побочная диагонали
n = len(m)
main = sum(m[i][i] for i in range(n))
side = sum(m[i][n - 1 - i] for i in range(n))
print(f'главная {main}, побочная {side}')
# Транспонирование
t = [[m[i][j] for i in range(len(m))] for j in range(len(m[0]))]
| Условие на индексы | Что выделяет |
|---|---|
| i == j | Главная диагональ |
| i + j == n − 1 | Побочная диагональ |
| i < j | Выше главной диагонали |
| i > j | Ниже главной диагонали |
| (i + j) % 2 == 0 | Клетки одного цвета в шахматном порядке |
Вложенные циклы
# Таблица умножения
for i in range(1, 6):
for j in range(1, 6):
print(f'{i*j:4}', end='')
print()
# Пузырьковая сортировка с ранним выходом
def bubble_sort(a):
n = len(a)
for i in range(n - 1):
swapped = False
for j in range(n - 1 - i):
if a[j] > a[j + 1]:
a[j], a[j + 1] = a[j + 1], a[j]
swapped = True
if not swapped: # массив уже отсортирован
break
return a
Флаг swapped превращает худшие O(n²) в лучшие O(n) на почти отсортированных данных. Такую деталь стоит упомянуть в отчёте: она показывает, что вы понимаете алгоритм, а не переписали его из учебника.
Ошибки индексации
| Ошибка | Проявление | Как избежать |
|---|---|---|
| range(len(a)) с обращением к a[i+1] | Выход за границу на последнем шаге | range(len(a) - 1) |
| Изменение массива внутри цикла по нему | Пропуск элементов | Идти по копии или собирать новый список |
| Индекс с единицы | Пропущен первый элемент | Помнить, что нумерация с нуля |
| Сравнение i <= len(a) | Выход за границу | Строго i < len(a) |
Что писать в отчёте
- Постановка задачи и формат входных данных.
- Блок-схема алгоритма или его словесное описание.
- Листинг с комментариями — в приложение, если он длиннее страницы.
- Таблица тестов: обычные данные, граничные (пустой массив, один элемент), некорректный ввод.
- Оценка сложности по времени и памяти.
Частые вопросы
Чем массив отличается от списка в Python?
Список — динамическая структура переменной длины с элементами любых типов. Массив (numpy или модуль array) хранит однотипные элементы подряд в памяти, работает быстрее и экономичнее, но размер фиксирован.
Почему индексация начинается с нуля?
Индекс — это смещение от начала массива в памяти. У первого элемента смещение нулевое, поэтому адрес вычисляется как база + i·размер_элемента без лишнего вычитания.
Как обойти двумерный массив по спирали?
Задать четыре границы — верх, низ, лево, право — и в цикле проходить верхнюю строку, правый столбец, нижнюю строку, левый столбец, каждый раз сдвигая соответствующую границу внутрь.