C++之list类

片头

嗨!小伙伴们,大家好!今天,我们一起来学习list类,准备好了吗?咱们开始咯~


一、list类的介绍和使用

1.1 list类的介绍
  • (1)list类是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代
  • (2)list类的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素
  • (3)list类和forward_list非常相似:最主要的不同在于forward_list是单链表,只能朝前迭代,已让其更简单高效
  • (4)与其他的序列式容器相比(array,vector,deque),list通常在任意位置进行插入、移除元素的执行效率更好
  • (5)与其他序列式容器相比,list和forward_list最大的缺陷是不支持任意位置的随机访问,比如:要访问list的第6个元素,必须从已知的位置(比如头部或者尾部)迭代到该位置,在这段位置上迭代需要线性的时间开销;list还需要一些额外的空间,以保护每个节点的相关联信息(对于存储类型较小的元素的大list来说这可能是一个重要的因素)

list是一个模板类,在使用的时候我们需要给出元素的类型

使用list类时,需要包含头文件<list>


1.2 list类的使用

list中的接口比较多,此处类似,只需要掌握如何正确的使用,然后再去深入研究背后原理,已达到可扩展的能力。以下为list中一些常见的重要接口。

1.2.1 list的构造
构造函数(constructor)接口说明
list();构造空的list
list(size_type n,const value_type& val = value_type())构造的list中包含n个值为val的元素
list(const list& x)拷贝构造函数
list(InputIterator first,InputIterator last);用[fist,last)区间中的元素构造list

(1)list();

list类的默认构造函数,构造一个空链表

例如:

(2)list(size_type n,const value_type& val = value_type());

构造一个list对象并用n个val去初始化

例如:

(3)list(const list& x);

list类的拷贝构造函数

(4)Template<class InputIterator>         

list(InputIterator first,InputIterator last);

用[first,last)区间中的元素构造list


1.2.2 容器操作接口
函数声明接口说明
size_type size() const;获取链表节点个数
bool empty() const;判断链表是否为空
void resize(size_type n,value_type val = value_type());增加或减少节点个数为n

(1)size_type size()  const;

获取链表节点个数

例如:

(2)bool empty()  const;

判断链表是否为空

例如:

 (3)void resize(size_type n,value_type val = value_type());

增加或者减少节点个数为n

如果没有给出val,就用0作为元素

例如:

 


1.2.3 访问和遍历
函数声明接口说明
begin + end返回第一个元素的迭代器+返回最后一个元素下一个位置的迭代器
rbegin+rend返回第一个元素的reverse_iterator,即end位置;返回最后一个元素下一个位置的reverse_iterator,即begin位置
front返回list的第一个节点中值的引用
back返回list的最后一个节点中值的引用

(1)迭代器

iterator begin();

const_iterator begin() const;

iterator end();

const_iterator end() const;

 迭代器(正向),用于获取链表中第一个节点的位置和最后一个节点的下一个位置(即哨兵位)

例如:

9

(2)反向迭代器

reverse_iterator begin();

const_reverse_iterator rbegin() const;

reverse_iterator rend();

const_reverse_iterator rend() const;

 反向迭代器,rbegin获取容器中最后一个节点的位置,rend获取容器中哨兵位的位置

例如:

 注意:

①begin与end为正向迭代器,对迭代器执行++操作,迭代器向后移动

②rbegin(end)与rend(begin)为反向迭代器,对迭代器执行++操作,迭代器向前移动

(3)front

reference front();

const_reference front() const;

返回链表中第一个节点存储的数据的引用

例如:

 (4)back

reference back();

const_reference back const;

返回链表中最后一个节点存储的数据的引用

例如:


1.2.4 list的增删查改
函数声明接口说明
push_front在list首元素前插入值为val的元素
pop_front删除list中第一个元素
push_back在list尾部插入值为val的元素
pop_back删除list中最后一个元素
find在2个迭代器区间寻找val并返回其所在处的迭代器
insert在pos位置插入1个或多个元素
erase删除pos位置的元素或者 [first,las) 区间的所有元素
swap交换2个list对象
assign为list指定新内容,替换其当前内容并修改节点个数
clear清空list中的有效元素

