std::list trong C++ với Ví dụ

⚡ Tóm tắt thông minh

std::list trong C++ Đây là một cấu trúc dữ liệu dạng chuỗi được triển khai dưới dạng danh sách liên kết đôi, cho phép chèn và xóa nhanh chóng tại bất kỳ vị trí nào, đồng thời lưu trữ các phần tử trong bộ nhớ không liền kề và hỗ trợ truy cập tuần tự hai chiều thay vì truy cập ngẫu nhiên.

  • 🔗 Danh sách liên kết đôi: Mỗi phần tử giữ liên kết đến nút trước và nút kế tiếp của nó, do đó dữ liệu std::list nằm trong vùng nhớ không liền kề.
  • Chèn và xóa nhanh: Việc thêm hoặc xóa một phần tử tại một vị trí xác định có thời gian thực hiện không đổi, khác với việc dịch chuyển các phần tử trong một vectơ.
  • 🚫 Không có quyền truy cập ngẫu nhiên: Các phần tử được truy cập bằng cách duyệt tuần tự từ một trong hai đầu, do đó việc lập chỉ mục như danh sách[3] không khả dụng.
  • 🧩 Người xây dựng: Các hàm tạo Default, fill, range, copy, move và initializer-list xây dựng một std::list theo những cách khác nhau.
  • 🛠️ Chức năng của thành viên: Các hàm push_front(), push_back(), insert(), erase(), size(), reverse() và merge() quản lý nội dung của danh sách.
  • 🤖 Hỗ trợ AI: GitHub Copilot và các công cụ hỗ trợ tương tự tạo ra các khai báo std::list, trình lặp và logic chèn hoặc xóa từ một đoạn chú thích ngắn.

std::list trong C++

Danh sách std::là gì?

In C++`std::list` đề cập đến một vùng chứa dữ liệu. `std::list` cho phép bạn chèn và xóa các mục từ bất kỳ đâu. `std::list` được triển khai dưới dạng danh sách liên kết đôi. Điều này có nghĩa là dữ liệu trong danh sách có thể được truy cập theo hai chiều và tuần tự.

Danh sách Thư viện Mẫu Chuẩn không hỗ trợ truy cập ngẫu nhiên nhanh, nhưng hỗ trợ truy cập tuần tự từ mọi hướng.

Bạn có thể phân tán các thành phần danh sách trong các khối bộ nhớ khác nhau. Thông tin cần thiết để truy cập tuần tự vào dữ liệu được lưu trữ trong một thùng chứa. Danh sách std::có thể mở rộng và thu nhỏ từ cả hai đầu nếu cần trong thời gian chạy. Bộ cấp phát nội bộ tự động đáp ứng các yêu cầu lưu trữ.

Những đặc điểm này đặt ra một câu hỏi thực tế: khi nào bạn thực sự nên sử dụng danh sách?

Tại sao nên sử dụng danh sách std::?

Dưới đây là những lý do nên sử dụng std::list:

  • So với các container chứa chuỗi khác như array và vector, std::list hoạt động tốt hơn.
  • Chúng có hiệu suất tốt hơn trong việc chèn, di chuyển và xuất.traccác phần tử từ bất kỳ vị trí nào.
  • Danh sách std::cũng hoạt động tốt hơn với các thuật toán thực hiện các thao tác đó một cách chuyên sâu.

Khi đã hiểu rõ lý do, bước tiếp theo là cú pháp để khai báo nó.

Cú pháp danh sách

Để xác định danh sách std::, chúng ta phải nhập tập tin tiêu đề. Đây là cú pháp định nghĩa std::list:

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

Dưới đây là mô tả về các tham số trên:

  • T – Xác định kiểu dữ liệu của phần tử chứa bên trong. Bạn có thể thay thế T bằng bất kỳ kiểu dữ liệu nào, kể cả các kiểu dữ liệu do người dùng định nghĩa.
  • Alloc – Xác định kiểu của đối tượng cấp phát bộ nhớ. Theo mặc định, nó sử dụng mẫu lớp cấp phát bộ nhớ. Nó phụ thuộc vào giá trị và sử dụng mô hình cấp phát bộ nhớ đơn giản.

Ví dụ 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';
	}
}

Đầu ra:

Kết quả đầu ra của ví dụ tạo và lặp std::list

Đây là ảnh chụp màn hình của mã:

C++ Đoạn mã tạo một std::list và in nó ra bằng vòng lặp for.

Code Giải thích:

  1. Bao gồm tệp tiêu đề thuật toán để sử dụng các chức năng của nó.
  2. Bao gồm tệp tiêu đề iostream để sử dụng các chức năng của nó.
  3. Bao gồm tệp tiêu đề danh sách để sử dụng các chức năng của nó.
  4. Gọi hàm main(). Logic chương trình phải được thêm vào trong phần thân của hàm này.
  5. Tạo một danh sách có tên my_list với bộ 4 số nguyên.
  6. Sử dụng vòng lặp for Để tạo một biến vòng lặp x. Biến này sẽ được sử dụng để lặp qua các phần tử trong danh sách.
  7. In ra các giá trị của danh sách trên bàn điều khiển.
  8. Kết thúc phần thân của vòng lặp for.
  9. Phần cuối của hàm main().

C++ Liệt kê các chức năng

Dưới đây là các hàm std::list phổ biến:

Chức năng Mô tả Chi tiết
chèn() Hàm này chèn một mục mới vào trước vị trí của các điểm lặp.
đẩy_back() Chức năng này thêm một mục mới vào cuối danh sách.
push_front() Nó thêm một mục mới vào đầu danh sách.
pop_front() Nó xóa mục đầu tiên của danh sách.
kích thước() Hàm này xác định số lượng phần tử danh sách.
đằng trước() Để xác định các mục đầu tiên của danh sách.
mặt sau() Để xác định mục cuối cùng của danh sách.
đảo ngược() Nó đảo ngược các mục danh sách.
hợp nhất () Nó hợp nhất hai danh sách được sắp xếp.

nhà xây dựng

Đây là danh sách chức năng được cung cấp bởi tập tin tiêu đề:

  • Hàm tạo mặc định std::list::list()- Nó tạo ra một danh sách trống, không có phần tử nào.
  • Hàm tạo fill std::list::list()- Nó tạo một danh sách có n phần tử và gán giá trị 0 (XNUMX) cho mỗi phần tử.
  • Hàm tạo phạm vi std::list::list()- tạo một danh sách có nhiều phần tử trong phạm vi từ đầu đến cuối.
  • Hàm tạo sao chép std::list::list()- Nó tạo một danh sách với bản sao của từng phần tử có trong danh sách hiện có.
  • Hàm tạo di chuyển std::list::list()- tạo một danh sách với các thành phần của danh sách khác bằng cách sử dụng ngữ nghĩa di chuyển.
  • Trình tạo danh sách khởi tạo std::list::list()-Nó tạo một danh sách với các thành phần của danh sách khác bằng cách sử dụng ngữ nghĩa di chuyển.

Ví dụ 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;
}

Đầu ra:

Kết quả đầu ra của ví dụ về hàm tạo std::list

Đây là ảnh chụp màn hình của mã:

C++ Đoạn mã minh họa các hàm tạo mặc định, phạm vi và di chuyển của std::list.

Code Giải thích:

  1. Bao gồm tệp tiêu đề iostream để sử dụng các chức năng của nó.
  2. Bao gồm tệp tiêu đề danh sách để sử dụng các chức năng của nó.
  3. Bao gồm không gian tên std trong mã để sử dụng các lớp của nó mà không cần gọi nó.
  4. Gọi hàm main(). Logic chương trình phải được thêm vào trong phần thân của hàm này.
  5. Tạo một danh sách trống có tên l.
  6. Tạo một danh sách có tên l1 với bộ 3 số nguyên.
  7. Tạo một danh sách có tên l2 với tất cả các thành phần trong danh sách có tên l1, từ đầu đến cuối.
  8. Tạo một danh sách có tên l3 bằng cách sử dụng ngữ nghĩa di chuyển. Danh sách l3 sẽ có nội dung giống như danh sách l2.
  9. In kích thước của danh sách có tên l trên bảng điều khiển cùng với văn bản khác.
  10. In một số văn bản trên bảng điều khiển.
  11. Tạo một trình lặp có tên là nó và sử dụng nó để lặp qua các phần tử của danh sách có tên là l2.
  12. In các phần tử của danh sách có tên l2 trên bảng điều khiển.
  13. In một số văn bản trên bảng điều khiển.
  14. Tạo một trình lặp có tên là nó và sử dụng nó để lặp qua các phần tử của danh sách có tên là l3.
  15. In các phần tử của danh sách có tên l3 trên bảng điều khiển.
  16. Chương trình phải trả về giá trị sau khi hoàn thành thành công.
  17. Phần cuối của hàm main().

Thuộc tính vùng chứa

Dưới đây là danh sách các thuộc tính vùng chứa:

Bất động sản Mô tả Chi tiết
Trình tự Các vùng chứa trình tự sắp xếp các phần tử của chúng theo một trình tự tuyến tính nghiêm ngặt. Các phần tử được truy cập theo vị trí của chúng trong chuỗi.
Danh sách liên kết đôi Mọi phần tử đều có thông tin về cách xác định vị trí các phần tử trước và phần tử tiếp theo. Điều này cho phép có thời gian liên tục cho các thao tác chèn và xóa.
Nhận biết người phân bổ Một đối tượng cấp phát được sử dụng để sửa đổi kích thước lưu trữ một cách linh hoạt.

Chèn vào danh sách

Có nhiều hàm khác nhau mà chúng ta có thể sử dụng để chèn giá trị vào một danh sách. Hãy cùng xem xét điều này:

Ví dụ 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';
	}
}

Đầu ra:

Kết quả sau khi chèn các phần tử vào std::list

Đây là ảnh chụp màn hình của mã:

C++ Đoạn mã sử dụng push_front, push_back và insert trên một std::list

Code Giải thích:

  1. Bao gồm tệp tiêu đề thuật toán để sử dụng các chức năng của nó.
  2. Bao gồm tệp tiêu đề iostream để sử dụng các chức năng của nó.
  3. Bao gồm tệp tiêu đề danh sách để sử dụng các chức năng của nó.
  4. Gọi hàm main(). Logic chương trình phải được thêm vào trong phần thân của hàm này.
  5. Tạo một danh sách có tên my_list với bộ 4 số nguyên.
  6. Chèn phần tử 11 vào trước danh sách có tên my_list.
  7. Chèn phần tử 18 vào cuối danh sách có tên my_list.
  8. Tạo một trình vòng lặp và sử dụng nó để tìm phần tử 10 từ danh sách my_list.
  9. Sử dụng câu lệnh if để xác định xem phần tử trên có được tìm thấy hay không.
  10. Chèn phần tử 21 trước phần tử trên nếu nó được tìm thấy.
  11. Kết thúc phần thân của câu lệnh if.
  12. Sử dụng vòng lặp for để tạo biến vòng lặp x. Biến này sẽ được sử dụng để lặp qua các thành phần trong danh sách.
  13. In ra các giá trị của danh sách trên bàn điều khiển.
  14. Kết thúc phần thân của vòng lặp for.
  15. Phần cuối của hàm main().

Các phần tử được đưa vào danh sách cũng có thể dễ dàng được loại bỏ.

Xóa khỏi danh sách

Có thể xóa các mục khỏi một danh sách. Hàm erase() cho phép bạn xóa một mục hoặc một phạm vi mục khỏi danh sách.

  • Để xóa một mục, bạn chỉ cần chuyển một vị trí số nguyên. Mục này sẽ bị xóa.
  • Để xóa một vùng nhớ, bạn cần truyền vào con trỏ bắt đầu và con trỏ kết thúc. Chúng ta hãy cùng chứng minh điều này.

Ví dụ 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;
}

Đầu ra:

Kết quả sau khi xóa một phần tử khỏi std::list

Đây là ảnh chụp màn hình của mã:

C++ đoạn mã sử dụng hàm erase trên một std::list

Code Giải thích:

  1. Bao gồm tệp tiêu đề thuật toán để sử dụng các chức năng của nó.
  2. Bao gồm tệp tiêu đề iostream để sử dụng các chức năng của nó.
  3. Bao gồm tệp tiêu đề danh sách để sử dụng các chức năng của nó.
  4. Bao gồm không gian tên std trong chương trình của chúng tôi để sử dụng các lớp của nó mà không cần gọi nó.
  5. Gọi hàm main(). Logic chương trình phải được thêm vào trong phần thân của hàm này.
  6. Tạo một danh sách có tên my_list với bộ 4 số nguyên.
  7. In một số văn bản trên bảng điều khiển.
  8. Sử dụng vòng lặp for để tạo biến vòng lặp x. Biến này sẽ được sử dụng để lặp qua các thành phần trong danh sách.
  9. In ra các giá trị của danh sách trên bàn điều khiển.
  10. Kết thúc phần thân của vòng lặp for.
  11. Tạo một iterator i trỏ đến phần tử đầu tiên của danh sách.
  12. Sử dụng hàm erase() được trỏ bởi iterator i.
  13. In một số văn bản trên bảng điều khiển.
  14. Sử dụng vòng lặp for để tạo biến vòng lặp x. Biến này sẽ được sử dụng để lặp qua các thành phần trong danh sách.
  15. In ra các giá trị của danh sách trên bàn điều khiển. Điều này xảy ra sau khi xóa.
  16. Kết thúc phần thân của vòng lặp for.
  17. Chương trình phải trả về một giá trị sau khi hoàn thành thành công.
  18. Phần cuối của hàm main().

Câu Hỏi Thường Gặp

std::vector lưu trữ các phần tử trong bộ nhớ liền kề với truy cập ngẫu nhiên O(1), trong khi std::list là một danh sách liên kết đôi cung cấp thao tác chèn hoặc xóa O(1) ở bất kỳ đâu. Chọn vector để lập chỉ mục và list để chèn vào giữa thường xuyên.

Không. std::list không có toán tử truy cập ngẫu nhiên, vì vậy list[2] không biên dịch được. Bạn truy cập một phần tử bằng cách lặp từ begin() hoặc end() từng nút một, tốn thời gian tuyến tính O(n) cho một vị trí sâu.

std::list là một danh sách liên kết đôi có thể duyệt theo cả hai chiều và hỗ trợ push_back. std::forward_list là một danh sách liên kết đơn chỉ di chuyển theo chiều tiến, sử dụng ít bộ nhớ hơn cho mỗi nút và không cung cấp size() hoặc trình lặp ngược.

Hãy gọi hàm thành viên my_list.sort(), hàm này chạy trong khoảng N log N và giữ cho các phần tử bằng nhau ổn định. Thuật toán std::sort sẽ không hoạt động vì nó cần các iterator truy cập ngẫu nhiên. Truyền std::greater cho sort() để sắp xếp theo thứ tự giảm dần.

Việc chèn hoặc xóa một nút có thời gian O(1) không đổi sau khi bạn giữ một con trỏ đến vị trí, bởi vì chỉ có các con trỏ lân cận thay đổi. Việc tìm vị trí đó trước bằng cách duyệt vẫn tốn thời gian O(n).

Đúng vậy. std::list không phải là một tập hợp, vì vậy nó lưu trữ các giá trị lặp lại một cách tự do. Mỗi thao tác push_back, push_front hoặc insert đều thêm một nút mới bất kể nội dung hiện có. Hãy sử dụng std::set khi bạn cần loại bỏ các phần tử trùng lặp.

Vâng. Trợ lý GitHub Nó ghi các khai báo std::list, vòng lặp iterator và các lệnh chèn hoặc xóa từ một đoạn chú thích ngắn hoặc tên hàm. Nó thường đề xuất std::vector khi lưu trữ liền kề phù hợp hơn với nhiệm vụ.

Các trợ lý lập trình AI tự động hoàn thành mã container STL, báo lỗi sử dụng iterator, chuyển đổi std::list thành std::vector và giải thích các đánh đổi về độ phức tạp. Chúng giúp tăng tốc quá trình học STL, mặc dù mọi đề xuất vẫn cần được xem xét lại.

Tóm tắt bài viết này với: