Добавить раздел «Массивы» #1
1 файл изменён
+36
Войди, чтобы оставлять замечания к строкам.
Показать разницуСкрыть разницу
| @@ -0,0 +1,36 @@ | |||
| 1 | +# Массивы в C++ | ||
| 2 | + | ||
| 3 | +Материал о последовательностях, динамической памяти и алгоритмах. Затем решите 12 авторских задач: [Intern](Intern.md), [Middle](Middle.md), [Senior](Senior.md) — по четыре задачи на уровень. | ||
| 4 | + | ||
| 5 | +## Последовательность и выбор контейнера | ||
| 6 | + | ||
| 7 | +Массив — упорядоченный ряд значений одного типа. Индексация начинается с нуля: при `n` элементах индексы от `0` до `n-1`. Для значений фиксированного размера есть `std::array<T, N>` (`<array>`); для размера, известного во время работы, обычно используют `std::vector<T>` (`<vector>`). Встроенный `T a[N]` тоже фиксированного размера. Стандартный C++ не поддерживает массивы переменной длины. | ||
| 8 | + | ||
| 9 | +```cpp | ||
| 10 | +std::vector<int> temperatures{18, 20, 17}; | ||
| 11 | +std::cout << temperatures[1]; // 20 | ||
| 12 | +``` | ||
| 13 | + | ||
| 14 | +Вектор хранит элементы непрерывно и знает число элементов. `size()` — число созданных элементов, `capacity()` — вместимость уже выделенной памяти. Не обращайтесь к элементу по индексу `size()` или выше. `operator[]` не проверяет границу; `at(i)` проверяет и бросает исключение. Пустой вектор нельзя индексировать. | ||
| 15 | + | ||
| 16 | +## Жизненный цикл памяти | ||
| 17 | + | ||
| 18 | +```cpp | ||
| 19 | +std::vector<int> a; | ||
| 20 | +a.reserve(1000); // место зарезервировано, size() всё ещё 0 | ||
| 21 | +for (int i = 0; i < 1000; ++i) a.push_back(i); | ||
| 22 | +``` | ||
| 23 | + | ||
| 24 | +`reserve` резервирует capacity, но не создаёт элементов. Для изменения количества элементов предназначен `resize`. Если размер заранее известен, создайте `std::vector<int> a(n)`. При исчерпании capacity `push_back` может выделить новый участок памяти и переместить элементы. Добавление в конец амортизированно `O(1)`, но конкретное перераспределение стоит `O(n)`. После него ссылки, указатели и итераторы на элементы могут стать недействительными. `insert` и `erase` в середине обычно `O(n)`, так как хвост сдвигается. | ||
| 25 | + | ||
| 26 | +`clear()` удаляет элементы, но не гарантирует освобождения буфера. `shrink_to_fit()` — необязательный запрос реализации. Обычно не нужны ручные `new[]` и `delete[]`: `std::vector` автоматически освобождает память по RAII и помогает избежать утечек и двойного освобождения. | ||
| 27 | + | ||
| 28 | +## Python и Rust | ||
| 29 | + | ||
| 30 | +Python `list` растёт при `append`, индексируется с нуля, но хранит ссылки на объекты; это не то же расположение данных, что компактный `std::vector<int>`. Для крупных числовых данных применяют, например, NumPy. Rust `Vec<T>` похож на вектор C++ по идее владеющего расширяемого буфера и автоматически освобождает память. Rust проверяет правила владения/заимствования; `v.get(i)` возвращает безопасный результат `Option`, а неверный `v[i]` паникует. В C++ выход `a[i]` за границы — неопределённое поведение, а время жизни ссылок контролирует программист. | ||
| 31 | + | ||
| 32 | +## Сложность, память и ошибки | ||
| 33 | + | ||
| 34 | +Доступ по индексу — `O(1)`, проход/поиск — `O(n)`, добавление в конец — амортизированно `O(1)`, вставка в начало — `O(n)`. Копирование вектора из `n` элементов требует `O(n)` времени и памяти; большой массив, который функция только читает, обычно передают как `const std::vector<T>&`. Отличайте память входа от дополнительной памяти алгоритма: `O(1)` дополнительной памяти означает фиксированное число временных переменных, а не отсутствие самого входного массива. | ||
| 35 | + | ||
| 36 | +Частые ошибки: индексация за границей; путаница `reserve` и `resize`; использование ссылки после перераспределения; копирование массива без необходимости; `O(n²)` из-за многократного удаления первого элемента; переполнение суммы (при необходимости используйте `long long`). `size_t` беззнаковый, поэтому осторожно смешивайте его с отрицательными индексами типа `int`. | ||