STL之list容器代码详解

1 基础概念

在这里插入图片描述

  • 功能: 将数据进行链式存储

链表(list)是一种物理存储单元上非连续的存储结构,数据元素的逻辑顺序是通过链表中的指针链接实现的

链表的组成:链表由一系列结点组成。

结点的组成:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域

在这里插入图片描述

STL中的链表是一个双向循环链表
在这里插入图片描述

由于链表的存储方式并不是连续的内存空间,因此链表list中的迭代器只支持前移和后移,属于双向迭代器

list的优点:

采用动态存储分配,不会造成内存浪费和溢出,用多少开多少空间,不像vector那样预留很多空间;

链表执行插入和删除操作十分方便,修改指针即可,不需要移动大量元素。

list的缺点:

链表灵活,但是空间(指针域) 和 时间(遍历)额外耗费较大;

List有一个重要的性质,插入操作和删除操作都不会造成原有list迭代器的失效,这在vector是不成立的。

2 代码解释

Talk is cheap, show me the code.

#include<iostream>
using namespace std;
#include<list>void printList(const list<int>& ll)
{for (list<int>::const_iterator it = ll.begin(); it != ll.end(); it++){cout << *it << " ";}cout << endl;
}/*
构造函数原型:
list<T> lst; //list采用采用模板类实现,对象的默认构造形式:
list(beg,end); //构造函数将[beg, end)区间中的元素拷贝给本身。
list(n,elem); //构造函数将n个elem拷贝给本身。
list(const list &lst); //拷贝构造函数。
*/void test01()
{list<int> l1;l1.push_back(10);l1.push_back(20);l1.push_back(30);l1.push_back(40);printList(l1);list<int> l2(l1.begin(), l1.end());printList(l2);list<int> l3(10, 88);printList(l3);list<int> l4(l3);printList(l4);
}/*
赋值和交换函数原型:
assign(beg, end); //将[beg, end)区间中的数据拷贝赋值给本身。
assign(n, elem); //将n个elem拷贝赋值给本身。
list& operator=(const list &lst); //重载等号操作符
swap(lst); //将lst与本身的元素互换。
*/void test02()
{list<int> l1;l1.push_back(10);l1.push_back(20);l1.push_back(30);l1.push_back(40);printList(l1);list<int> l2;l2.assign(l1.begin(), l1.end());printList(l2);list<int> l3;l3.assign(10, 88);printList(l3);list<int> l4;l4 = l3;printList(l4);//swap 需不需要元素个数相等l4.swap(l1);printList(l1);printList(l4);
}/*
大小操作函数原型:
size(); //返回容器中元素的个数
empty(); //判断容器是否为空
resize(num); //重新指定容器的长度为num,若容器变长,则以默认值填充新位置。
//如果容器变短,则末尾超出容器长度的元素被删除。
resize(num, elem); //重新指定容器的长度为num,若容器变长,则以elem值填充新位置。
//如果容器变短,则末尾超出容器长度的元素被删除。
*/void test03()
{list<int> l1;l1.push_back(10);l1.push_back(20);l1.push_back(30);l1.push_back(40);printList(l1);if (l1.empty()){cout << "Empty" << endl;}else{cout << "the size of l1: " << l1.size() << endl;}l1.resize(10, 1000);printList(l1);l1.resize(4);printList(l1);
}/*
插入和删除函数原型:
push_back(elem);//在容器尾部加入一个元素
pop_back();//删除容器中最后一个元素
push_front(elem);//在容器开头插入一个元素
pop_front();//从容器开头移除第一个元素
insert(pos,elem);//在pos位置插elem元素的拷贝,返回新数据的位置。
insert(pos,n,elem);//在pos位置插入n个elem数据,无返回值。
insert(pos,beg,end);//在pos位置插入[beg,end)区间的数据,无返回值。
clear();//移除容器的所有数据
erase(beg,end);//删除[beg,end)区间的数据,返回下一个数据的位置。
erase(pos);//删除pos位置的数据,返回下一个数据的位置。
remove(elem);//删除容器中所有与elem值匹配的元素。
*/void test04()
{list<int> l1;l1.push_back(100); //100l1.push_front(200); //200 100l1.push_front(300); //300 200 100l1.push_front(400); //400 300 200 100l1.pop_back(); //400 300 200l1.pop_front(); //300 200printList(l1);list<int>::iterator it = l1.begin();cout << "*it=" << *it << endl;l1.insert(++it, 1000); //300 1000 200printList(l1);cout << "*it=" << *it << endl; //特别注意这个时候迭代器的位置!!!l1.insert(++it, 3, 33); //300 1000 200 33 33 33printList(l1);list<int> l2(l1);l2.insert(l2.begin(), l1.begin(), l1.end());printList(l2); //300 1000 200 33 33 33  300 1000 200 33 33 33it = l1.begin();cout << "*it=" << *it << endl;printList(l1);//l1.erase(it, ++it); //注意并没有这个操作!!!!!正确的应该只有起始迭代器到结束迭代器的操作printList(l1);l1.erase(++it);printList(l1); //33 33 200}/*
数据存取函数原型:
//移除 L.push_back(10000); L.push_back(10000); L.push_back(10000); printList(L); L.remove(10000); printList(L); //清空 L.clear(); printList(L); }int main() { test01(); system("pause"); return 0; } 43444546474849505152535455565758596061626364
front(); //返回第一个元素。
back(); //返回最后一个元素。
*/void test05()
{list<int> l1;l1.push_back(100); //100l1.push_front(200); //200 100l1.push_front(300); //300 200 100l1.push_front(400); //400 300 200 100cout << l1.front() << endl;cout << l1.back() << endl;
}/*
反转和排序函数原型:
reverse(); //反转链表
sort(); //链表排序
*/
bool myCompare(int a, int b)
{return a > b;
}void test06()
{list<int> l1;l1.push_back(123210); //100l1.push_front(2422410); //200 100l1.push_front(343430); //300 200 100l1.push_front(403242130); //400 300 200 100printList(l1);l1.reverse();printList(l1);l1.sort();printList(l1);l1.sort(myCompare);printList(l1);}/*
排序案例
案例描述:将Person自定义数据类型进行排序,Person中属性有姓名、年龄、身高
排序规则:按照年龄进行升序,如果年龄相同按照身高进行降序
*/
class Person
{
public:Person(string name, int age, int height){this->name = name;this->age = age;this->height = height;}string name;int age;int height;
};bool comparePerson(const Person&p1,const Person&p2)
{if (p1.age == p2.age){return p1.height > p2.height;}else{return p1.age < p2.age;}
}void printPersonInfo(const list<Person>& ll)
{for (list<Person>::const_iterator it = ll.begin(); it != ll.end(); it++){cout << (*it).name << " " << (*it).age << " " << (*it).height << endl;}
}void test07()
{Person p1("zhao",99,170);Person p2("qian", 89, 190);Person p3("sun", 79, 200);Person p4("li", 89, 180);Person p5("zhou", 109, 210);list<Person> p;p.push_back(p1);p.push_back(p2);p.push_back(p3);p.push_back(p4);p.push_back(p5);printPersonInfo(p);p.sort(comparePerson);printPersonInfo(p);}int main()
{test07();system("pause");return 0;
}

3 应用场景

C++的STL(Standard Template Library)中的list容器是一个双向链表,它提供了在两端进行高效插入和删除操作的能力。在实际项目中,list容器常常用于以下一些应用场景:

  1. 高效的插入和删除操作: 由于list是一个双向链表,插入和删除元素的时间复杂度是O(1),这使得它在需要频繁插入或删除元素的场景中非常高效。例如,某些算法需要在中间插入或删除元素时,list相比于数组或向量更具优势。

    #include <list>
    #include <iostream>int main() {std::list<int> myList;myList.push_back(1);myList.push_back(2);myList.push_back(3);// 在第二个位置插入元素auto it = std::next(myList.begin());myList.insert(it, 4);// 删除第一个元素myList.pop_front();// 输出:2 4 3for (const auto &element : myList) {std::cout << element << " ";}return 0;
    }
  2. 不需要随机访问的情况: list不支持通过索引直接访问元素,但在某些情况下,我们并不需要随机访问,而是更关注在序列中进行插入和删除操作的性能。

    #include <list>
    #include <algorithm>
    #include <iostream>int main() {std::list<std::string> wordList = {"apple", "banana", "orange", "grape"};// 删除所有长度小于 6 的单词wordList.remove_if([](const std::string &word) {return word.length() < 6;});// 输出:banana orangefor (const auto &word : wordList) {std::cout << word << " ";}return 0;
    }
  3. 避免动态数组重新分配的开销:vector不同,list的元素在内存中不是连续存储的,因此在插入和删除操作时,不需要进行动态数组的重新分配,从而避免了重新分配的开销。

    #include <list>
    #include <iostream>int main() {std::list<int> myList;for (int i = 0; i < 10000; ++i) {// 在列表末尾插入元素,不会导致重新分配myList.push_back(i);}// 执行大量的插入和删除操作而不触发数组重新分配// ...return 0;
    }

这些场景中,list容器的特性使得它成为一个合适的选择。然而,需要注意的是,由于其非连续存储的特性,list在访问元素时性能较差,因此在需要频繁随机访问元素的情况下,可能会考虑其他容器,如vector

4 代码示例

假设你正在开发一个任务调度器,需要存储和管理一系列待执行的任务,并且需要支持高效的插入和删除操作。在这种情况下,使用std::list容器可以提供一些优势。以下是一个简单的示例,演示了如何使用list来管理任务列表:

#include <iostream>
#include <list>
#include <string>// 任务结构体
struct Task {std::string name;int priority;Task(const std::string& taskName, int taskPriority): name(taskName), priority(taskPriority) {}
};// 任务调度器类
class TaskScheduler {
private:std::list<Task> taskList;public:// 添加任务void addTask(const Task& task) {// 在任务列表末尾添加任务taskList.push_back(task);}// 删除指定优先级的所有任务void removeTasksByPriority(int priority) {// 使用 erase-remove 惯用法删除指定优先级的任务taskList.remove_if([priority](const Task& task) {return task.priority == priority;});}// 打印所有任务void printTasks() const {std::cout << "Tasks in the scheduler:\\n";for (const auto& task : taskList) {std::cout << "Name: " << task.name << ", Priority: " << task.priority << "\\n";}}
};int main() {TaskScheduler scheduler;// 添加一些任务scheduler.addTask(Task("TaskA", 2));scheduler.addTask(Task("TaskB", 1));scheduler.addTask(Task("TaskC", 3));scheduler.addTask(Task("TaskD", 2));// 打印所有任务scheduler.printTasks();// 删除优先级为2的任务scheduler.removeTasksByPriority(2);// 打印删除后的任务列表scheduler.printTasks();return 0;
}

在这个示例中,std::list容器使得在任务调度器中添加和删除任务变得简单高效。使用remove_if算法可以方便地删除符合特定条件的任务,而不需要手动管理元素的移动。这样,list容器提供了在实际项目中处理类似场景的便利性和性能优势。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/519869.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

嵌入式学习第二十五天!(网络的概念、UDP编程)

网络&#xff1a; 可以用来&#xff1a;数据传输、数据共享 1. 网络协议模型&#xff1a; 1. OSI协议模型&#xff1a; 应用层实际收发的数据表示层发送的数据是否加密会话层是否建立会话连接传输层数据传输的方式&#xff08;数据包&#xff0c;流式&#xff09;网络层数据的…

HelpLook VS GitBook:知识库优劣详解

在信息爆炸的时代&#xff0c;企业要保持竞争优势&#xff0c;就必须善于管理和利用内部的知识资产。企业知识库作为一种集中存储和共享知识的工具&#xff0c;正在成为现代企业不可或缺的一部分。 HelpLook和Gitbook是提供专业知识库的两个平台&#xff0c;也被大众熟知。它们…

一文读懂HDMI的演变-从HDMI1.0到HDMI2.1(建议收藏)

HDMI&#xff0c;全称为&#xff08;High Definition Multimedia Interface&#xff09;高清多媒体接口&#xff0c;主要用于传输高清音视频信号。 HDMI System HDMI系统包括HDMI的source和HDMI的sink, 其中source 是源端&#xff0c;即信号的来源&#xff1b;Sink的接收端&a…

什么是以人为本的AI?

AI技术蓬勃发展&#xff0c;有望极大改善我们的日常生活。因此&#xff0c;人工智能专家经常围绕在我们社会中利用人工智能的最佳方式展开对话&#xff0c;并由此得出了以人为中心的AI方法。以人为中心的AI意为不是用机器代替人类&#xff0c;而是用机器来优化人类的体验。 在…

乌鸡的身高

解法&#xff1a; 只需要看身高最高的乌鸡个数是否>2.若满足则除去当前这只乌鸡的最高身高都是最高身高。 若不满足则只需要看最高的和第二高的乌鸡。 #include<iostream> #include<vector> #include<algorithm> #include<cmath> using namespac…

图像检索与识别——词袋模型(Bag-of-features models)

一、定义 计算机视觉单词袋是一种描述计算图像之间相似度的技术&#xff0c;常用于用于图像分类当中。该方法起源于文本检索&#xff08;信息检索&#xff09;&#xff0c;是对NLP“单词袋”算法的扩展。在“单词袋”中&#xff0c;我们扫描整个文档&#xff0c;并保留文档中出…

STL中push_back和emplace_back效率的对比

文章目录 过程对比1.通过构造参数向vector中插入对象&#xff08;emplace_back更高效&#xff09;2.通过插入实例对象&#xff08;调用copy函数&#xff09;3.通过插入临时对象&#xff08;调用move函数&#xff09; 效率对比emplace_back 的缺点 我们以STL中的vector容器为例。…

LeetCode 刷题 [C++] 第300题.最长递增子序列

题目描述 给你一个整数数组 nums &#xff0c;找到其中最长严格递增子序列的长度。 子序列 是由数组派生而来的序列&#xff0c;删除&#xff08;或不删除&#xff09;数组中的元素而不改变其余元素的顺序。例如&#xff0c;[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。 题目…

Leetcode 206. 反转链表

给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5] 输出&#xff1a;[5,4,3,2,1] 示例 2&#xff1a; 输入&#xff1a;head [1,2] 输出&#xff1a;[2,1] 示例 3&#xff1a; 输…

forwardRef和useImperativeHandle的配合使用

通过ref调用子组件里的方法 useImperativeHandle往外暴露子组件的方法&#xff0c;注意useImperativeHandle第三个参数中括号依赖&#xff0c;有这个依赖&#xff0c;childFun才能获取到最新的值

计算机基础专升本笔记十四-计算机网络基础(一)

计算机基础专升本笔记十四-计算机网络基础&#xff08;一&#xff09; 一、计算机网络的发展历程 第一代计算机网络&#xff08;数据通信&#xff09; 以数据通信为主的第一代计算机网络。主要是指美国军方用于防控系统的一种联机系统。它只是计算机网络的雏形。 第二代计算…

【思考】crud接口命名规范

写代码时取名字真的是痛苦。 方法命名 1、阿里命名规范 Service/Dao 层命名规约 1&#xff09; 获取单个对象的方法用 get 做前缀。 2&#xff09; 获取多个对象的方法用 list 做前缀。 3&#xff09; 获取统计值的方法用 count 做前缀。 4&#xff09; 插入的方法用 save&am…