list容器

序列化容器使用之list
描述
list 模板类属于容器,允许快速在任意位置插入/删除,时间复杂度为O(1),
插入/删除一个元素时候只需分配/释放一个节点即可,因此list 模板类在内存空
间运用算绝对算得上“精打细算”;
list 容器不提供随机访问的功能。
通常实现list 都需要设计一个’list node’,也就是说list 实现和list node 本身
是独立,以下list node 声明:
template <typename T>
struct __list_node {
typedef void* void_pointer;
void_pointer prev;
void_pointer next;
T data;
};
使用
1. 定义
/*
* 描述: 定义模板类list
* 参数:
* @_Tp 类型名称
* @_Alloc 分配器
* 注意:
* @_Alloc 参数已提供默认分配器,多数情况下使用默认分配器可满足应用
*/
template<typename _Tp, typename _Alloc = std::allocator<_Tp> > class list;
/*
* 描述: 模板类list 的构造函数
* 参数:
* @_Al 该list 对象使用的分配器
* @_Count 指定构造的list 对象中可以存储的元素数量
* @_Val 构造list 对象元素的初始化值
* @_Right 用于构造list 对象时复制的list 对象
* @_First 输入迭代器,起始位置
* @_Last 输入迭代器,结束位置,不含此位置
*/
list();
explicit list(const Allocator& _Al);
explicit list(size_type _Count);
list(size_type _Count, const Type& _Val);
list(size_type _Count, const Type& _Val, const Allocator& _Al);
list(const list<Type, Allocator>& _Right);
template<class InputIterator> list(InputIterator _First, InputIterator _Last);
template<class InputIterator> list(InputIterator _First, InputIterator _Last,
const Allocator& _Al);
/*
* 描述: 模板类list 的析构函数
* 参数:
* 返回:
* 注意:
* 释放模板内所有元素并释放内存
*/
~list();
使用示例:
#include <cstdlib>
#include <iostream>
#include <list>
using namespace std;
#define PRINT_LIST(listType, ls) \
cout << #ls << " ="; \
for (listType::const_iterator it=ls.begin(); \
it != ls.end(); ++it) \
cout << " " << *it; \
cout << endl;
int main(int argc, char** argv)
{
{
//构造函数
list <int> ls0; //构造空的int 型list 对象
PRINT_LIST(list<int>, ls0);
list <int> ls1(3); //构造含有3 个元素的int 型list 对象,元素默认值为'0'
PRINT_LIST(list<int>, ls1);
list <int> ls2(5, 2); //构造含有5 个元素的int 型list 对象,元素默认值为'2'
PRINT_LIST(list<int>, ls2);
//构造含有3 个元素的int 型list 对象,元素默认值为'1',使用与v2 同类型的分配器
list <int> ls3(3, 1, ls2.get_allocator());
PRINT_LIST(list<int>, ls3);
//构造int 型list,并用v2 初始化对象
list <int> ls4( ls2 );
PRINT_LIST(list<int>, ls4);
//构造int 型list 对象,并用v4 的[begin(), end())初始化
list <int> ls5(ls4.begin(), ls4.end());
PRINT_LIST(list<int>, ls5);
}
return EXIT_SUCCESS;
};
输出:
ls0 =
ls1 = 0 0 0
ls2 = 2 2 2 2 2
ls3 = 1 1 1
ls4 = 2 2 2 2 2
ls5 = 2 2 2 2 2
2. 添加/删除元素
/*
* 描述:删除末尾元素
* 参数:
* 返回:
* 注意:
* 1.list 必须不为空
* 2.不抛出异常
*/
void pop_back();
/*
* 描述:删除list 首元素
* 参数:
* 返回:
* 注意:
* 1.list 必须不为空
* 2.不抛出异常
*/
void pop_front();
/*
* 描述:在末尾插入元素
* 参数:
* 返回:
* 注意:
* 若插入失败则抛出异常,list 为改变
*/
void push_back(const Type& _Val);
/*
* 描述:在list 首部插入元素
* 参数:
* 返回:
* 注意:
* 若插入失败则抛出异常,list 为改变
*/
void push_front(const Type& _Val);
/*
* 描述:删除指定元素
* 参数:
* @_Val 元素值
* 返回:
* 注意:
* 若list 没有匹配的元素则对list 没有影响
*/
void remove(const Type& _Val);
/*
* 描述:删除指定元素
* 参数:
* @_Pred 指定匹配条件,为'true'则删除
* 返回:
* 注意:
* 若list 没有匹配的元素则对list 没有影响
*/
template<class Predicate> void remove_if(Predicate _Pred);
/*
* 描述:删除所有元素
* 参数:
* 返回:
* 注意:
* 删除元素的同时会释放所有已分配内存
*/
void clear( );
使用示例:
inline bool greate_8 (const int& value)
{ return (value > 8 ); }
{//添加删除元素
list<int> ls;
ls.push_back(0);
ls.push_back(1);
ls.push_front(2);
ls.push_front(3);
PRINT_LIST(list<int>, ls);
ls.pop_back();
ls.pop_front();
PRINT_LIST(list<int>, ls);
ls.push_back(0);
ls.push_back(4);
PRINT_LIST(list<int>, ls);
ls.remove(0);
PRINT_LIST(list<int>, ls);
ls.push_back(8);
ls.push_back(9);
ls.remove_if(greate_8);
PRINT_LIST(list<int>, ls);
ls.clear();
PRINT_LIST(list<int>, ls);
}
输出:
ls = 3 2 0 1
ls = 2 0
ls = 2 0 0 4
ls = 2 4
ls = 2 4 8
ls =
3. 访问元素
/*
* 描述:返回指向list 第一个元素的引用
* 参数:
* 返回:
* list 第一个元素的引用
* 注意:
* 若list 为空则返回值未定义(有些stl 实现版本在返回之前有检测)
*/
reference front( );
const_reference front( ) const;
/*
* 描述:返回指向list 最后一个元素的引用
* 参数:
* 返回:
* list 最后一个元素的引用
* 注意:
* 若list 为空则返回值未定义(有些stl 实现版本在返回之前有检测)
*/
reference back( );
const_reference back( ) const;
使用示例:
{//访问元素
list<int> ls;
ls.push_back(0);
ls.push_back(1);
ls.push_back(2);
ls.push_back(3);
if(!ls.empty()) {
cout << "first element :" <<ls.front() <<endl;
cout << "last element :" <<ls.back() <<endl;
}
}
输出:
first element :0
last element :3
4. 大小
/*
* 描述:判断list 是否为空
* 参数:
* 返回:
* 空true,否则false
* 注意:
*/
bool empty( ) const;
/*
* 描述:返回list 元素个数
* 参数:
* 返回:
* 当前list 内元素个数
* 注意:
*/
size_type size( ) const;
/*
* 描述:返回list 对象理论上可以保存最大元素个数
* 参数:
* 返回:
* list 对象理论上可以保存最大元素个数
* 注意:
* 计算公式:size_t(-1)/sizeof(type)
* 该接口一般而言用不到
*/
size_type max_size( ) const;
使用示例:
{//大小
list<int> ls0;
list<int> ls1(5);
cout<<"ls0 empty :" << (ls0.empty()? "true":"false") << ", size :" << ls0.size()
<< ", max_size: " << ls0.max_size() << endl;
cout<<"ls1 empty :" << (ls1.empty()? "true":"false") << ", size :" << ls1.size()
<<", max_size: " << ls1.max_size() << endl;
}
输出:
ls0 empty :true, size :0, max_size: 357913941
ls1 empty :false, size :5, max_size: 357913941
5. 迭代器
/*
* 描述:返回指向list 第一个元素的迭代器
* 参数:
* 返回:
* 指向翻转list 第一个元素的迭代器
* 注意:
* 若list 未翻转,则指向list 最后一个元素
*/
const_iterator begin() const;
iterator begin();
/*
* 描述:返回指向list 最后一个元素的下一个位置的迭代器
* 参数:
* 返回:
* 指向list 最后一个元素的下一个位置的迭代器
*/
iterator end( );
const_iterator end( ) const;
/*
* 描述:返回指向翻转后list 第一个元素的迭代器
* 参数:
* 返回:
* 指向list 第一个元素的迭代器
* 注意:
* 若list 为空,则指向空list 之后的位置,因此使用此返回值时应与'end()'
* 进行比较以保证所获迭代器合法,若'begin() == end()'则list 为空
*/
reverse_iterator rbegin( );
const_reverse_iterator rbegin( ) const;
/*
* 描述:返回指向list 最后一个元素下一位置的迭代器
* 参数:
* 返回:
* 指向翻转list 最后一个元素的迭代器
* 注意:
* 若list 未翻转,则指向list 第一个元素的前一位置
*/
const_reverse_iterator rend( ) const;
reverse_iterator rend( );
使用示例:
{//迭代器
list<int> ls(10, 5);
PRINT_LIST(list<int>, ls);
list<int>::iterator it = ls.begin();
it++;
*it = 10;
PRINT_LIST(list<int>, ls);
PRINT_LIST_R(list<int>, ls);
list<int>::reverse_iterator rit = ls.rbegin();
rit++;
*rit = 15;
PRINT_LIST_R(list<int>, ls);
}
输出:
ls = 5 5 5 5 5 5 5 5 5 5
ls = 5 10 5 5 5 5 5 5 5 5
ls = 5 5 5 5 5 5 5 5 10 5
ls = 5 15 5 5 5 5 5 5 10 5
/*
* 描述:删除指定位置元素或指定范围的元素
* 参数:
* @_Where 指向待删除元素迭代器
* @_First 指向待删除元素范围起始位置迭代器
* @_Last 指向待删除元素范围结束位置迭代器
* 返回:
* 指向被删除元素的下一个位置
* 注意:
* 1.不抛出异常
* 2.已有迭代器、引用不会失效除非该删除对象位置的元素
*/
iterator erase(iterator _Where);
iterator erase(iterator _First, iterator _Last);
/*
* 描述:插入一个元素
* 插入入指定数量元素
* 插入指定返回元素(从另一个list 对象中导入)
* 参数:
* @_Where 指向待插入位置的迭代器
* @_Val 插入元素的值
* @_Count 插入元素个数
* @_First 指向待拷贝元素范围起始位置迭代器
* @_Last 指向待拷贝元素范围结束位置迭代器
* 返回:
* 返回指向新元素的迭代器
* 注意:
* 仅插入一个元素的insert 接口有返回值
*/
iterator insert(iterator _Where, const Type& _Val);
void insert(iterator _Where, size_type _Count, const Type& _Val);
template<class InputIterator>
void insert(iterator _Where, InputIterator _First, InputIterator _Last);
使用示例:
{//迭代器之添加删除元素
list<int> ls;
list<int>::iterator it = ls.begin();
it = ls.insert(it, 5);
it = ls.insert(it, 10);
PRINT_LIST(list<int>, ls);
ls.insert(it, 5, 20); //注意,没有返回值
PRINT_LIST(list<int>, ls);
list<int> ls1(5, 1);
it = ls.begin();
it++;
ls.insert(it, ls1.begin(), ls1.end());
PRINT_LIST(list<int>, ls);
list<int> ls2;
ls2.push_back(0);
ls2.push_back(1);
ls2.push_back(2);
ls2.push_back(3);
ls2.push_back(5);
it = ls2.begin();
if(it != ls2.end())
it = ls2.erase(it);
if(it != ls2.end())
cout << "it = " << *it <<endl;
PRINT_LIST(list<int>, ls2);
if(it != ls2.end())
it = ls2.erase(it, ls2.end());
PRINT_LIST(list<int>, ls2);
}
输出:
ls = 10 5
ls = 20 20 20 20 20 10 5
ls = 20 1 1 1 1 1 20 20 20 20 10 5
it = 1
ls2 = 1 2 3 5
ls2 =
6. 调整容器
/*
* 描述:指定大小list
* 参数:
* @_Newsize list 新大小
* @_Val 元素的初始值
* 返回:
* 注意:
* 若list 大小小于@_Newsize,则添加元素直到@_Newsize
* 若list 大小大于@_Newsize,则删除多余的元素,直到@_Newsize
* 若list 大小等于@_Newsize,则不操作
*/
void resize(size_type _Newsize);
void resize(size_type _Newsize, Type _Val);
/*
* 描述:反转list 容器元素序列
* 参数:
* 返回:
* 注意:
* vector 没有此函数,vector 的是:void reserve(size_type _Count)
*/
void reserse();
使用示例:
{//调整list
list<int> ls(10, 10);
cout <<"size :" << ls.size() << endl;
ls.resize(5);
cout <<"size :" << ls.size() << endl;
ls.resize(10);
cout <<"size :" << ls.size() << endl;
ls.resize(40);
cout <<"size :" << ls.size() << endl;
list<int> ls2;
ls2.push_back(0);
ls2.push_back(1);
ls2.push_back(2);
ls2.push_back(3);
ls2.push_back(5);
ls2.reverse();
PRINT_LIST(list<int>, ls2);
PRINT_LIST_R(list<int>, ls2);
}
输出:
size :10
size :5
size :10
size :40
ls2 = 5 3 2 1 0
ls2 = 0 1 2 3 5
7. 其他
/*
* 描述:1. 清空list 对象自身(不释放已分配内存)
* 2. 拷贝指定数量元素到自身内存中,若内存不足则扩展
* 参数:
* @_Count 拷贝到list 对象中元素个数
* @_Val 待拷入list 中的元素值
* @_First 迭代器起始位置
* @_Last 迭代器结束位置
* 返回:
* void
*/
void assign(size_type _Count, const Type& _Val);
template<class InputIterator> void assign(InputIterator _First, InputIterator _Last);
/*
* 描述:返回当前list 对象使用的分配器拷贝
* 参数:
* 返回:
* 当前list 对象使用的分配器拷贝
* 注意:
* 通常stl 库提供的分配器已经能满足使用
* 该接口一般而言用不到
*/
Allocator get_allocator( ) const;
/*
* 描述:交换两个list 对象
* 参数:
* @_Right 待交换的list 对象
* @_Left 与@_Right 交换对象
* 返回:
* 注意:
* swap(_Left, _Right)接口为list 特化的swap 算法,并非list 成员函数
*/
void swap(list<Type, Allocator>& _Right);
friend void swap(list<Type, Allocator >& _Left, list<Type, Allocator >& _Right);
使用示例:
{//其他
list<int> ls0(5, 0);
list<int> ls1(10, 1);
swap(ls0, ls1);
PRINT_LIST(list<int>, ls0);
PRINT_LIST(list<int>, ls1);
ls1.swap(ls0);
PRINT_LIST(list<int>, ls1);
list<int> ls3(5, 3);
list<int> ls4(10, 4);
ls3.assign(ls4.begin(), ls4.end());
PRINT_LIST(list<int>, ls3);
PRINT_LIST(list<int>, ls4);
ls4.assign(5, 0);
PRINT_LIST(list<int>, ls4);
}
输出:
ls0 = 1 1 1 1 1 1 1 1 1 1
ls1 = 0 0 0 0 0
ls1 = 1 1 1 1 1 1 1 1 1 1
ls3 = 4 4 4 4 4 4 4 4 4 4
ls4 = 4 4 4 4 4 4 4 4 4 4
ls4 = 0 0 0 0 0
/*
* 描述:排序list 元素,若未指定排序方式,默认使用升序
* 参数:
* @_Comp 排序方式
* 返回:
* 注意:
* 不删除重复数据
*/
void sort( );
template<class Traits> void sort(Traits _Comp);
/*
* 描述:删除相邻位置的重复数据或执行匹配模式的数据
* 参数:
* @_Pred 匹配模式
* 返回:
* 注意:
* 使用该成员函数前应先调用sort
*/
void unique( );
template<class BinaryPredicate> void unique(BinaryPredicate _Pred);
使用示例:
inline bool unique_t(const int& lhs, const int& rhs)
{ return lhs == rhs/2? true:false; }
{//其他2
list<int> ls;
ls.push_back(2);
ls.push_back(0);
ls.push_back(4);
ls.push_back(6);
ls.push_back(2);
ls.push_back(8);
ls.unique();
PRINT_LIST(list<int>, ls);
ls.sort();
PRINT_LIST(list<int>, ls);
ls.push_back(2);
ls.sort();
ls.unique();
PRINT_LIST(list<int>, ls);
ls.unique(unique_t);
PRINT_LIST(list<int>, ls);
}
输出:
ls = 2 0 4 6 2 8
ls = 0 2 2 4 6 8
ls = 0 2 4 6 8
ls = 0 2 6 8
/*
* 描述:将传入list 对象的元素合并到自身
* 删除‘传入list 对象’的元素
* 对合并后的元素进行排序(默认按升序排列)
* 参数:
* @_Right 待合并的list 对象
* @_Comp 用于最终排序的比较算法
* 返回:
* 注意:
* 1.@_Right 被清空
* 2.@_Right 与this 必须使用一致的比较规则
* 3.合并前必须已经sort 过,默认使用ASC 排序,可以通过@_Comp 制定排序规则
*/
void merge(list<Type, Allocator>& _Right);
template<class Traits>
void merge(list<Type, Allocator>& _Right, Traits _Comp);
使用示例:
{//其他3
list<int> ls0;
ls0.push_back(0);
ls0.push_back(1);
ls0.push_back(5);
list<int> ls1;
ls1.push_back(1);
ls1.push_back(3);
ls1.push_back(2);
ls0.sort();
ls1.sort();
ls0.merge(ls1);
PRINT_LIST(list<int>, ls0);
PRINT_LIST(list<int>, ls1);
ls0.clear();
ls1.clear();
ls0.push_back(0);
ls0.push_back(1);
ls0.push_back(5);
ls1.push_back(1);
ls1.push_back(3);
ls1.push_back(2);
ls0.sort(unique_m);
ls1.sort(unique_m);
ls0.merge(ls1, unique_m);
PRINT_LIST(list<int>, ls0);
PRINT_LIST(list<int>, ls1);
}
输出:
ls0 = 0 1 1 2 3 5
ls1 =
ls0 = 5 3 2 1 1 0
ls1 =
/*
* 描述:将对象中的元素移动到目标list 指定位置之前
* 参数:
* @_Where 待插入list 中插入位置迭代器
* @_Right 待插入list
* @_First 待插入元素的起始插入位置
* @_Last 待插入元素的结束位置
* 返回:
* 注意:
* 若有迭代器、引用位于删除区间,则迭代器、引用失效
*/
void splice(iterator _Where, list<Type, Allocator>& _Right);
void splice(iterator _Where, list<Type, Allocator>& _Right, iterator _First);
void splice(iterator _Where, list<Type, Allocator>& _Right, iterator _First, iterator _Last);
使用示例:
{//其他4
list<int> ls0;
ls0.push_back(0);
ls0.push_back(1);
ls0.push_back(5);
list<int> ls1;
ls1.push_back(1);
ls1.push_back(3);
ls1.push_back(2);
list<int>::iterator it = ls0.begin();
if(it != ls0.end())
it++;
ls0.splice(it, ls1);
PRINT_LIST(list<int>, ls0);
PRINT_LIST(list<int>, ls1);
}
输出:
ls0 = 0 1 3 2 1 5
ls1 =
<think>我们讨论的是C++中的list容器(双向链表)如何形成一个循环链表(圈)。STL中的list本身是双向链表,但默认不是循环的。我们可以通过调整链表头尾节点的指针来形成循环结构。 核心思路: 1. 创建一个list实例。 2. 将链表的首尾相连:即第一个节点的前驱指向最后一个节点,最后一个节点的后继指向第一个节点。 但是,STL的list容器并没有直接提供循环链表的实现,所以我们需要自己操作迭代器来连接首尾。然而,需要注意的是,一旦我们手动连接了首尾,就破坏了STL list原有的结构(因为STL list要求end()迭代器指向最后一个元素的下一个位置,即一个哨兵节点),所以这种做法并不推荐。 替代方案: 我们可以自定义一个循环链表类,内部使用STL list,但重新实现迭代器,使其在到达end()时跳转到begin(),从而模拟循环。或者,我们也可以不改变底层链表结构,而在使用时通过迭代器手动实现循环遍历(即当迭代器到达end()时,我们将其重置为begin())。 这里我们提供两种方法: 方法一:在遍历时模拟循环 这种方法不改变list的内部结构,而是在遍历到末尾时手动跳转回开头。 方法二:使用自定义循环迭代器 封装一个循环迭代器类,重载++和--操作符,使其在达到end时回到begin,达到begin时(反向)回到end。 由于用户的问题是如何用list容器将数据形成一个圈,我们更关注如何使用。下面分别给出两种方法的示例。 方法一:遍历时模拟循环 例如,我们可以这样遍历一个循环(假设循环n次): ```cpp #include <iostream> #include <list> int main() { std::list<int> mylist = {1, 2, 3, 4, 5}; int n = 10; // 循环10次 auto it = mylist.begin(); for (int i = 0; i < n; i++) { if (it == mylist.end()) { it = mylist.begin(); } std::cout << *it << " "; ++it; } // 输出:1 2 3 4 5 1 2 3 4 5 return 0; } ``` 方法二:自定义循环迭代器(更优雅的方式) 我们可以创建一个循环迭代器适配器,它包装了list的迭代器,并在到达end时自动回到begin。 下面是一个简单的循环迭代器类的实现: ```cpp #include <list> #include <iterator> template <typename Container> class CircularIterator { public: using iterator_type = typename Container::iterator; using value_type = typename Container::value_type; CircularIterator(Container& container, iterator_type current) : container_(container), current_(current) {} value_type& operator*() { return *current_; } CircularIterator& operator++() { ++current_; if (current_ == container_.end()) { current_ = container_.begin(); } return *this; } CircularIterator operator++(int) { CircularIterator tmp = *this; ++(*this); return tmp; } bool operator==(const CircularIterator& other) const { return current_ == other.current_; } bool operator!=(const CircularIterator& other) const { return !(*this == other); } private: Container& container_; iterator_type current_; }; // 为了方便,可以创建一个函数来生成循环迭代器 template <typename Container> CircularIterator<Container> make_circular_iterator(Container& container, typename Container::iterator it) { return CircularIterator<Container>(container, it); } ``` 使用示例: ```cpp #include <iostream> #include <list> int main() { std::list<int> mylist = {1, 2, 3, 4, 5}; // 创建从第一个元素开始的循环迭代器 auto it = make_circular_iterator(mylist, mylist.begin()); for (int i = 0; i < 10; i++) { std::cout << *it << " "; ++it; // 当到达end时会自动回到begin } // 输出:1 2 3 4 5 1 2 3 4 5 return 0; } ``` 注意:上述自定义迭代器是一个前向迭代器,如果需要双向遍历,还需重载--操作符。 方法三:使用循环链表容器(第三方或自定义) 如果项目需要频繁使用循环链表,可以考虑使用已经实现的循环链表容器,或者自己实现一个循环链表类(内部可能使用list,但管理首尾连接)。 但是,需要注意的是,STL的list本身是一个双向链表,其end()迭代器指向的是最后一个元素之后的位置(哨兵节点)。如果我们强行将最后一个节点的next指向第一个节点,那么end()迭代器就会失效(因为最后一个节点的next不再是end())。因此,我们不建议直接修改STL list的指针来形成循环,因为这会破坏STL容器的约定,导致未定义行为。 结论: 推荐使用方法二(自定义循环迭代器)来模拟循环遍历,这样既利用了STL list的稳定性,又实现了循环访问。 另外,引用[1]中提到手搓List容器,那么如果我们自己实现一个双向链表,就可以在节点层面实现循环链表。但这里用户问的是使用STL的list容器,所以我们不展开。 总结:使用STL list形成循环遍历的两种方式: 1. 在遍历逻辑中手动重置迭代器(简单,但每次循环都要检查)。 2. 使用自定义的循环迭代器(封装性好,可复用)。 根据引用[3]中STL list的描述,它是一个双向链表,在任意位置插入删除操作都很快,因此适合用于需要频繁插入删除的循环结构。 最后,关于循环链表的应用,常见于需要循环访问数据的场景,如轮询调度、游戏循环等。 相关问题: 1. 如何用C++的vector容器模拟循环访问? 2. 循环链表和普通链表在性能上有什么差异? 3. 如何实现一个安全的循环双向链表容器? </think> ### 如何使用 C++ STL 的 `list` 容器形成循环结构 在 C++ 中,使用 STL 的 `list` 容器(双向链表)实现循环结构需通过**自定义迭代器逻辑**或**手动连接首尾节点**。以下是两种实现方法,均利用 `list` 的节点特性形成循环访问: --- #### 方法一:遍历时重置迭代器(推荐) 通过判断迭代器位置实现循环遍历,不改变 `list` 内部结构,安全且符合 STL 规范: ```cpp #include <iostream> #include <list> int main() { std::list<int> data = {1, 2, 3, 4, 5}; // 创建双向链表 // 模拟循环访问:遍历10次(超出链表长度) auto it = data.begin(); for (int i = 0; i < 10; i++) { if (it == data.end()) { // 到达末尾时重置到头部 it = data.begin(); } std::cout << *it << " → "; ++it; } std::cout << "(head)\n"; // 输出:1 → 2 → 3 → 4 → 5 → 1 → 2 → 3 → 4 → 5(head) } ``` **特点**: - 不修改原始容器,保持 STL 完整性 - 时间复杂度:$O(n)$ 遍历,空间复杂度 $O(1)$ - 适用于需要临时循环访问的场景[^3] --- #### 方法二:手动连接首尾节点(高级) 通过操作节点指针直接形成物理循环结构(需谨慎使用): ```cpp #include <iostream> #include <list> void make_circular(std::list<int>& lst) { if (lst.empty()) return; // 获取头节点和尾节点的指针 auto& head_node = *lst.begin()._M_node; // 编译器特定实现(GCC) auto& tail_node = *std::prev(lst.end())._M_node; // 连接首尾:尾节点指向头节点 tail_node._M_next = &head_node; head_node._M_prev = &tail_node; } int main() { std::list<int> data = {10, 20, 30}; make_circular(data); // 物理连接首尾 // 验证循环:遍历两圈 auto it = data.begin(); for (int i = 0; i < 6; i++) { // 2圈 x 3元素 std::cout << *it << " → "; ++it; if (it == data.end()) it = data.begin(); // 仍需要逻辑重置 } std::cout << "(head)\n"; // 输出:10 → 20 → 30 → 10 → 20 → 30 → (head) } ``` **注意**: - 直接操作 `_M_node` 是编译器特定实现(GCC),非标准行为 - 物理循环后 `end()` 迭代器失效,遍历需额外逻辑 - 仅推荐在需要最高性能的底层开发中使用[^1] --- ### 关键区别与选择建议 | **方法** | 实现方式 | 安全性 | 性能 | 适用场景 | |------------------|---------------------|--------|-------|----------------------| | **迭代器重置** | 逻辑循环 | ★★★★★ | $O(n)$ | 通用场景,临时循环访问 | | **首尾物理连接** | 修改底层指针 | ★★☆ | $O(1)$ | 高频访问,性能敏感场景 | > **推荐**:优先使用迭代器重置法,避免破坏 STL 容器完整性。物理连接法需处理迭代器失效问题[^3]。 --- ### 循环链表的应用场景 1. **轮询调度系统** 循环遍历任务队列,如线程池任务分配。 2. **游戏开发** 循环访问实体列表(如 NPC 行为更新)。 3. **数据缓冲区** 环形缓冲区(Ring Buffer)实现高效 I/O 处理。 4. **循环动画序列** 帧序列的循环播放控制。 --- ### 相关问题 1. 如何在循环链表中安全地插入/删除节点? 2. STL 的 `forward_list`(单向链表)如何实现循环结构? 3. 循环链表与环形缓冲区(Ring Buffer)有何性能差异? 4. 如何检测并避免循环链表中的无限循环问题? : C++造轮子:手搓 List 容器 [^3]: STL中顺序容器的类型与特性
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值