[C++初阶]list类的初步理解

一、标准库的list

list的底层是一个带哨兵位的双向循环链表结构
对比forward_list的单链表结构,list的迭代器是一个双向迭代器
与vector等顺序结构的容器相比,list在任意位置进行插入删除的效率更好,但是不支持任意位置的随机访问
list是一个模板类,在使用的时候我们需要给出元素的类型

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

二、list类的常用接口

1.常用的构造函数

1.默认构造函数,构造一个空的链表

list();

例如:

void test()
{
	list<int> lt;
}

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

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

例如:

void test()
{
	list<int> lt(3, 1);
	for (auto i : lt)
	{
		cout << i << " ";
	}
}

运行结果:

3.list类的拷贝构造函数

list(const list& x); 

例如:

void test()
{
	list<int> lt(3, 1);
	list<int> lt1(lt);
	for (auto i : lt1)
	{
		cout << i << " ";
	}
}

运行结果:

4.使用迭代器的初始化构造

Template<class InputIterator>

list(InputIterator first, InputIterator last);

例如:

void test()
{
	list<int> lt(3, 1);
	list<int> lt1(lt.begin(), lt.end());
	for (auto i : lt1)
	{
		cout << i << " ";
	}
}

运行结果:

2.常用的容量接口

还是老几样,我这里一块讲一讲吧。

1.size,empty,max_size,resize

size:返回链表容器中的元素数。

size_type size() const;

empty:返回列表容器是否为空(即其大小是否为 0)。
(注意:此函数不会以任何方式修改容器。若要清除链表容器的内容,请参阅 list::clear。)

bool empty() const;

max_size(不常用):返回链表容器可以容纳的最大元素数。
(由于已知的系统或库实现限制,这是容器可以达到的最大潜在大小,但绝不能保证容器能够达到该大小:在达到该大小之前,它仍然可能无法在任何时候分配存储。)

size_type max_size() const;

resize:调整容器的大小,使其包含 n 个元素。
如果 n 小于当前容器大小,则内容将减少到其前 n 个元素,删除超出(并销毁它们)的元素。
如果 n 大于当前容器大小,则通过在末尾插入所需数量的元素来扩展内容,以达到 n 的大小。如果指定了 val,则新元素将初始化为 val 的副本,否则,它们将进行值初始化。
(请注意,此函数通过插入或擦除容器中的元素来更改容器的实际内容。)

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

例如:

​
void test()
{
	list<int> lt(3, 1);
	/*list<int> lt1(lt);*/
	list<int> lt1(lt.begin(), lt.end());
	list<int> lt2;
	cout << lt1.size() <<endl;
	cout << lt1.empty() <<endl;
	cout << lt2.empty() <<endl;
	lt1.resize(10);
	cout << lt1.size() << endl;
	for (auto i : lt1)
	{
		cout << i << " ";
	}
}

​

运行结果:

3. 常用的访问与遍历

1.迭代器

iterator begin();

const_iterator begin() const;

iterator end();

const_iterator end() const;

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

例如:

void test()
{
	list<int> lt(5, 2);
	list<int> ::iterator it = lt.begin();
	while (it != lt.end())
	{
		cout << *it << " ";
		++it;
	}
}

运行结果:

2.反向迭代器

reverse_iterator rbegin();

const_reverse_iterator rbegin() const;

reverse_iterator rend();

const_reverse_iterator rend() const;

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

void test()
{
	list<int> lt = { 1,23,4,4,5,2 };
	list<int> ::reverse_iterator rit = lt.rbegin();
	while (rit != lt.rend())
	{
		cout << *rit << " ";
		++rit;
	}
}

运行结果:

注意:反向迭代器rit也要用++而不是--

3.front和back

front:

reference front();

const_reference front() const;

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

back:

reference back();

const_reference back() const;

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

例如:

void test()
{
	list<int> lt = {213123,123,34524,213};
	cout << lt.front() << endl;
	cout << lt.back() << endl;

}

运行结果:

4.list的增删查改

1)push_front和pop_front

push_front叫头插,从链表头部插入一个元素

void push_front(const value_type& val);

pop_front叫头删,从链表头部删除一个元素

void pop_front();

void pop_front();

例如:

void test()
{
	list<int> lt = {213123,123,34524,213};
	cout << lt.front() << endl;
	lt.push_front(21345);
	cout << lt.front() << endl;
	lt.pop_front();
	cout << lt.front() << endl;
}

运行结果:

2) push_back和pop_back

push_back叫尾插,从链表尾部插入一个元素

void push_back(const value_type& val);

pop_back叫尾删,从链表尾部删除一个元素

void pop_back();

例如:

void test()
{
	list<int> lt = {213123,123,34524,213};
	cout << lt.back() << endl;
	lt.push_back(21345);
	cout << lt.back() << endl;
	lt.pop_back();
	cout << lt.back() << endl;
}

运行结果:

3) find

template <class InputIterator, class T>

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

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

例如:

void test()
{
	list<int> lt;
	for (int i = 0; i < 3; i++)
	{
		lt.push_back(i);
	}
	list<int>::iterator pos = find(lt.begin(), lt.end(), 1);
	*pos = 114514;
	for (auto i : lt)
	{
		cout << i << " ";
	}
}

运行结果:

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

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

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

4)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位置的前面插入一个或多个元素

例如:

void test()
{
	list<int> lt;
	for (int i = 0; i < 3; i++)
	{
		lt.push_back(i);
	}
	list<int>::iterator pos = find(lt.begin(), lt.end(), 1);
	lt.insert(pos, 8848);
	for (auto i : lt)
	{
		cout << i << " ";
	}
}

运行结果:

细心的同学可能已经发现了,list的insert操作是不会导致迭代器失效的,因为pos指向的节点不变,相对位置也不变。

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

5) erase

iterator erase(iterator position);

iterator erase(iterator first, iterator last);

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

例如:

void test()
{
	list<int> lt;
	for (int i = 0; i < 3; i++)
	{
		lt.push_back(i);
	}
	list<int>::iterator pos = find(lt.begin(), lt.end(), 1);
	lt.erase(lt.begin());
	for (auto i : lt)
	{
		cout << i << " ";
	}
}

运行结果:

void test()
{
	list<int> lt;
	for (int i = 0; i < 3; i++)
	{
		lt.push_back(i);
	}
	list<int>::iterator pos = find(lt.begin(), lt.end(), 2);
	lt.erase(lt.begin(),pos);
	for (auto i : lt)
	{
		cout << i << " ";
	}
}

运行结果:

6) swap

void swap(vector& x);

交换两个list对象

例如:

void test()
{
	list<int> lt;
	list<int> lt2(6, 12);
	for (int i = 0; i < 10; i++)
	{
		lt.push_back(i);
	}
	lt.swap(lt2);
	for (auto i : lt2)
	{
		cout << i << " ";
	}
	cout << endl;
}

运行结果:

7)assign

template <class InputIterator>

void assign(InputIterator first, InputIterator last);

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

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

例如:

void test()
{
	list<int> lt;
	for (int i = 0; i < 10; i++)
	{
		lt.push_back(i);
	}
	lt.assign(4, 0);
	for (auto i : lt)
	{
		cout << i << " ";
	}
}

运行结果:

8)clear

void clear();

删除链表所有节点

例如:

void test()
{
	list<int> lt;
	for (int i = 0; i < 10; i++)
	{
		lt.push_back(i);
	}
	lt.clear();
	cout << lt.size() << endl;
}

运行结果:

5.list的顺序修改接口

(1)sort

void sort();

template<class Compare>

void sort(Compare comp);

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

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

default (1)	
template <class RandomAccessIterator>
  void sort (RandomAccessIterator first, RandomAccessIterator last);
custom (2)	
template <class RandomAccessIterator, class Compare>
  void sort (RandomAccessIterator first, RandomAccessIterator last, Compare comp);

迭代器有几个功能分类:

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

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

(2)reverse

template <class BidirectionalIterator>
  void reverse (BidirectionalIterator first, BidirectionalIterator last);

对链表进行逆置

例如:

void test()
{
	list<int> lt;
	for (int i = 0; i < 10; i++)
	{
		lt.push_back(i);
	}
	for (auto i : lt)
	{
		cout << i << " ";
	}
	cout << endl;
	lt.reverse();
	for (auto i : lt)
	{
		cout << i << " ";
	}
	cout << endl;

}

运行结果:


如有错误,欢迎指正。

  • 16
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值