std::list in C++ с Пример

⚡ Умно обобщение

std::list in C++ е контейнер за последователности, реализиран като двойно свързан списък, позволяващ бързо вмъкване и изтриване на всяка позиция, като същевременно съхранява елементи в несвързана памет и поддържа двупосочен последователен достъп вместо произволен достъп.

  • 🔗 Двойно свързан списък: Всеки елемент запазва връзки към предишния и следващия си възел, така че данните от std::list се намират в несвързана памет.
  • Бързо вмъкване и изтриване: Добавянето или премахването на елемент на известна позиция е постоянно време, за разлика от вектор, който измества елементи.
  • 🚫 Без произволен достъп: Елементите се достигат чрез последователно обхождане от двата края, така че индексиране като list[3] не е налично.
  • 🧩 Конструктори: Конструкторите по подразбиране, запълване, диапазон, копиране, преместване и инициализиращ списък изграждат std::list по различни начини.
  • 🛠️ Функции на членовете: push_front(), push_back(), insert(), erase(), size(), reverse() и merge() управляват съдържанието на списъка.
  • 🤖 AI помощ: GitHub Copilot и подобни асистенти изграждат декларации на std::list, итератори и вмъкват или изтриват логика от кратък коментар.

std::list in C++

Какво е std::list?

In C++, std::list се отнася до контейнер за съхранение. std::list ви позволява да вмъквате и премахвате елементи от всяко място. std::list е реализиран като двойно свързан списък. Това означава, че до данните от списъка може да се осъществява достъп двупосочно и последователно.

Списъкът със стандартни библиотеки с шаблони не поддържа бърз произволен достъп, но поддържа последователен достъп от всички посоки.

Можете да разпръснете елементи от списъка в различни части от паметта. Информацията, необходима за последователен достъп до данни, се съхранява в контейнер. std::list може да се разширява и свива от двата края, ако е необходимо по време на изпълнение. Вътрешен разпределител автоматично изпълнява изискванията за съхранение.

Тези черти повдигат практичен въпрос: кога всъщност трябва да посегнете към списък?

Защо да използвате std::list?

Ето са причините за използването на std::list:

  • std::list се справя по-добре в сравнение с други контейнери за последователности, като например array и vector.
  • Те имат по-добра производителност при вмъкване, преместване и изваждане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:

функция Descriptйон
вмъкване () Тази функция вмъква нов елемент преди позицията, която сочи итераторът.
push_back() Тази функция добавя нов елемент в края на списъка.
push_front() Той добавя нов елемент в началото на списъка.
pop_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 default, range и move

Code Обяснение:

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

Съдържание на контейнера

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

Имот Descriptйон
Последователност Контейнерите за последователности подреждат своите елементи в строга линейна последователност. Елементите са достъпни чрез тяхната позиция в последователността.
Двойно свързан списък Всеки елемент има информация как да намерите предишни и следващи елементи. Това позволява постоянно време за операции по вмъкване и изтриване.
Разпределител наясно Обект за разпределение се използва за динамично модифициране на размера на паметта.

Вмъкване в списък

Има различни функции, които можем да използваме, за да вмъкваме стойности в списък. Нека демонстрираме това:

Пример 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. Използвайте функцията erase(), посочена от итератора i.
  13. Отпечатайте малко текст на конзолата.
  14. Използвайте for цикъл, за да създадете цикълна променлива x. Тази променлива ще се използва за итерация на елементите на списъка.
  15. Разпечатайте стойностите на списъка на конзолата. Това идва след изтриването.
  16. Край на тялото на for цикъла.
  17. Програмата трябва да върне стойност при успешно завършване.
  18. Край на тялото на функцията main().

Въпроси и Отговори

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, итераторни цикли и вмъква или изтрива извиквания от кратък коментар или име на функция. Често предлага std::vector, когато непрекъснатото съхранение е по-подходящо за задачата.

Асистентите за кодиране с изкуствен интелект автоматично довършват кода на STL контейнера, сигнализират за неправилно използване на итератор, конвертират std::list в std::vector и обясняват компромисите със сложността. Те ускоряват изучаването на STL, въпреки че всяко предложение все още се нуждае от преглед.

Обобщете тази публикация с: