【C++】list 类深度解析:探索双向链表的奇妙世界
🌟快来参与讨论💬,点赞👍、收藏⭐、分享📤,共创活力社区。 🌟
如果你对string,vector还存在疑惑,欢迎阅读我之前的作品 :
之前文章🔥🔥🔥 【C++】vector 类深度解析:探索动态数组的奥秘
🔥🔥🔥 【C++】string 类深度解析:探秘字符串操作的核心
目录
💯前言
💯为什么要学习 list 类
(一)高效的插入和删除操作
(二)灵活的内存管理
(三)迭代器稳定性
💯标准库中的 list 类
(一)定义与头文件📄
💯list 类的内部结构🧐
(一)节点结构
(二)内存布局
💯list 类的构造函数🚀
(一)默认构造函数
(二)带参数的构造函数
💯list 类的成员函数
(一)获取链表信息的函数📈
(二)链表修改函数✍️
(三)获取特定元素的函数🔍
(四)链表查找函数🔎
💯list 类的操作符重载🎯
(一)赋值操作符(=)
(二)下标操作符([])
💯总结
💯前言
嘿😎,小伙伴们!在 C++ 编程的奇妙旅程中,我们经常会碰到各种数据结构的难题🧐。今天,咱们就来好好研究一下 C++ 标准库中的 list 类,它就像一个超级厉害的双向链表小助手,能帮我们轻松搞定很多复杂的任务哦😜!当我们需要频繁地在数据序列中插入和删除元素时,list 类可是个绝佳的选择呢🤩!它比传统数组灵活多啦,不会因为插入或删除一个元素就把其他元素弄得乱七八糟😏。想知道它为什么这么神奇吗🧐?那就接着往下看吧😎!
相关资料👉list文档介绍
💯为什么要学习 list 类
(一)高效的插入和删除操作
想象一下🤔,你有一排小玩偶🧸,想要在中间插入一个新的玩偶或者拿走一个玩偶。如果它们是像传统数组那样紧紧挨在一起排列的,每次插入或拿走一个玩偶,都得把后面的玩偶一个一个地挪动位置,是不是超级麻烦😫?但是,如果这些玩偶是用一种特殊的方式连接起来的,就像 list 类中的双向链表一样,每个玩偶都有两条 “小绳子”🧵,一条连着前面的玩偶,一条连着后面的玩偶。当你要插入或拿走一个玩偶时,只需要解开和系上相应的 “小绳子” 就可以啦,是不是简单多了😃?这就是 list 类在插入和删除元素时的厉害之处哦!无论在链表的哪个位置进行操作,它都能快速完成,时间复杂度是常数级别呢😎!对于那些经常需要修改数据顺序的程序来说,它简直就是一个神器👏!
(二)灵活的内存管理
list 类的内存管理就像搭积木一样有趣又灵活😄!每个元素就像是一块单独的积木🧱,它们不需要紧紧地靠在一起,可以分散在内存的各个地方。当你需要添加或删除一个元素时,就像拿走或添加一块积木,不会影响其他积木的位置哦😏。这种方式避免了因为内存碎片而导致的性能问题,能让你的程序更好地利用内存资源,是不是很棒呢😎?
(三)迭代器稳定性
迭代器就像是一个小小的导航员🧭,用来在 list 中逐个访问元素。在 list 中,当你插入或删除一个元素时,除了指向被操作元素的迭代器会失效(因为这个元素被改变了嘛😉),其他迭代器就像坚固的小灯塔一样,依然稳稳地指向它们原来的元素哦😎!这在我们需要一边遍历 list,一边进行插入或删除操作时特别有用,不用担心因为操作一个元素而导致其他元素找不到了,程序可以稳稳地运行下去,是不是很让人安心呢😃?
💯标准库中的 list 类
(一)定义与头文件📄
list 类住在 C++ 标准库的<list>
头文件里哦🧐。就像我们要去一个神秘的地方需要知道地址一样,想要使用 list 类,就得先包含这个头文件,然后加上using namespace std;
这句话,这样我们就能在程序里使用 list 类提供的各种神奇功能啦😎!
#include <iostream>
#include <list>
using namespace std;
💯list 类的内部结构🧐
(一)节点结构
list 类内部是由一个个节点组成的,每个节点就像一个小小的魔法盒子🎁,里面装着三个重要的东西呢😎!首先是真正的数据元素,就像魔法盒子里的宝藏一样🧳;然后是一个指向前面节点的指针,就像魔法盒子前面的小钩子🪝,可以勾住前面的节点;还有一个指向后面节点的指针,就像魔法盒子后面的小尾巴🐈,可以连接后面的节点。通过这些指针,节点们就像手拉手的好朋友一样,形成了一个双向链表,我们可以从前往后或者从后往前遍历哦,是不是很有趣😃?
(二)内存布局
节点们在内存里就像一群自由自在的小精灵🧚♂️🧚♀️,它们不需要站在连续的位置上。每个节点根据自己的需要在内存中找到一个合适的地方安家🏠。当我们要插入或删除一个节点时,就像小精灵飞进或飞出它们的群体一样,只需要调整相关节点的指针,不需要像数组那样大规模地移动元素,所以操作起来速度很快哦😎!
💯list 类的构造函数🚀
(一)默认构造函数
list 类的默认构造函数就像一个神奇的魔法咒语✨,念一下就能创建一个空的 list 对象,这个时候 list 里面什么元素都没有,就像一个空的魔法盒子一样🎁。例如:
(二)带参数的构造函数
使用迭代器区间构造
我们还可以用迭代器区间来创建 list 对象哦😉!比如说,我们有一个其他容器(像 vector)里的一段元素,想把它们放到 list 里,就可以用迭代器指定这个区间,然后 list 就会把这段区间里的元素一个一个复制过来,就像把一群小蚂蚁从一个地方搬到另一个地方一样🐜。例如:
#include <iostream>
#include <list>
#include <vector>
using namespace std;
int main() {
vector<int> v = {1, 2, 3, 4, 5};
list<int> l(v.begin(), v.end());
cout << "使用迭代器区间构造的list对象的大小为: " << l.size() << endl;
return 0;
}
指定数量和初始值构造
另外,我们也可以指定要创建的 list 对象里元素的数量,让它们都用默认值初始化,或者直接指定数量和初始值😎。就像我们要订做一批小蛋糕🎂,可以告诉面包师做几个,是都做成普通口味(默认值),还是都做成巧克力口味(指定初始值)。例如:
#include <iostream>
#include <list>
using namespace std;
int main() {
list<int> l1(5); // 创建包含5个默认初始化元素的list
cout << "使用指定数量默认初始化构造的list对象l1的大小为: " << l1.size() << endl;
list<int> l2(3, 10); // 创建包含3个值为10的元素的list
cout << "使用指定数量和初始值构造的list对象l2的大小为: " << l2.size() << endl;
return 0;
}
💯list 类的成员函数
(一)获取链表信息的函数📈
size()
函数size()
函数就像一个小计数器🧮,它能准确地返回 list 中当前元素的个数😃。例如:#include <iostream> #include <list> using namespace std; int main() { list<int> l = {1, 2, 3, 4, 5}; cout << "list中的元素个数为: " << l.size() << endl; return 0; }
empty()
函数empty()
函数则像一个小检查员👀,用来检测 list 是否为空。如果 list 里面没有元素,它就会返回true
,就像告诉你 “这个盒子是空的哦”😏;如果有元素,就返回false
。例如:#include <iostream> #include <list> using namespace std; int main() { list<int> l1; cout << "list1是否为空: " << (l1.empty() ? "是" : "否") << endl; list<int> l2 = { 1 }; cout << "list2是否为空: " << (l2.empty() ? "是" : "否") << endl; return 0; }
(二)链表修改函数✍️
push_front()
函数push_front()
函数就像一个热情的小主人,总是在 list 的开头添加新元素😎。例如:#include <iostream> #include <list> using namespace std; int main() { list<int> l; l.push_front(5); l.push_front(3); l.push_front(1); for (int i : l) { cout << i << " "; } cout << endl; return 0; }
push_back()
函数push_back()
函数则像一个贴心的小尾巴,在 list 的末尾添加元素😄。例如:#include <iostream> #include <list> using namespace std; int main() { list<int> l; l.push_back(1); l.push_back(3); l.push_back(5); for (int i : l) { cout << i << " "; } cout << endl; return 0; }
pop_front()
函数pop_front()
函数就像一个勇敢的小先锋,从 list 的开头删除元素😉。例如:#include <iostream> #include <list> using namespace std; int main() { list<int> l = {1, 2, 3, 4, 5}; l.pop_front(); for (int i : l) { cout << i << " "; } cout << endl; return 0; }
pop_back()
函数pop_back()
函数像一个安静的小后卫,从 list 的末尾删除元素😎。例如:#include <iostream> #include <list> using namespace std; int main() { list<int> l = {1, 2, 3, 4, 5}; l.pop_back(); for (int i : l) { cout << i << " "; } cout << endl; return 0; }
insert()
函数insert()
函数是一个多功能的小工匠🧑🔧,它可以在指定位置插入一个或多个元素😉。例如:#include <iostream> #include <list> using namespace std; int main() { list<int> l = {1, 3, 4, 5}; auto it = l.begin(); ++it; l.insert(it, 2); for (int i : l) { cout << i << " "; } cout << endl; return 0; }
erase()
函数erase()
函数像一个精准的小剪刀✂️,可以删除 list 中的指定元素或元素区间😎。例如:#include <iostream> #include <list> using namespace std; int main() { list<int> l = {1, 2, 3, 4, 5}; auto it = l.begin(); ++it; l.erase(it); for (int i : l) { cout << i << " "; } cout << endl; return 0; }
(三)获取特定元素的函数🔍
front()
函数front()
函数就像一个小探险家🧗♂️,总是能快速找到 list 中的第一个元素并返回它😎。例如:#include <iostream> #include <list> using namespace std; int main() { list<int> l = {1, 2, 3, 4, 5}; cout << "list中的第一个元素为: " << l.front() << endl; return 0; }
back()
函数back()
函数则像一个小后卫👮♂️,负责返回 list 中的最后一个元素😉。例如:#include <iostream> #include <list> using namespace std; int main() { list<int> l = {1, 2, 3, 4, 5}; cout << "list中的最后一个元素为: " << l.back() << endl; return 0; }
(四)链表查找函数🔎
在 list 中查找元素可以使用find()
算法结合 list 的迭代器。就像在一个宝藏迷宫里寻找特定的宝物一样🧳,我们可以通过迭代器逐个比较元素来找到我们想要的元素😉。例如:
#include <iostream>
#include <list>
#include <algorithm>
using namespace std;
int main() {
list<int> l = {1, 2, 3, 4, 5};
auto it = find(l.begin(), l.end(), 3);
if (it!= l.end()) {
cout << "找到元素的位置为: " << distance(l.begin(), it) << endl;
} else {
cout << "未找到元素" << endl;
}
return 0;
}
💯list 类的操作符重载🎯
(一)赋值操作符(=)
list 类重载了赋值操作符,就像一个神奇的复制魔法🪄,可以将一个 list 对象的值复制给另一个 list 对象😎。例如:
#include <iostream>
#include <list>
using namespace std;
int main() {
list<int> l1 = {1, 2, 3};
list<int> l2;
l2 = l1;
for (int i : l2) {
cout << i << " ";
}
cout << endl;
return 0;
}
(二)下标操作符([])
虽然 list 类本身没有像 vector 那样直接支持下标操作符重载,但我们可以通过迭代器来模拟类似的功能😉。例如:
#include <iostream>
#include <list>
using namespace std;
int main() {
list<int> l = {1, 2, 3, 4, 5};
auto it = l.begin();
advance(it, 2); // 将迭代器移动到第三个元素位置
cout << "第三个元素的值为: " << *it << endl;
return 0;
}
💯总结
哇哦😎!C++ 的 list 类真的是一个非常强大且有趣的双向链表工具呢👏!它的内部结构、构造函数、成员函数和操作符重载等特性,让我们在处理动态数据序列时更加得心应手😃。通过深入理解 list 类,我们可以更好地应对各种编程挑战,提高程序的灵活性和效率💪。在实际编程中,我们要根据具体需求合理选择使用 list 类,充分发挥它的优势,避免一些常见的错误哦😉。希望小伙伴们在 C++ 的编程世界里继续探索,发现更多的乐趣和惊喜😜!
期待下次与你们分享更多精彩内容哦🤗!欢迎关注我👉【A Charmer】