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

C++——list容器以及手动实现

LIST容器

  • list概述
    • 列表
    • 容器属性
    • 例子
  • list函数
    • 构造函数
      • 默认构造函数:
      • 带有元素个数和元素初值的构造函数:
      • 范围构造函数:
      • 拷贝构造函数:
      • 移动构造函数:
      • 示例
    • 赋值运算符重载
      • 拷贝赋值操作符 (1):
      • 移动赋值操作符 (2):
      • 初始化列表赋值操作符 (3):
      • 示例
      • 注意事项
    • 迭代器函数
    • 插入和删除函数
    • 清空和删除函数
  • 自己实现的list容器案例

list概述

std::list

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

列表

列表是序列容器,允许在序列中的任何位置执行恒定时间插入和擦除操作,并在两个方向上进行迭代。

列表容器以双向链表的形式实现;双向链表可以将它们包含的每个元素存储在不同且不相关的存储位置。排序是通过与每个元素的关联在内部保持的,该关联链接到它前面的元素,以及与它后面的元素的链接。

它们与 forward_list 非常相似:主要区别在于forward_list对象是单链表,因此它们只能向前迭代,以换取更小、更高效的性能。

与其他基本标准序列容器(数组、向量和 deque)相比,列表在插入、提取和移动已获得迭代器的容器内的任何位置的元素方面通常表现更好,因此在大量使用这些元素的算法(如排序算法)中也是如此。

与这些其他序列容器相比,列表s 和 forward_lists 的主要缺点是它们无法通过其位置直接访问元素;例如,要访问列表中的第六个元素,必须从已知位置(如开始或结束)迭代到该位置,这需要它们之间的线性时间。它们还会消耗一些额外的内存,以保持与每个元素关联的链接信息(这可能是小型元素的大型列表的一个重要因素)。

容器属性

序列

序列容器中的元素按严格的线性顺序排序。各个元素通过它们在此序列中的位置进行访问。

双向链表

每个元素都保留有关如何定位下一个和上一个元素的信息,允许在特定元素(甚至整个范围)之前或之后进行恒定时间插入和擦除操作,但不能直接随机访问。

分配器感知

容器使用分配器对象来动态处理其存储需求。

模板参数

T

元素的类型

别名为成员类型 list::value_type。

分配

用于定义存储分配模型的分配器对象的类型。默认情况下,使用分配器类模板,该模板定义了最简单的内存分配模型,并且与值无关。

别名为成员类型 list::allocator_type。

例子

std::list 是 C++ 标准库中的双向链表(doubly linked list)容器,它提供了高效的插入和删除操作,但不支持随机访问元素。下面简要介绍一下 std::list 的底层实现机制和一个简单的示例。

底层实现机制

std::list 使用双向链表作为其底层数据结构,每个节点(node)包含两个指针:一个指向前一个节点,一个指向后一个节点。这种结构使得在任意位置插入和删除操作都很高效,因为只需要调整相邻节点的指针即可,不需要像数组那样移动大量元素。

双向链表的结构如下所示

nullptr <-> Node1 <-> Node2 <-> … <-> NodeN <-> nullptr
其中 nullptr 表示空指针,用来表示链表的头尾。每个节点中会存储实际的数据,比如 int、char、struct 等。

示例

下面是一个简单的示例展示如何使用 std::list 容器:

#include <iostream>
#include <list>int main() {// 创建一个空的 std::list 容器std::list<int> mylist;// 向 list 中插入元素mylist.push_back(1);    // {1}mylist.push_back(2);    // {1, 2}mylist.push_front(3);   // {3, 1, 2}mylist.push_back(4);    // {3, 1, 2, 4}// 使用迭代器遍历输出 list 中的元素std::cout << "List elements: ";for (auto it = mylist.begin(); it != mylist.end(); ++it) {std::cout << *it << " ";}std::cout << std::endl;// 在指定位置插入元素auto it = ++mylist.begin(); // 获取第二个元素的迭代器mylist.insert(it, 5); // 在第二个元素后面插入 5,{3, 5, 1, 2, 4}// 删除元素mylist.pop_front(); // 删除第一个元素,{5, 1, 2, 4}mylist.pop_back();  // 删除最后一个元素,{5, 1, 2}// 输出修改后的 list 元素std::cout << "Modified List elements: ";for (auto it = mylist.begin(); it != mylist.end(); ++it) {std::cout << *it << " ";}std::cout << std::endl;return 0;
}

示例解释

1,首先,我们创建了一个 std::list 类型的空列表 mylist。

2,使用 push_back() 和 push_front() 方法向列表中添加元素。

3,使用迭代器 begin() 和 end() 遍历输出列表中的元素。

4,使用 insert() 在指定位置插入元素。

5,使用 pop_front() 和 pop_back() 方法删除第一个和最后一个元素。

6,最后,再次使用迭代器遍历输出修改后的列表元素。

这个例子展示了 std::list 容器的基本操作,包括插入、删除和遍历元素等,利用了其底层双向链表的特性,使得这些操作都能够高效地执行。

list函数

构造函数

在这里插入图片描述

std::list 的构造函数可以分为以下几类:

默认构造函数:

std::list();

创建一个空的双向链表。链表中不包含任何元素。

带有元素个数和元素初值的构造函数:

explicit std::list(size_type count, const T& value = T());

创建一个包含 count 个元素的链表,每个元素的值都是 value。例如:

std::list<int> mylist(5, 10);  // 创建一个包含5个值为10的元素的链表

范围构造函数:

template<class InputIterator>
std::list(InputIterator first, InputIterator last);

从迭代器范围 [first, last) 中构造链表,将范围内的元素复制到新建的链表中。例如:

int arr[] = {1, 2, 3, 4, 5};
std::list<int> mylist(arr, arr + 5);  // 从数组中复制元素到链表

拷贝构造函数:

std::list(const std::list& other);

使用另一个 std::list 对象 other 中的元素创建新的链表。例如:

std::list<int> original = {1, 2, 3};
std::list<int> copy = original;  // 使用拷贝构造函数创建副本

移动构造函数:

std::list(std::list&& other) noexcept;

使用另一个 std::list 对象 other 中的元素创建新的链表,并接管 other 的资源。移动构造函数通常比拷贝构造函数更高效。例如:

std::list<int> source = {1, 2, 3};
std::list<int> dest = std::move(source);  // 使用移动构造函数

示例

下面是一个综合示例,展示了 std::list 的不同构造函数的使用方式:

#include <iostream>
#include <list>int main() {// 默认构造函数创建空链表std::list<int> empty_list;// 使用带有元素个数和初值的构造函数std::list<int> filled_list(5, 10);  // 包含5个值为10的元素的链表// 使用范围构造函数int arr[] = {1, 2, 3, 4, 5};std::list<int> range_list(arr, arr + 5);  // 从数组中复制元素到链表// 使用拷贝构造函数std::list<int> original = {1, 2, 3};std::list<int> copy = original;  // 创建副本// 使用移动构造函数std::list<int> source = {4, 5, 6};std::list<int> dest = std::move(source);  // 移动构造函数// 输出链表中的元素std::cout << "Filled list elements:";for (auto& elem : filled_list) {std::cout << " " << elem;}std::cout << std::endl;std::cout << "Range list elements:";for (auto& elem : range_list) {std::cout << " " << elem;}std::cout << std::endl;std::cout << "Copy list elements:";for (auto& elem : copy) {std::cout << " " << elem;}std::cout << std::endl;std::cout << "Moved list elements:";for (auto& elem : dest) {std::cout << " " << elem;}std::cout << std::endl;return 0;

赋值运算符重载

在这里插入图片描述

拷贝赋值操作符 (1):

list& operator= (const list& x);

将另一个 std::list 对象 x 中的元素拷贝赋值给当前链表对象。例如:

std::list<int> list1 = {1, 2, 3};
std::list<int> list2 = {4, 5};list2 = list1;  // 使用拷贝赋值操作符
// 现在 list2 的元素为 {1, 2, 3}

移动赋值操作符 (2):

list& operator= (list&& x);

将另一个 std::list 对象 x 中的元素移动赋值给当前链表对象。移动赋值操作符通常用于提高效率。例如:

std::list<int> list1 = {1, 2, 3};
std::list<int> list2 = {4, 5};list2 = std::move(list1);  // 使用移动赋值操作符
// 现在 list2 的元素为 {1, 2, 3},list1 变成空链表

初始化列表赋值操作符 (3):

list& operator= (initializer_list<value_type> il);

使用初始化列表 il 中的元素来赋值给当前链表对象。例如:

std::list<int> list1 = {1, 2, 3};list1 = {4, 5, 6};  // 使用初始化列表赋值操作符
// 现在 list1 的元素为 {4, 5, 6}

示例

下面是一个综合示例,演示了 std::list 中各种赋值操作符的使用:

#include <iostream>
#include <list>int main() {// 拷贝赋值操作符示例std::list<int> list1 = {1, 2, 3};std::list<int> list2 = {4, 5};list2 = list1;  // 拷贝赋值操作符std::cout << "List2 after copy assignment:";for (auto& elem : list2) {std::cout << " " << elem;}std::cout << std::endl;// 移动赋值操作符示例std::list<int> list3 = {7, 8};std::list<int> list4 = {9, 10};list4 = std::move(list3);  // 移动赋值操作符std::cout << "List4 after move assignment:";for (auto& elem : list4) {std::cout << " " << elem;}std::cout << std::endl;// 初始化列表赋值操作符示例std::list<int> list5 = {11, 12, 13};list5 = {14, 15, 16};  // 初始化列表赋值操作符std::cout << "List5 after initializer list assignment:";for (auto& elem : list5) {std::cout << " " << elem;}std::cout << std::endl;return 0;
}

注意事项

拷贝赋值操作符和移动赋值操作符的选择通常取决于右侧操作数的类型和实际需求。

初始化列表赋值操作符用于通过大括号 {} 直接赋值一组元素。

迭代器函数

1,begin() 和 end():

iterator begin();
iterator end();
const_iterator begin() const;
const_iterator end() const;

这些函数返回链表的起始迭代器和结束迭代器。对于非常量对象,begin() 返回指向第一个元素的迭代器,end() 返回指向最后一个元素后面位置的迭代器;对于常量对象,返回的是 const_iterator,用于只读访问链表的元素。

示例

std::list<int> myList = {1, 2, 3, 4};// 使用迭代器遍历链表
for (auto it = myList.begin(); it != myList.end(); ++it) {std::cout << *it << " ";
}

2,rbegin() 和 rend():

reverse_iterator rbegin();
reverse_iterator rend();
const_reverse_iterator rbegin() const;
const_reverse_iterator rend() const;

这些函数返回链表的反向迭代器,用于反向遍历链表元素。rbegin() 返回指向最后一个元素的反向迭代器,rend() 返回指向第一个元素前面位置的反向迭代器;对于常量对象,返回的是 const_reverse_iterator。

示例

std::list<int> myList = {1, 2, 3, 4};// 使用反向迭代器遍历链表
for (auto rit = myList.rbegin(); rit != myList.rend(); ++rit) {std::cout << *rit << " ";
}

3,cbegin() 和 cend():

const_iterator cbegin() const noexcept;
const_iterator cend() const noexcept;

这些函数返回链表的常量迭代器,用于只读访问链表元素。cbegin() 返回指向第一个元素的常量迭代器,cend() 返回指向最后一个元素后面位置的常量迭代器。

示例

std::list<int> myList = {1, 2, 3, 4};// 使用常量迭代器遍历链表
for (auto cit = myList.cbegin(); cit != myList.cend(); ++cit) {std::cout << *cit << " ";
}

4,crbegin() 和 crend():

const_reverse_iterator crbegin() const noexcept;
const_reverse_iterator crend() const noexcept;

这些函数返回链表的常量反向迭代器,用于只读反向访问链表元素。crbegin() 返回指向最后一个元素的常量反向迭代器,crend() 返回指向第一个元素前面位置的常量反向迭代器。

示例

std::list<int> myList = {1, 2, 3, 4};// 使用常量反向迭代器遍历链表
for (auto crit = myList.crbegin(); crit != myList.crend(); ++crit) {std::cout << *crit << " ";
}

插入和删除函数

  • push_front(const T& value)
    在链表头部插入一个元素。

语法结构

void push_front(const T& value);

示例用法

#include <iostream>
#include <list>int main() {std::list<int> myList = {1, 2, 3};// 在链表头部插入元素myList.push_front(0); // 现在链表为 {0, 1, 2, 3}for (auto it = myList.begin(); it != myList.end(); ++it) {std::cout << *it << " ";}std::cout << std::endl;return 0;
}
  • push_back(const T& value)
    在链表尾部插入一个元素。

语法结构

void push_back(const T& value);

示例用法

#include <iostream>
#include <list>int main() {std::list<int> myList = {1, 2, 3};// 在链表尾部插入元素myList.push_back(4); // 现在链表为 {1, 2, 3, 4}for (auto it = myList.begin(); it != myList.end(); ++it) {std::cout << *it << " ";}std::cout << std::endl;return 0;
}
  • pop_front()
    删除链表头部的元素。

语法结构

void pop_front();

示例用法

#include <iostream>
#include <list>int main() {std::list<int> myList = {1, 2, 3};// 删除链表头部的元素myList.pop_front(); // 现在链表为 {2, 3}for (auto it = myList.begin(); it != myList.end(); ++it) {std::cout << *it << " ";}std::cout << std::endl;return 0;
}
  • pop_back()
    删除链表尾部的元素。

语法结构

void pop_back();

示例用法

#include <iostream>
#include <list>int main() {std::list<int> myList = {1, 2, 3};// 删除链表尾部的元素myList.pop_back(); // 现在链表为 {1, 2}for (auto it = myList.begin(); it != myList.end(); ++it) {std::cout << *it << " ";}std::cout << std::endl;return 0;
}
  • insert(const_iterator pos, const T& value)
    在迭代器 pos 所指位置插入元素 value。

语法结构

iterator insert(const_iterator pos, const T& value);

示例用法

#include <iostream>
#include <list>int main() {std::list<int> myList = {1, 2, 3};auto it = myList.begin();++it; // 移动到第二个位置// 在指定位置插入元素myList.insert(it, 4); // 现在链表为 {1, 4, 2, 3}for (auto it = myList.begin(); it != myList.end(); ++it) {std::cout << *it << " ";}std::cout << std::endl;return 0;
}
  • erase(const_iterator pos)
    删除迭代器 pos 所指位置的元素。

语法结构

iterator erase(const_iterator pos);

示例用法

#include <iostream>
#include <list>int main() {std::list<int> myList = {1, 2, 3};auto it = myList.begin();++it; // 移动到第二个位置// 删除指定位置的元素myList.erase(it); // 现在链表为 {1, 3}for (auto it = myList.begin(); it != myList.end(); ++it) {std::cout << *it << " ";}std::cout << std::endl;return 0;
}

清空和删除函数

  • clear()
    清空链表,移除所有元素。

语法结构

void clear();

示例用法

#include <iostream>
#include <list>int main() {std::list<int> myList = {1, 2, 2, 3};// 清空链表myList.clear(); // 现在链表为空std::cout << "List size after clearing: " << myList.size() << std::endl;return 0;
}
  • remove(const T& value)
    移除链表中所有等于 value 的元素。

语法结构

void remove(const T& value);

示例用法

#include <iostream>
#include <list>int main() {std::list<int> myList = {1, 2, 2, 3};// 移除所有值为 2 的元素myList.remove(2); // 现在链表为 {1, 3}for (auto it = myList.begin(); it != myList.end(); ++it) {std::cout << *it << " ";}std::cout << std::endl;return 0;
}
  • remove_if(UnaryPredicate p)
    根据条件 p 移除符合条件的元素。

语法结构

template<class UnaryPredicate>
void remove_if(UnaryPredicate p);

示例用法

#include <iostream>
#include <list>bool isEven(int num) {return num % 2 == 0;
}int main() {std::list<int> myList = {1, 2, 3, 4, 5};// 移除所有偶数元素myList.remove_if(isEven); // 现在链表为 {1, 3, 5}for (auto it = myList.begin(); it != myList.end(); ++it) {std::cout << *it << " ";}std::cout << std::endl;return 0;
}
  • unique()
    移除链表中连续重复的元素,只保留一个。

语法结构

void unique();

示例用法

#include <iostream>
#include <list>int main() {std::list<int> myList = {1, 2, 2, 3, 3, 3, 4};// 移除连续重复的元素myList.unique(); // 现在链表为 {1, 2, 3, 4}for (auto it = myList.begin(); it != myList.end(); ++it) {std::cout << *it << " ";}std::cout << std::endl;return 0;
}

自己实现的list容器案例

namespace mylist
{// List的节点类template<class T>struct ListNode{ListNode(const T& val = T()): _pPre(nullptr), _pNext(nullptr), _val(val) {}ListNode<T>* _pPre;ListNode<T>* _pNext;T _val;};// List的迭代器类template<class T, class Ref, class Ptr>class ListIterator{typedef ListNode<T>* PNode;typedef ListIterator<T, Ref, Ptr> Self;public:ListIterator(PNode pNode = nullptr): _pNode(pNode) {}ListIterator(const Self& l): _pNode(l._pNode) {}PNode Ptr() const{return _pNode;}T& operator*() const{return _pNode->_val; // 直接解引用 _pNode}T* operator->() const{return &_pNode->_val; // 直接返回 _pNode->_val 的地址}Self& operator++(){_pNode = _pNode->_pNext;return *this;}Self operator++(int){Self tem(_pNode);_pNode = _pNode->_pNext;return tem;}Self& operator--(){_pNode = _pNode->_pPre;return *this;}Self operator--(int){Self tem(_pNode);_pNode = _pNode->_pPre;return tem;}bool operator!=(const Self& l) const{return _pNode != l._pNode;}bool operator==(const Self& l) const{return _pNode == l._pNode;}private:PNode _pNode;};// list类template<class T>class list{typedef ListNode<T> Node;typedef Node* PNode;public:typedef ListIterator<T, T&, T*> iterator;typedef ListIterator<T, const T&, const T&> const_iterator;public:list(){CreateHead();length = 0;}list(int n, const T& value = T()){CreateHead();length = n;while (n--){PNode tem = new Node(value); //创建 Node 对象非 PNode_pHead->_pPre->_pNext = tem;tem->_pPre = _pHead->_pPre;tem->_pNext = _pHead;_pHead->_pPre = tem;}}template <class Iterator>list(Iterator first, Iterator last){CreateHead();while (first != last){PNode tem = new Node(*first); //创建 Node 对象非 PNodefirst++;_pHead->_pPre->_pNext = tem;tem->_pPre = _pHead->_pPre;tem->_pNext = _pHead;_pHead->_pPre = tem;length++;}}list(const list<T>& l){CreateHead();PNode sta = l._pHead->_pNext;PNode end = l._pHead;while (sta != end){PNode tem = new Node(sta->_val); // 创建 Node 对象而非 PNodesta = sta->_pNext;_pHead->_pPre->_pNext = tem;tem->_pPre = _pHead->_pPre;tem->_pNext = _pHead;_pHead->_pPre = tem;length++;}}list<T>& operator=(const list<T>& l){clear(); // 使用 clear() 而不是 ~list() 来清空内容CreateHead();PNode sta = l._pHead->_pNext;PNode end = l._pHead;while (sta != end){PNode tem = new Node(sta->_val); // 创建 Node 对象而非 PNodesta = sta->_pNext;_pHead->_pPre->_pNext = tem;tem->_pPre = _pHead->_pPre;tem->_pNext = _pHead;_pHead->_pPre = tem;length++;}return *this;}~list(){clear(); // 使用 clear() 来删除所有节点delete _pHead; //删除链表头节点}///// List Iteratoriterator begin(){return iterator(_pHead->_pNext);}iterator end(){return iterator(_pHead);}const_iterator begin() const{return const_iterator(_pHead->_pNext);}const_iterator end() const{return const_iterator(_pHead);}///// List Capacitysize_t size() const{return length;}bool empty() const{return length == 0;}// List AccessT& front(){assert(!empty()); // assert(!empty())return _pHead->_pNext->_val;}const T& front() const{assert(!empty()); // assert(!empty())return _pHead->_pNext->_val;}T& back(){assert(!empty()); // assert(!empty())return _pHead->_pPre->_val;}const T& back() const{assert(!empty()); // 修改:assert(!empty())return _pHead->_pPre->_val;}// List Modifyvoid push_back(const T& val) { insert(end(), val); }void pop_back() { erase(--end()); }void push_front(const T& val) { insert(begin(), val); }void pop_front() { erase(begin()); }iterator insert(iterator pos, const T& val){assert(pos != end());PNode cur = pos.Ptr();PNode tem = new Node(val);tem->_pPre = cur->_pPre; // 修正插入节点的前驱指针tem->_pNext = cur;cur->_pPre->_pNext = tem;cur->_pPre = tem;length++;return iterator(tem);}iterator erase(iterator pos){PNode cur = pos.Ptr();cur->_pPre->_pNext = cur->_pNext;cur->_pNext->_pPre = cur->_pPre;iterator next(cur->_pNext);delete cur;length--;return next;}void clear(){PNode cur = _pHead->_pNext;while (cur != _pHead){PNode next = cur->_pNext;delete cur;cur = next;}_pHead->_pNext = _pHead;_pHead->_pPre = _pHead;length = 0;}void swap(list<T>& l){std::swap(_pHead, l._pHead);std::swap(length, l.length);}private:void CreateHead(){_pHead = new Node;_pHead->_pNext = _pHead;_pHead->_pPre = _pHead;}PNode _pHead;size_t length;};};
http://www.lryc.cn/news/412145.html

相关文章:

  • Win11系统文件资源管理器鼠标右键卡顿解决方法
  • 零基础学Python之 第十八讲 文件读写
  • 检索增强生成(RAG):智能内容生成的新纪元
  • ubuntu2204安装elasticsearch7.17.22
  • 介绍Servlet后端中两种接收参数方式req.getAttributer和req.getParameter的区别
  • Delphi FMX安卓Android播放mp3音频内存流
  • MapUtils常用方法
  • 自定义PasswordEditText控件,在手机字体应用后,字体样式未发生改变
  • 学习打卡第31天
  • opencascade AIS_TexturedShape源码学习 贴纹理
  • C# winform 串口读取字节流,MB级别字节流
  • 创建一个简单的单链表
  • 15.1 Zookeeper简介安装及基础使用
  • 详细说明Java中Map和Set接口的使用方法
  • CSS3 scale 适配
  • SX_初识GitLab_1
  • 这才是 PHP 高性能框架 Workerman 的立命之本
  • Python——记录pip问题(解决下载慢、升级失败问题)
  • Windows Server 2025 Preview 部署 Ⅰ—— ISO下载和硬件要求
  • AI2-CUDA、CuDNN、TensorRT的详细安装教程
  • TCP连接中重复使用了两个相同的端口怎么办
  • 如何自定义异常
  • C++中的依赖注入
  • CSS平面转换-平移
  • Linux-3:Shell编程——基础语法(0-50%)
  • C++ --> string类模拟实现(附源码)
  • 基于PHP+MySQL组合开发的微信活动投票小程序源码系统 带完整的安装代码包以及搭建部署教程
  • 利用Arcgis设置分式标注(分子分母标注)
  • 大麦网抢票攻略:使用Python Selenium实现
  • Navicat 在整个数据库中查找字符