std::list в C++ с примером

⚡ Умное резюме

std::list в C++ Это контейнер последовательностей, реализованный в виде двусвязного списка, обеспечивающий быструю вставку и удаление элементов в любой позиции, при этом элементы хранятся в несмежной памяти и поддерживают двунаправленный последовательный доступ вместо произвольного доступа.

  • 🔗 Двусвязный список: Каждый элемент сохраняет ссылки на свой предыдущий и следующий узел, поэтому данные std::list хранятся в несмежной области памяти.
  • ⚡ Быстрая вставка и удаление: Добавление или удаление элемента в известной позиции происходит за постоянное время, в отличие от вектора, в котором происходит сдвиг элементов.
  • ???? Без произвольного доступа: Доступ к элементам осуществляется путем последовательного обхода с обоих концов, поэтому индексация, например, list[3], недоступна.
  • 🧩 Конструкторы: Конструкторы default, fill, range, copy, move и initializer-list создают std::list различными способами.
  • 🇧🇷 Функции участника: Функции push_front(), push_back(), insert(), erase(), size(), reverse() и merge() управляют содержимым списка.
  • 🤖 Помощь ИИ: GitHub Copilot и аналогичные сервисы позволяют создавать шаблоны для объявлений std::list, итераторов, а также добавлять или удалять логику из коротких комментариев.

std::list в C++

Что такое std::list?

In C++std::list — это контейнер для хранения данных. Он позволяет добавлять и удалять элементы из любого места списка. std::list реализован как двусвязный список. Это означает, что доступ к данным списка возможен в обоих направлениях и последовательно.

Список стандартных шаблонов библиотеки не поддерживает быстрый произвольный доступ, но поддерживает последовательный доступ со всех направлений.

Вы можете разбросать элементы списка по разным участкам памяти. Информация, необходимая для последовательного доступа к данным, хранится в контейнере. Std::list может расширяться и сжиматься с обоих концов по мере необходимости во время выполнения. Внутренний распределитель автоматически удовлетворяет требования к хранилищу.

Эти характеристики поднимают практический вопрос: когда же действительно стоит обратиться к списку?

Зачем использовать std::list?

Вот причины использования std::list:

  • Класс std::list показывает лучшие результаты по сравнению с другими контейнерами последовательностей, такими как массивы и векторы.
  • Они демонстрируют лучшие показатели при вставке, перемещении и извлечении.tracЭлементы, расположенные в любом месте.
  • Std::list также лучше справляется с алгоритмами, которые интенсивно выполняют такие операции.

После того, как причины стали ясны, следующим шагом является синтаксис, который это объявляет.

Синтаксис списка

Чтобы определить std::list, нам нужно импортировать заголовочный файл. Вот синтаксис определения std::list:

template < class Type, class Alloc =allocator<T> > class list;

Вот описание вышеуказанных параметров:

  • T — определяет тип содержащегося элемента. Вы можете заменить T любым типом данных, даже определяемым пользователем.
  • Alloc – Определяет тип объекта распределителя памяти. По умолчанию используется шаблон класса распределителя. Он зависит от значения и использует простую модель выделения памяти.

Пример 1

#include <algorithm>
#include <iostream>
#include <list>
int main() {
	std::list<int> my_list = { 12, 5, 10, 9 };

	for (int x : my_list) {
		std::cout << x << '\n';
	}
}

Выход:

Результат выполнения примера создания и итерации в std::list.

Вот скриншот кода:

C++ Код, создающий std::list и выводящий его в цикле for.

Code Объяснение:

  1. Включите заголовочный файл алгоритма, чтобы использовать его функции.
  2. Включите заголовочный файл iostream, чтобы использовать его функции.
  3. Включите файл заголовка списка, чтобы использовать его функции.
  4. Вызовите функцию main(). Логику программы следует добавить в тело этой функции.
  5. Создайте список с именем my_list с набором из 4 целых чисел.
  6. Использовать для цикла Для создания переменной цикла x. Эта переменная будет использоваться для итерации по элементам списка.
  7. Распечатайте значения списка на консоли.
  8. Конец тела цикла for.
  9. Конец тела функции main().

C++ Список функций

Вот общие функции std::list:

Функция Описание
вставка () Эта функция вставляет новый элемент перед позицией, на которую указывает итератор.
отталкивать() Эти функции добавляют новый элемент в конец списка.
push_front() Он добавляет новый элемент в начало списка.
поп_фронт() Он удаляет первый элемент списка.
размер() Эта функция определяет количество элементов списка.
передний() Определяет первые элементы списка.
назад() Чтобы определить последний элемент списка.
обеспечить регресс() Он переворачивает элементы списка.
объединить () Он объединяет два отсортированных списка.

Конструкторы

Вот список Функции предоставлено заголовочный файл:

  • Конструктор по умолчанию std::list::list() — создает пустой список с нулевыми элементами.
  • Конструктор заполнения std::list::list() — создает список из n элементов и присваивает каждому элементу значение ноль (0).
  • Конструктор диапазона std::list::list() — создает список со множеством элементов в диапазоне от первого до последнего.
  • Конструктор копирования std::list::list() — создает список с копией каждого элемента, содержащегося в существующем списке.
  • Конструктор перемещения std::list::list() — создает список с элементами другого списка, используя семантику перемещения.
  • Конструктор списка инициализаторов std::list::list() — создает список с элементами другого списка, используя семантику перемещения.

Пример 2

#include <iostream>
#include <list>
using namespace std;
int main(void) {
	list<int> l;
	list<int> l1 = { 10, 20, 30 };
	list<int> l2(l1.begin(), l1.end());
	list<int> l3(move(l1));  
	cout << "Size of list l: " << l.size() << endl;
	cout << "List l2 contents: " << endl;
	for (auto it = l2.begin(); it != l2.end(); ++it)
	      cout << *it << endl;
	cout << "List l3 contents: " << endl;
	for (auto it = l3.begin(); it != l3.end(); ++it)
		cout << *it << endl;
	return 0;
}

Выход:

Вывод примера использования конструкторов std::list

Вот скриншот кода:

C++ Код, демонстрирующий конструкторы по умолчанию, диапазона и перемещения для std::list.

Code Объяснение:

  1. Включите заголовочный файл iostream, чтобы использовать его функции.
  2. Включите файл заголовка списка, чтобы использовать его функции.
  3. Включите пространство имен std в код, чтобы использовать его классы без его вызова.
  4. Вызовите функцию main(). Логику программы следует добавить в тело этой функции.
  5. Создайте пустой список с именем l.
  6. Создайте список с именем l1 с набором из трех целых чисел.
  7. Создайте список с именем l2 со всеми элементами списка с именем l1 от начала до конца.
  8. Создайте список с именем l3, используя семантику перемещения. Список l3 будет иметь то же содержимое, что и список l2.
  9. Выведите размер списка с именем l на консоли вместе с другим текстом.
  10. Напечатайте текст на консоли.
  11. Создайте итератор с именем it и используйте его для перебора элементов списка с именем l2.
  12. Выведите элементы списка с именем l2 на консоль.
  13. Напечатайте текст на консоли.
  14. Создайте итератор с именем it и используйте его для перебора элементов списка с именем l3.
  15. Выведите элементы списка с именем l3 на консоль.
  16. Программа должна вернуть значение после успешного завершения.
  17. Конец тела функции main().

Свойства контейнера

Вот список свойств контейнера:

Свойства Описание
Последовательность Контейнеры последовательностей упорядочивают свои элементы в строгой линейной последовательности. Доступ к элементам осуществляется по их положению в последовательности.
Двусвязный список Каждый элемент содержит информацию о том, как найти предыдущий и следующий элементы. Это обеспечивает постоянное время для операций вставки и удаления.
С поддержкой распределителя Объект-распределитель используется для динамического изменения размера хранилища.

Вставка в список

Существуют различные функции, которые можно использовать для вставки значений в список. Давайте это продемонстрируем:

Пример 3

#include <algorithm>
#include <iostream>
#include <list>
int main() {
	std::list<int> my_list = { 12, 5, 10, 9 };
	my_list.push_front(11);
	my_list.push_back(18);
	auto it = std::find(my_list.begin(), my_list.end(), 10);
	if (it != my_list.end()) {
		my_list.insert(it, 21);
	}
	for (int x : my_list) {
		std::cout << x << '\n';
	}
}

Выход:

Вывод после вставки элементов в std::list

Вот скриншот кода:

C++ Код с использованием функций push_front, push_back и insert в std::list

Code Объяснение:

  1. Включите заголовочный файл алгоритма, чтобы использовать его функции.
  2. Включите заголовочный файл iostream, чтобы использовать его функции.
  3. Включите файл заголовка списка, чтобы использовать его функции.
  4. Вызовите функцию main(). Логику программы следует добавить в тело этой функции.
  5. Создайте список с именем my_list с набором из 4 целых чисел.
  6. Вставьте элемент 11 в начало списка с именем my_list.
  7. Вставьте элемент 18 в конец списка с именем my_list.
  8. Создайте для него итератор и с его помощью найдите элемент 10 из списка my_list.
  9. Используйте оператор if, чтобы определить, найден ли указанный выше элемент или нет.
  10. Вставьте элемент 21 перед указанным выше элементом, если он был найден.
  11. Конец тела оператора if.
  12. Используйте цикл for, чтобы создать переменную цикла x. Эта переменная будет использоваться для перебора элементов списка.
  13. Распечатайте значения списка на консоли.
  14. Конец тела цикла for.
  15. Конец тела функции main().

Элементы, входящие в список, с тем же успехом могут быть и удалены.

Удаление из списка

Из списка можно удалять элементы. Функция erase() позволяет удалить один элемент или диапазон элементов из списка.

  • Чтобы удалить один элемент, вы просто передаете одну целочисленную позицию. Элемент будет удален.
  • Для удаления диапазона необходимо передать начальный и конечный итераторы. Давайте это продемонстрируем.

Пример 4

#include <algorithm>
#include <iostream>
#include <list>
using namespace std;
int main() {
	std::list<int> my_list = { 12, 5, 10, 9 };
	cout << "List elements before deletion: ";
	for (int x : my_list) {
		std::cout << x << '\n';
	}
	list<int>::iterator i = my_list.begin();
	my_list.erase(i);
	cout << "\nList elements after deletion: ";
	for (int x : my_list) {
		std::cout << x << '\n';
	}
	return 0;
}

Выход:

Результат после удаления элемента из std::list

Вот скриншот кода:

C++ Код, использующий функцию erase для объекта std::list.

Code Объяснение:

  1. Включите заголовочный файл алгоритма, чтобы использовать его функции.
  2. Включите заголовочный файл iostream, чтобы использовать его функции.
  3. Включите файл заголовка списка, чтобы использовать его функции.
  4. Включите пространство имен std в нашу программу, чтобы использовать его классы, не вызывая его.
  5. Вызовите функцию main(). Логику программы следует добавить в тело этой функции.
  6. Создайте список с именем my_list с набором из 4 целых чисел.
  7. Напечатайте текст на консоли.
  8. Используйте цикл for, чтобы создать переменную цикла x. Эта переменная будет использоваться для перебора элементов списка.
  9. Распечатайте значения списка на консоли.
  10. Конец тела цикла for.
  11. Создайте итератор i, указывающий на первый элемент списка.
  12. Используйте функцию стирания(), на которую указывает итератор i.
  13. Напечатайте текст на консоли.
  14. Используйте цикл for, чтобы создать переменную цикла x. Эта переменная будет использоваться для перебора элементов списка.
  15. Распечатайте значения списка на консоли. Это происходит после удаления.
  16. Конец тела цикла for.
  17. Программа должна вернуть значение после успешного завершения.
  18. Конец тела функции main().

Часто задаваемые вопросы (FAQ)

std::vector хранит элементы в смежной памяти с произвольным доступом за O(1), в то время как std::list — это двусвязный список, обеспечивающий вставку или удаление элементов в любом месте за O(1). Выберите vector для индексации и list для частых вставок в середине списка.

Нет. std::list не имеет оператора произвольного доступа, поэтому list[2] не компилируется. Достичь элемента можно, перебирая по одному узлу из begin() или end(), что занимает линейное время O(n) для глубокой позиции.

std::list — это двусвязный список, который перемещается в обоих направлениях и поддерживает push_back. std::forward_list — это односвязный список, который перемещается только вперед, использует меньше памяти на узел и не предоставляет итераторов size() или reverse.

Вызовите функцию-член my_list.sort(), которая работает примерно за N log N и обеспечивает стабильность равных элементов. Алгоритм std::sort не подойдет, поскольку ему требуются итераторы с произвольным доступом. Передайте std::greater в sort() для сортировки в порядке убывания.

Вставка или удаление узла занимает постоянное время O(1), если вы удерживаете итератор на позиции, поскольку изменяются только указатели на соседние узлы. Поиск этой позиции первым путем обхода по-прежнему занимает время O(n).

Да. std::list не является множеством, поэтому он свободно хранит повторяющиеся значения. Каждое действие push_back, push_front или insert добавляет новый узел независимо от существующего содержимого. Используйте std::set, когда вам нужно отклонить повторяющиеся элементы.

Да. Второй пилот GitHub Записывает объявления std::list, циклы итераторов, а также вызовы insert или erase из короткого комментария или имени функции. Часто предлагает использовать std::vector, когда для решения задачи лучше подходит непрерывное хранилище.

Искусственный интеллект, помогающий программировать, автоматически дополняет код контейнеров STL, указывает на неправильное использование итераторов, преобразует std::list в std::vector и объясняет компромиссы в вычислительной сложности. Они ускоряют обучение STL, хотя каждое предложение все еще требует проверки.

Подведем итог этой публикации следующим образом: