list的使用

前言

我们前面已经对string和vector进行了学习使用,以及对他们的底层进行了模拟实现!本期我们继续学习STL的另外一个容器---list。

本期内容介绍

什么是list?

list的常用接口

什么是list?

还是来看看官方的文档说明!

这里通过官方文档我们可以知道!list是一个带头双向循环的链表!在插入和删除时,时间复杂度是常量级别的!

list常用接口

在正式的开始介绍接口使用前,我们还是来了解一下类型重命名!

这里主要用到的就是上面的三个! value_type 就是T, reference 是value_type&, size_type 就是size_t

构造、拷贝构造、赋值拷贝、析构

list<int> lt1;//空构造

list<int> lt2(10, 6);//n 个 val构造

vector<int> v = { 1, 2,4,56,7,8,-1 };

list<int> lt3(v.begin(), v.end());//迭代器区间构造

list<int> lt4(lt2);//拷贝构造

这里除了介绍这些常见的外!这里在穿插一个C++11引入的一个非常好用的,初始化序列初始化!

这个在上期vector的模拟实现已经介绍了,auto ret = {1,2,3};此时的ret就是initializer_list<int>。

list<int> lt5 = { 1,2,3,4,5 };//C++11的初始化序列初始化

list<int> lt4(lt2);//拷贝构造

list<int> lt5 = { 1,2,3,4,5 };//C++11的初始化序列初始化

lt5 = lt4;//赋值拷贝

析构还是一样的:清理资源、释放空间~!

迭代器

正向

list<int> lt = { 1,2,3,4,5 };//C++11的初始化序列初始化
const list<int> clt = { 10, 20,30, 40 };

list<int>::iterator it = lt.begin();//正向
while (it != lt.end())
{
	cout << *it << " ";
	++it;
}
cout << endl;

list<int>::const_iterator cit = clt.begin();//const正向
while (cit != clt.end())
{
	cout << *cit << " ";
	++cit;
}
cout << endl;

支持迭代器必然支持范围for,范围for就是傻傻的替换迭代器!

反向

list<int>::reverse_iterator it = lt.rbegin();//反向
while (it != lt.rend())
{
	cout << *it << " ";
	++it;
}
cout << endl;

list<int>::const_reverse_iterator cit = clt.rbegin();//const反向
while (cit != clt.rend())
{
	cout << *cit << " ";
	++cit;
}
cout << endl;

const和非const的区别主要还是权限的问题~!如果不修改建议使用const的!!!

注意:这里的迭代器需要指定类域的原因是模板的原因,模板参数不一样就是一个类,为了让迭代器用法统且不冲突,需要指定是哪个类的迭代器~!

容量

empty

list<int> lt = { 1,2,3,4,5,6,7,8,9 };
bool ret = lt.empty();
cout << ret << endl;

size

list<int> lt = { 1,2,3,4,5,6,7,8,9 };
size_t sz = lt.size();
cout << sz << endl;

元素访问

这里文档上说的很清楚:如果是空链表的话,你去取头和尾的数据是未定义的行为!!因为当链表为空时,头尾就是哨兵位的头结点,我们只是规定头结点的next指向实际链表的第一个节点,_prev指向对后一个元素,并未规定头结点的数据域存的是啥,所以如果为空链表,你去取就是未定义行为!

这和begin迭代器不一样,迭代器是返回链表的元素,这里是返回引用!

list<int> lt = { 1,2,3,4,5,6,7,8,9 };
int front = lt.front();
int back = lt.back();

cout << front << " " << back << endl;

const list<int> clt = { 10, 20,30, 40 };
int cfront = clt.front();
int cback = clt.back();

cout << cfront << " " << cback << endl;

修改

删除的这三个是涉及右值引用!在后面的C++11那一期会专门介绍~!

assign

list<int> lt = { 1,2,3,4,5,6,7,8,9,10 };
list<int> lt2;
list<int> lt3 = { 0, 2,4,6,8 };
lt.assign(5, 1);
for (auto& e : lt)
{
	cout << e << " ";
}
cout << endl;

lt2.assign(++lt3.begin(), --lt3.end());
for (auto& e : lt2)
{
	cout << e << " ";
}
cout << endl;

这个和构造函数那里很像,但是不一样!这个是已经存的你再去把他的原来内容用指定的内容替换掉!

push_front

list<int> lt = { 1,2,3,4 };
lt.push_front(0);
for (auto& e : lt)
{
	cout << e << " ";
}
cout << endl;

pop_front

list<int> lt = { 1,2,3,4 };

lt.pop_front();
for (auto& e : lt)
{
	cout << e << " ";
}
cout << endl;

push_back

list<int> lt = { 1,2,3,4 };

lt.push_back(-5);
for (auto& e : lt)
{
	cout << e << " ";
}
cout << endl;

pop_back

list<int> lt = { 1,2,3,4 };

lt.pop_back();
for (auto& e : lt)
{
	cout << e << " ";
}
cout << endl;

insert

list<int> lt = { 1,2,3,4 };
lt.insert(lt.begin(), 0);//在pos位置插入一个val

lt.insert(++lt.begin(), 5, -1);//在pos位置插入n个val

vector<int> v = { 90, 98, 23,34,56 };
lt.insert(lt.begin(), v.begin(), v.end());//在pos位置插入一个迭代器区间

erase

list<int> lt = { 1,2,3,4 };
lt.erase(lt.begin());//删除pos位置的元素

lt.erase(++lt.begin(), lt.end());//删除一段迭代器区间

resize

由于是链表,所以不用考虑扩容的问题!这里的resize就要变成了尾插和尾插了!

list<int> lt = { 1,2,3,4 };

lt.resize(3);//相当于尾删,只保留前n个元素

lt.resize(10, 0);//相当于尾插到节点数目为10,不够的就是0

swap

还是和前面的两个容器的一样,他这里的是对list对象的属性进行交换!

list<int> lt1 = { 1,2,3,4 };
list<int> lt2 = { 10,20,30,40 };

lt1.swap(lt2);

clear

list<int> lt = { 1,2,3,4 };

lt.clear();
cout << lt.size() << endl;

其他操作

splice

splice是粘结,结合的意思!这个接口的作用是转移链表的元素!重载了三个:

将一个链表的数据转移到另一个链表的pos位置

将一个链表的i位置的元素转移到另一个链表的pos位置

将一个链表的一个迭代器区间转移到另一个链表的pos位置

list<int> lt1 = { 1,2,3,4,5,6,7,8,9 };
list<int> lt2 = { 0, -1,-2 };

lt1.splice(lt1.begin(), lt2);//在pos位置,将x转移过来

lt2.splice(lt2.end(), lt1, ++lt1.begin());//在pos位置将x的i位置的一个元素转移过来

lt2.splice(++lt2.begin(), lt1, ++lt1.begin(), --lt1.end());//在pos位置将x的一段迭代器区间给转移过来

remove

这个函数的作用是:删除所有特定的值!

list<int> lt = { 2,3,2,2,2,4,5,6,1,2 };
lt.remove(2);
print(lt);

remove_if

这个函数的作用是删除符合条件的元素,这里的形参可以是一个对象,也可以是一个函数指针!

bool is_odd(const int& val)
{
	return val % 2 == 0;
}

struct single_digit
{
	bool operator() (const int& val)
	{
		return val < 10;
	}
};

void test_list7()
{
	list<int> lt = { 2,3,2,2,2,4,5,6,1,2,21,11,23 };

	lt.remove_if(is_odd);
	print(lt);

	lt.remove_if(single_digit());
	print(lt);
}

unique

一看名字就知道这是去重的,但是这个去重是去重连续相邻的重复元素

list<int> lt = { 2,3,2,2,2,2,4,5,6,1,2,21,11,23 };

lt.unique();
print(lt);

merge

这接口的作用就是:合并两个链表!但注意:这两个链表必须是有序的!!!!

list<int> lt1 = { 1,3,7,8,9 };
list<int> lt2 = { 2,4,5,7,9 };

lt1.merge(lt2);
print(lt1);
print(lt2);//此时lt2是空的

sort

这个接口的作用是:对链表进行排序!它的底层是归并排序!

list<int> lt = { 1,3,2,1,-1,7,8,9 };

lt.sort();
print(lt);

但是这个效率就很,,,不太好~!不是归并不行,是链表排序不太行!

OK,举个例子:

void test_op1()
{
	srand(time(0));
	const int N = 1000000;

	list<int> lt1;
	list<int> lt2;

	vector<int> v;

	for (int i = 0; i < N; ++i)
	{
		auto e = rand() + i;
		lt1.push_back(e);
		v.push_back(e);
	}

	int begin1 = clock();
	// 
	sort(v.begin(), v.end());
	int end1 = clock();

	int begin2 = clock();
	lt1.sort();
	int end2 = clock();

	printf("vector sort:%d\n", end1 - begin1);
	printf("list sort:%d\n", end2 - begin2);
}

这个代码是将同样的数据插入到一个vector和一个list,分别对他们排序,看他们排序花费的时间!

差了两倍多!!!再来看一个:

void test_op2()
{
	srand(time(0));
	const int N = 1000000;

	list<int> lt1;
	list<int> lt2;

	for (int i = 0; i < N; ++i)
	{
		auto e = rand();
		lt1.push_back(e);
		lt2.push_back(e);
	}

	int begin1 = clock();
	// vector

	vector<int> v(lt2.begin(), lt2.end());
	// 
	sort(v.begin(), v.end());

	// lt2
	lt2.assign(v.begin(), v.end());

	int end1 = clock();

	int begin2 = clock();
	lt1.sort();
	int end2 = clock();

	printf("list copy vector sort copy list sort:%d\n", end1 - begin1);
	printf("list sort:%d\n", end2 - begin2);
}

这个代码是:先将两个链表插入相同的数据,在将一个放到vector中排序,然后再拷回来,一个是直接调用链表的sort!

直接差了4倍!所以,list的这个sort效率真不咋地,建议少用~!

reverse

这个函数的作用就是反转链表!

list<int> lt = { 1,2,3,4,5,6,7,8 };
lt.reverse();
print(lt);

非成员函数swap

有这个接口的原因和前面的几个容器一样!防止调到标准库里面的那个!

list<int> lt1 = { 1,2,3};
list<int> lt2 = { 10,20,30, 50};

swap(lt1, lt2);
print(lt1);
print(lt2);

OK,本期内容就分享到这里,我们下期再见~!

结束语:不要因为别人的三言两语就打破你的深思熟虑!

相关推荐

  1. List使用

    2024-04-06 19:36:05       31 阅读
  2. 【C++】list使用

    2024-04-06 19:36:05       9 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-04-06 19:36:05       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-04-06 19:36:05       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-04-06 19:36:05       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-04-06 19:36:05       18 阅读

热门阅读

  1. GPS经纬度坐标转换

    2024-04-06 19:36:05       15 阅读
  2. 代码随想录 day39 第九章 动态规划part02

    2024-04-06 19:36:05       10 阅读
  3. 【云原生篇】K8S配置管理之ConfigMap 和 Secret

    2024-04-06 19:36:05       15 阅读
  4. Python SQLite数据库中处理空值几种方法

    2024-04-06 19:36:05       13 阅读
  5. 洛谷P1000-P1001题解

    2024-04-06 19:36:05       18 阅读
  6. 【C++】 二叉搜索树复习+模拟实现

    2024-04-06 19:36:05       12 阅读
  7. uview2 表单Form校验validate不生效处理方法

    2024-04-06 19:36:05       16 阅读
  8. Python数据分析与可视化笔记 十 关联

    2024-04-06 19:36:05       14 阅读