std::รายการใน C++ พร้อมตัวอย่าง

⚡ สรุปอย่างชาญฉลาด

std::รายการใน C++ เป็นคอนเทนเนอร์ลำดับที่ถูกนำมาใช้ในรูปแบบของรายการเชื่อมโยงสองทาง ซึ่งช่วยให้สามารถแทรกและลบข้อมูลได้อย่างรวดเร็วในตำแหน่งใดก็ได้ ในขณะที่จัดเก็บองค์ประกอบในหน่วยความจำที่ไม่ต่อเนื่องกัน และรองรับการเข้าถึงตามลำดับแบบสองทิศทาง แทนที่จะเป็นการเข้าถึงแบบสุ่ม

  • 🔗 รายการเชื่อมโยงสองทาง: แต่ละองค์ประกอบจะเก็บลิงก์ไปยังโหนดก่อนหน้าและโหนดถัดไป ดังนั้นข้อมูลใน std::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::รายการใน C++

std::รายการคืออะไร?

In C++`std::list` หมายถึงคอนเทนเนอร์สำหรับจัดเก็บข้อมูล `std::list` อนุญาตให้คุณแทรกและลบรายการจากที่ใดก็ได้ `std::list` ถูกสร้างขึ้นโดยใช้โครงสร้างข้อมูลแบบลิสต์สองทิศทาง (doubly-linked list) ซึ่งหมายความว่าข้อมูลในลิสต์สามารถเข้าถึงได้ทั้งสองทิศทางและเรียงลำดับกัน

รายการไลบรารีเทมเพลตมาตรฐานไม่รองรับการเข้าถึงแบบสุ่มที่รวดเร็ว แต่รองรับการเข้าถึงแบบเรียงลำดับจากทุกทิศทาง

คุณสามารถกระจายองค์ประกอบรายการในส่วนหน่วยความจำต่างๆ ได้ ข้อมูลที่จำเป็นสำหรับการเข้าถึงข้อมูลตามลำดับจะถูกจัดเก็บไว้ในคอนเทนเนอร์ รายการ std::สามารถขยายและย่อจากปลายทั้งสองด้านได้ตามต้องการในระหว่างรันไทม์ ตัวจัดสรรภายในจะตอบสนองความต้องการพื้นที่เก็บข้อมูลโดยอัตโนมัติ

ลักษณะเหล่านี้ทำให้เกิดคำถามเชิงปฏิบัติขึ้นมาว่า: คุณควรจะใช้รายการเมื่อใดกันแน่?

เหตุใดจึงใช้ std::list

ต่อไปนี้คือเหตุผลที่ควรใช้ std::list:

  • std::list ทำงานได้ดีกว่าเมื่อเทียบกับคอนเทนเนอร์ลำดับอื่นๆ เช่น array และ vector
  • พวกเขามีประสิทธิภาพที่ดีกว่าในการสอดใส่ เคลื่อนย้าย และดึงออกtracองค์ประกอบ ting จากตำแหน่งใดก็ได้
  • std::list ยังทำงานได้ดีขึ้นกับอัลกอริทึมที่ดำเนินการอย่างเข้มข้นดังกล่าวอีกด้วย

เมื่อเหตุผลชัดเจนแล้ว ขั้นตอนต่อไปคือไวยากรณ์ที่ใช้ในการประกาศสิ่งนั้น

ไวยากรณ์รายการ

ในการกำหนดรายการ std:: เราต้องนำเข้าไฟล์ ไฟล์ส่วนหัว นี่คือไวยากรณ์คำจำกัดความ 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';
	}
}

Output:

ผลลัพธ์ของตัวอย่างการสร้างและการวนซ้ำ std::list

นี่คือภาพหน้าจอของรหัส:

C++ โค้ดที่สร้าง std::list และพิมพ์ออกมาโดยใช้ลูป for

Code คำอธิบาย:

  1. รวมไฟล์ส่วนหัวของอัลกอริทึมเพื่อใช้ฟังก์ชันต่างๆ
  2. รวมไฟล์ส่วนหัว iostream เพื่อใช้ฟังก์ชันต่างๆ
  3. รวมไฟล์ส่วนหัวของรายการเพื่อใช้ฟังก์ชันต่างๆ
  4. เรียกใช้ฟังก์ชัน main() ควรเพิ่มตรรกะของโปรแกรมภายในเนื้อหาของฟังก์ชันนี้
  5. สร้างรายการชื่อ my_list ด้วยชุดจำนวนเต็ม 4 ตัว
  6. ใช้ สำหรับห่วง เพื่อสร้างตัวแปรลูป x ตัวแปรนี้จะใช้ในการวนซ้ำผ่านองค์ประกอบในลิสต์
  7. พิมพ์ค่าของรายการบนคอนโซล
  8. ส่วนท้ายของ for loop
  9. ส่วนท้ายของฟังก์ชัน main()

C++ รายการฟังก์ชั่น

ต่อไปนี้เป็นฟังก์ชัน std::list ทั่วไป:

ฟังก์ชัน Descriptไอออน
แทรก() ฟังก์ชันนี้จะแทรกรายการใหม่ก่อนตำแหน่งที่ตัววนซ้ำชี้
ผลักดัน_กลับ() ฟังก์ชันนี้จะเพิ่มรายการใหม่ที่ส่วนท้ายของรายการ
push_front() โดยจะเพิ่มรายการใหม่ที่ด้านหน้าของรายการ
pop_front() มันจะลบรายการแรกของรายการ
ขนาด() ฟังก์ชันนี้กำหนดจำนวนองค์ประกอบรายการ
ด้านหน้า() เพื่อกำหนดรายการแรกของรายการ
กลับ() เพื่อกำหนดรายการสุดท้ายของรายการ
ย้อนกลับ () มันกลับรายการ
ผสาน() มันรวมสองรายการที่เรียงลำดับเข้าด้วยกัน

คอนสตรัคเตอร์

นี่คือรายการของ ฟังก์ชั่น จัดทำโดย ไฟล์ส่วนหัว:

  • ตัวสร้างเริ่มต้น std::list::list()- มันสร้างรายการว่างที่มีองค์ประกอบเป็นศูนย์
  • Fill Constructor std::list::list()- สร้างรายการที่มีองค์ประกอบ n และกำหนดค่าเป็นศูนย์ (0) ให้กับแต่ละองค์ประกอบ
  • ตัวสร้างช่วง std::list::list()- สร้างรายการที่มีองค์ประกอบมากมายในช่วงแรกถึงสุดท้าย
  • คัดลอกตัวสร้าง std::list::list()- สร้างรายการพร้อมสำเนาของแต่ละองค์ประกอบที่มีอยู่ในรายการที่มีอยู่
  • Move Constructor std::list::list()- สร้างรายการที่มีองค์ประกอบของรายการอื่นโดยใช้ความหมายการย้าย
  • ตัวสร้างรายการ Initializer 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;
}

Output:

ผลลัพธ์ของตัวอย่างคอนสตรัคเตอร์ std::list

นี่คือภาพหน้าจอของรหัส:

C++ โค้ดแสดงการใช้งานคอนสตรัคเตอร์แบบ default, range และ move ของ std::list

Code คำอธิบาย:

  1. รวมไฟล์ส่วนหัว iostream เพื่อใช้ฟังก์ชันต่างๆ
  2. รวมไฟล์ส่วนหัวของรายการเพื่อใช้ฟังก์ชันต่างๆ
  3. รวมเนมสเปซมาตรฐานในโค้ดเพื่อใช้คลาสโดยไม่ต้องเรียกมัน
  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';
	}
}

Output:

ผลลัพธ์หลังจากแทรกองค์ประกอบลงใน 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 loop เพื่อสร้างตัวแปร loop x ตัวแปรนี้จะถูกนำมาใช้เพื่อวนซ้ำองค์ประกอบรายการ
  13. พิมพ์ค่าของรายการบนคอนโซล
  14. ส่วนท้ายของตัว for a loop
  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;
}

Output:

ผลลัพธ์หลังจากลบองค์ประกอบออกจาก std::list

นี่คือภาพหน้าจอของรหัส:

C++ โค้ดที่ใช้ฟังก์ชัน erase กับ std::list

Code คำอธิบาย:

  1. รวมไฟล์ส่วนหัวของอัลกอริทึมเพื่อใช้ฟังก์ชันต่างๆ
  2. รวมไฟล์ส่วนหัว iostream เพื่อใช้ฟังก์ชันต่างๆ
  3. รวมไฟล์ส่วนหัวของรายการเพื่อใช้ฟังก์ชันต่างๆ
  4. รวมเนมสเปซมาตรฐานในโปรแกรมของเราเพื่อใช้คลาสโดยไม่ต้องเรียกมัน
  5. เรียกใช้ฟังก์ชัน main() ควรเพิ่มตรรกะของโปรแกรมภายในเนื้อหาของฟังก์ชันนี้
  6. สร้างรายการชื่อ my_list ด้วยชุดจำนวนเต็ม 4 ตัว
  7. พิมพ์ข้อความบนคอนโซล
  8. ใช้ for loop เพื่อสร้างตัวแปร loop x ตัวแปรนี้จะถูกนำมาใช้เพื่อวนซ้ำองค์ประกอบรายการ
  9. พิมพ์ค่าของรายการบนคอนโซล
  10. ส่วนท้ายของ for loop
  11. สร้างตัววนซ้ำ i ที่ชี้ไปยังองค์ประกอบแรกของรายการ
  12. ใช้ฟังก์ชันลบ () ชี้โดยตัววนซ้ำ i
  13. พิมพ์ข้อความบนคอนโซล
  14. ใช้ for loop เพื่อสร้างตัวแปร loop x ตัวแปรนี้จะถูกนำมาใช้เพื่อวนซ้ำองค์ประกอบรายการ
  15. พิมพ์ค่าของรายการบนคอนโซล สิ่งนี้เกิดขึ้นหลังจากการลบ
  16. ส่วนท้ายของ for loop
  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 iterator)

เรียกใช้ฟังก์ชันสมาชิก my_list.sort() ซึ่งใช้เวลาประมาณ N log N และรักษาค่าองค์ประกอบที่เท่ากันให้คงที่ อัลกอริทึม std::sort จะใช้ไม่ได้เพราะต้องการ iterator ที่เข้าถึงแบบสุ่ม ส่งค่า std::greater ไปยัง sort() เพื่อเรียงลำดับจากมากไปน้อย

การแทรกหรือลบโหนดใช้เวลาคงที่ O(1) เมื่อคุณเก็บตัวชี้ไปยังตำแหน่งนั้นไว้ เนื่องจากมีเพียงตัวชี้ข้างเคียงเท่านั้นที่เปลี่ยนแปลง การค้นหาตำแหน่งนั้นก่อนโดยการท่องไปยังคงใช้เวลา O(n)

ใช่แล้ว std::list ไม่ใช่ set ดังนั้นจึงเก็บค่าที่ซ้ำกันได้อย่างอิสระ การดำเนินการ push_back, push_front หรือ insert ทุกครั้งจะเพิ่มโหนดใหม่โดยไม่คำนึงถึงเนื้อหาที่มีอยู่เดิม ควรใช้ std::set เมื่อต้องการปฏิเสธองค์ประกอบที่ซ้ำกัน

ใช่. นักบิน GitHub โปรแกรมนี้เขียนการประกาศ std::list, ลูป iterator และการเรียก insert หรือ erase จากข้อความแสดงความคิดเห็นสั้นๆ หรือชื่อฟังก์ชัน โดยมักจะแนะนำให้ใช้ std::vector เมื่อการจัดเก็บข้อมูลแบบต่อเนื่องเหมาะสมกับงานนั้นมากกว่า

ผู้ช่วยเขียนโค้ด AI จะเติมโค้ดคอนเทนเนอร์ STL โดยอัตโนมัติ แจ้งเตือนการใช้งานตัววนซ้ำที่ไม่ถูกต้อง แปลง std::list เป็น std::vector และอธิบายข้อแลกเปลี่ยนด้านความซับซ้อน พวกมันช่วยเร่งการเรียนรู้ STL แม้ว่าทุกคำแนะนำยังคงต้องได้รับการตรวจสอบอยู่ดี

สรุปโพสต์นี้ด้วย: