Day 26 C++ list container (linked list)

basic concept of list

definition

A linked list (list) is a discontinuous storage structure on a physical storage unit, which can store data in a chain , and the logical order of data elements is realized through pointer links in the linked list . The linked list in STL is a doubly circular linked list.

structure

Composition of linked list: A linked list consists of a series of node groups.

The composition of the node: one is a data field that stores data elements, and the other is a pointer field that stores the address of the next node
[External link picture transfer failed, the source site may have an anti-leeching mechanism, it is recommended to save the picture and upload it directly (img-P6joB49u-1691658357899)(assets/clip_image002-1547608564071.jpg)]

bidirectional iterator

Since the storage method of the linked list is not a continuous memory space, the iterator in the linked list list only supports forward and backward movement , which is a bidirectional iterator

advantage

  • Using dynamic storage allocation, will not cause memory waste and overflow
  • The linked list is very convenient to perform insertion and deletion operations, just modify the pointer without moving a large number of elements

shortcoming

  • The linked list is flexible, but the space (pointer field) and time (traversal) are extra

> The linked list needs to store additional pointer fields in each node, which will consume more memory space. In addition, since the linked list is stored non-sequentially, accessing elements at a specific position requires sequentially traversing the linked list from the beginning or the end, and the time complexity is O(n), which is less efficient than the constant time access of vectors O(1).

The difference between List and vector

storage structure

list is a doubly linked list, each node contains a value and forward and backward pointers;
and vector is a dynamic array that uses continuous memory blocks to store elements.

memory management

Since list uses dynamic memory allocation, elements can be efficiently inserted and deleted at any position, but additional pointer overhead will be generated at the same time; while
vector uses continuous memory blocks, although insertion and deletion operations need to move elements, the memory access location is more continuous , which can provide better cache performance.

Iterator stability

In a list, inserting or deleting elements will not affect the validity of existing iterators;
while in a vector, when inserting or deleting elements, it may cause the iterator to become invalid and need to be reacquired.

random access efficiency

Because vector uses continuous memory blocks, elements can be randomly accessed through indexing, and has better performance;
while list needs to traverse the linked list in order from the beginning or from the end in order to access the elements at the specified position, which is inefficient.

In scenarios where insertion and deletion are frequent, list may be more suitable;
and in cases where fast random access to elements is required or the container size is large, vector may be more suitable.

list constructor - create a list container

function prototype

  • list<T> lst;//list is implemented using a template class, and the default construction form of the object:
  • list(beg,end);//The constructor copies the elements in the range [beg, end) to itself.
  • list(n,elem);//The constructor copies n elems to itself.
  • list(const list &lst);// Copy constructor.
    The list construction method is consistent with several other STL commonly used containers

example

#include <list>
#include <iostream>
#include <list>

int main() {
    // 示例1:使用默认构造函数创建空的list
    std::list<int> myList1;

    // 示例2:使用迭代器构造函数将数组中的元素拷贝到list中
    int arr[] = {1, 2, 3, 4, 5};
    std::list<int> myList2(arr, arr + sizeof(arr) / sizeof(int));

    // 示例3:使用元素数量和元素值构造list
    std::list<int> myList3(3, 10); // 包含三个值为10的元素

    // 示例4:使用拷贝构造函数创建一个副本
    std::list<int> myList4(myList3);

    // 输出list中的元素
    std::cout << "myList1: ";
    for (const auto& element : myList1) {
        std::cout << element << " ";
    }
    std::cout << std::endl;

    std::cout << "myList2: ";
    for (const auto& element : myList2) {
        std::cout << element << " ";
    }
    std::cout << std::endl;

    std::cout << "myList3: ";
    for (const auto& element : myList3) {
        std::cout << element << " ";
    }
    std::cout << std::endl;

    std::cout << "myList4: ";
    for (const auto& element : myList4) {
        std::cout << element << " ";
    }
    std::cout << std::endl;

    return 0;
}

output
myList1:
myList2: 1 2 3 4 5
myList3: 10 10
10 myList4: 10 10 10

list assignment and exchange

function prototype

  • assign(beg, end);//Assign the copy of the data in the interval [beg, end) to itself.
  • assign(n, elem);// Assign n elem copies to itself.
  • list& operator=(const list &lst);// Overload the equals operator
  • swap(lst);//Swap lst with its own elements .

Example:

#include <iostream>
#include <list>

int main() {
    std::list<int> myList1 = {1, 2, 3};
    std::list<int> myList2 = {4, 5, 6};

    std::cout << "Before swap:" << std::endl;
    std::cout << "myList1: ";
    for (const auto& element : myList1) {
        std::cout << element << " ";
    }
    std::cout << std::endl;

    std::cout << "myList2: ";
    for (const auto& element : myList2) {
        std::cout << element << " ";
    }
    std::cout << std::endl;

    // 使用swap函数交换两个list的元素
    myList1.swap(myList2);

    std::cout << "After swap:" << std::endl;
    std::cout << "myList1: ";
    for (const auto& element : myList1) {
        std::cout << element << " ";
    }
    std::cout << std::endl;

    std::cout << "myList2: ";
    for (const auto& element : myList2) {
        std::cout << element << " ";
    }
    std::cout << std::endl;

    return 0;
}

Output
Before swap:
myList1: 1 2 3
myList2: 4 5 6
After swap:
myList1: 4 5 6
myList2: 1 2 3

list size operations

function prototype

  • size(); //returns the number of elements in the container

  • empty(); // Check if the container is empty

  • resize(num);//Respecify the length of the container as num, if the container becomes longer, fill the new position with the default value 0.

    ​// If the container becomes shorter, the elements at the end that exceed the length of the container are deleted.

  • resize(num, elem); //Respecify the length of the container as num. If the container becomes longer, fill the new position with the value of elem.

    // If the container gets shorter, elements at the end that exceed the length of the container are removed.

example

#include <iostream>
#include <list>

int main() {
    std::list<int> myList = {1, 2, 3, 4, 5};

    // 使用size函数获取容器中元素的个数
    std::cout << "Size of myList: " << myList.size() << std::endl;

    // 使用empty函数判断容器是否为空
    if (myList.empty()) {
        std::cout << "myList is empty." << std::endl;
    } else {
        std::cout << "myList is not empty." << std::endl;
    }

    // 使用resize函数改变容器的长度为7,默认填充0
    myList.resize(7);
    std::cout << "myList after resize to size 7 with default value:" << std::endl;
    for (const auto& element : myList) {
        std::cout << element << " ";
    }
    std::cout << std::endl;

    // 使用resize函数改变容器的长度为10,使用值9填充新位置
    myList.resize(10, 9);
    std::cout << "myList after resize to size 10 with value 9:" << std::endl;
    for (const auto& element : myList) {
        std::cout << element << " ";
    }
    std::cout << std::endl;

    // 使用resize函数将容器的长度改为3
    myList.resize(3);
    std::cout << "myList after resize to size 3:" << std::endl;
    for (const auto& element : myList) {
        std::cout << element << " ";
    }
    std::cout << std::endl;

    return 0;
}

输出
Size of myList: 5
myList is not empty.
myList after resize to size 7 with default value:
1 2 3 4 5 0 0
myList after resize to size 10 with value 9:
1 2 3 4 5 0 0 9 9 9
myList after resize to size 3:
1 2 3

In the example, we create a list object myList and initialize its elements.
Then, we use the size function to output the size of the container, and the empty function to determine whether the container is empty.
Next, we use the resize function to change the size of the container to 7, 10, and 3, respectively.
When the container becomes larger, the new position will be filled with the default value of 0 or the specified value of 9;
when the container is smaller, elements at the end that exceed the length of the container will be deleted. Finally, we print out the modified container contents

list insertion and deletion

function prototype

  • push_back(elem);//Add an element at the end of the container
  • pop_back();//Delete the last element in the container
  • push_front(elem);//Insert an element at the beginning of the container
  • pop_front();//Remove the first element from the beginning of the container
  • insert(pos,elem);//Insert a copy of the elem element at the pos position, and return the position of the new data.
  • insert(pos,n,elem);//Insert n elem data at position pos, no return value.
  • insert(pos,beg,end);//Insert the data in [beg,end) range at position pos, no return value.
  • clear() ;//Remove all data in the container
  • erase(beg,end);//Delete the data in [beg,end) range, and return the position of the next data.
  • erase(pos);//Delete the data at position pos and return the position of the next data.
  • remove(elem);//Delete all elements in the container that match the value of elem.

Note that
inserting multiple data has no return value , deleting and returning the next data position
beg end are all iterators

example

#include <iostream>
#include <list>

int main() {
    std::list<int> myList = {1, 2, 3, 4, 5};

    std::cout << "Initial myList:" << std::endl;
    for (const auto& element : myList) {
        std::cout << element << " ";
    }
    std::cout << std::endl;

    // 在容器尾部加入一个元素
    myList.push_back(6);

    // 删除容器中最后一个元素
    myList.pop_back();

    // 在容器开头插入一个元素
    myList.push_front(0);

    // 从容器开头移除第一个元素
    myList.pop_front();

    // 在指定位置插入元素的拷贝
    auto it = myList.begin();
    std::advance(it, 2);
    myList.insert(it, 7);

    // 在指定位置插入多个相同元素
    it = myList.begin();
    std::advance(it, 3);
    myList.insert(it, 3, 8);

    // 在指定位置插入另一个区间的元素
    std::list<int> newElements = {9, 10};
    it = myList.begin();
    std::advance(it, 4);
    myList.insert(it, newElements.begin(), newElements.end());

    std::cout << "myList after modifications:" << std::endl;
    for (const auto& element : myList) {
        std::cout << element << " ";
    }
    std::cout << std::endl;

    // 移除容器的所有数据
    myList.clear();

    std::cout << "myList after clear:" << std::endl;
    if (myList.empty()) {
        std::cout << "myList is empty." << std::endl;
    } else {
        std::cout << "myList is not empty." << std::endl;
    }

    return 0;
}

list data access

function prototype

  • front();// Return the first element.
  • back();// Return the last element.

Notice

The iterator of the list container is a bidirectional iterator, which does not support random access, has no index, cannot skip access, and cannot access data through [] or at

example

#include <list>

//数据存取
void test01()
{
	list<int>L1;
	L1.push_back(10);
	L1.push_back(20);
	L1.push_back(30);
	L1.push_back(40);

	
	//cout << L1.at(0) << endl;//错误 不支持at访问数据
	//cout << L1[0] << endl; //错误  不支持[]方式访问数据
	cout << "第一个元素为: " << L1.front() << endl;
	cout << "最后一个元素为: " << L1.back() << endl;

	//list容器的迭代器是双向迭代器,不支持随机访问
	list<int>::iterator it = L1.begin();
	//it = it + 1;//错误,不可以跳跃访问,即使是+1
}

int main() {

	test01();

	system("pause");

	return 0;
}

list reverse and sort

function prototype

  • reverse();// reverse linked list
  • sort();//List sorting//The default sorting rules are ascending from small to large

example

#include <iostream>
#include <list>

int main() {
    std::list<int> myList = {5, 2, 4, 1, 3};

    // 反转链表
    myList.reverse();
    std::cout << "Reversed list: ";
    for (const auto& element : myList) {
        std::cout << element << " ";
    }
    std::cout << std::endl;

    // 链表排序
    myList.sort();
    std::cout << "Sorted list: ";
    for (const auto& element : myList) {
        std::cout << element << " ";
    }
    std::cout << std::endl;

    return 0;
}

Output
Reversed list: 3 1 4 2 5
Sorted list: 1 2 3 4 5

Advanced Sorting - One more logical rule-making on sorting rules

  • For custom data types, the collation must be specified, otherwise the compiler does not know how to sort
  • Advanced sorting is just another logical rule formulation on the sorting rules, which is not complicated
#include <iostream>
#include <vector>
#include <algorithm>

struct Student {
    std::string name;
    int age;
    int score;
};

bool compareStudents(const Student& student1, const Student& student2) {
    // 先按分数降序排序
    if (student1.score != student2.score) {
        return student1.score > student2.score;
    }

    // 如果分数相同,则按年龄升序排序
    if (student1.age != student2.age) {
        return student1.age < student2.age;
    }

    // 如果分数和年龄都相同,则按姓名的字典序升序排序
    return student1.name < student2.name;
}

int main() {
    // 创建学生对象
    std::vector<Student> students = {
   
   {"Alice", 20, 90}, {"Bob", 18, 85}, {"Charlie", 19, 90}};

    // 使用自定义的排序规则对学生进行排序
    std::sort(students.begin(), students.end(), compareStudents);

    // 输出排序结果
    for (const auto& student : students) {
        std::cout << "Name: " << student.name << ", Age: " << student.age << ", Score: " << student.score << std::endl;
    }

    return 0;
}

First, we define a struct named Student that contains the student's name, age, and score.
Next, we implemented a custom comparison function called compareStudents.
The function sorts the students based on their score, age, and name.
First sort by score in descending order, if the scores are the same, sort by age in ascending order, and finally sort by name in ascending order lexicographically.
In the main function, we create a vector of student objects and initialize several student objects.
Then, we use the std::sort function to sort the student objects, passing in a custom comparison function as an argument.
Finally, we loop through the sorted student objects and output the name, age, and score.

Guess you like

Origin blog.csdn.net/m0_74921567/article/details/132214060