当前位置: 首页 > article >正文

【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 类的成员函数

(一)获取链表信息的函数📈

  1. 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;
    }

  2. 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;
    }

 

(二)链表修改函数✍️

  1. 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;
    }

  2. 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;
    }

  3. 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;
    }

  4.  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;
    }

  5. 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;
    }

  6. 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;
    }

 

(三)获取特定元素的函数🔍

  1. 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;
    }

  2. 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】 

 

 


http://www.kler.cn/a/400190.html

相关文章:

  • Datawhale组队学习】模型减肥秘籍:模型压缩技术3——模型量化
  • ASP.NET Core Webapi 返回数据的三种方式
  • 在云服务器搭建 Docker
  • 第6章详细设计 -6.7 PCB工程需求表单
  • Python 神经网络项目常用语法
  • JavaWeb——JS、Vue
  • 第 20 章 - Golang 网络编程
  • 富格林:安全指正规防欺诈套路
  • HarmonyOs学习笔记-布局单位
  • flutter pigeon gomobile 插件中使用go工具类
  • 基于css的Grid布局和vue实现点击左移右移轮播过渡动画效果
  • 【Patroni官方文档】复制模式
  • STM32 使用 STM32CubeMX HAL库实现低功耗模式
  • PCL 三维重建 泊松曲面重建算法
  • AIGC(生成式AI)试用 18 -- AI Prompt
  • World Wide Walrus:下一代数据存储协议
  • 【C++学习(36)】C++20的co_await 的不同使用方式和特性
  • Cellebrite VS IOS18Rebooting
  • 建设项目全生命周期数智化归档与协同管理平台
  • 【第七课】Rust所有权系统(三)
  • React|bpmn.js|react-bpmn使用示例详解
  • STARTS:一种用于自动脑电/脑磁(E/MEG)源成像的自适应时空框架|文献速递-基于深度学习的病灶分割与数据超分辨率
  • 区块链中的wasm合约是什么?
  • 主界面获取个人信息测试服务端方
  • 第6章-详细设计 6.4归一化
  • Verilog HDL学习笔记