(1)push_front

void push_front(const value_type& val);

在list首元素前插入值为val的元素

 例如:

(2)pop_front

void pop_front();

删除list中的第一个元素

例如:

(3)push_back

void push_back(const value_type& val);

从list尾部插入值为val的元素

例如:

(4)pop_back

void pop_back(); 

删除list中最后一个元素

例如:

 

(5)find

template<class InputIterator,class T>

InputIterator find(InputIterator first,InputIterator last,const T& val);

 在2个迭代器区间寻找val并返回其所在处的迭代器

例如:

注意:

①该函数并非list的成员函数,是标准库中的函数,多个容器共用该find函数

 可以看到,我们可以直接对list的迭代器进行解引用并修改其内容,但是迭代器不应该指向节点吗?为什么对其解引用能修改数据呢?

实际上,list的迭代器并不是用原生态指针进行模拟实现的,需要进行底层的封装,这里会在list的模拟实现的源代码中体现

(6)insert

Iterator insert(iterator position,const value_type& val);

void insert(iterator position,size_type n,const value_type& val);

——————————————————————————————————————

template<class InputIterator>

void insert(iterator position,InputIterator first,InputIterator last);

 在position位置插入1个或者多个元素

例如:

哈哈,小伙伴可能已经发现了:

list的insert操作是不会导致迭代器失效的,因为pos指向的节点不变,相对位置也不变

但是list的删除操作一定会导致迭代器失效,并且失效的只是指向被删除节点的迭代器,其他迭代器不会受到影响

(7)erase

iterator erase(iterator position);

iterator erase(iterator first,iterator last);

删除position位置的元素或者 [first,last) 区间的所有元素

例如:

(8) swap

void swap(list& x);

交换2个list对象

 例如:

(9)assign

tepmplate<class InputIterator>

void assign(InputIterator first,InputIterator last);

void assign(size_type n,const value_type& val);

 为list指定新内容,替换其当前内容并修改节点个数

例如:

(10)clear

void clear();

清空list中的有效元素

例如:


1.2.5 list的顺序修改接口

(1)sort

void sort();

template<class Compare>

void sort(Compare comp);

 list由于结构是双向链表,是无法使用标准库中的sort函数的,因为迭代器无法进行相减操作

并且在C++文档中,我们可以看到标准库中的sort也限定了迭代器的类型必须是可以进行随机访问(RandomAccess)的

迭代器有几个功能分类:

  • 单向迭代器,只能进行++
  • 双向迭代器,可以++和--
  • 随机迭代器,可以++,--,+和-

但是list的sort函数也是没什么必要的,因为直接对链表排序的效率比拷贝到vector进行排序再拷贝回链表要慢很多

void test22() {
	list<int> l1 = { 4,1,10,3,6,2 };
	list<int>::iterator it1 = l1.begin();
	while (it1 != l1.end()) {
		cout << *it1 << " ";
		it1++;
	}
	cout << endl;

	//默认排升序
	l1.sort();
	for (auto e : l1) {
		cout << e << " ";
	}
	cout << endl;

	//排降序,传递仿函数(匿名对象)
	l1.sort(greater<int>());
	for (auto e : l1) {
		cout << e << " ";
	}
	cout << endl;
}

(2) unique

void unique();

去掉list容器中重复的元素

例如:

注意:使用unique函数之前,必须先进行排序(升序或降序),不然运行不正确。 

(3)reverse

void reverse();

对链表进行逆置

例如:

 


片尾

今天,我们学习了list类的相关接口,在下一篇中,我们将学习如何模拟实现list类,希望看完这篇文章对友友们有所帮助!!!

点赞收藏加关注!!!

谢谢大家!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值