🔥个人主页: Forcible Bug Maker
🔥专栏: STL || C++
目录
前言
本篇博客主要内容:STL库中list用法的讲解。
让我们接着上一篇博文的内容继续,进入list最后一个模块,操作list对象的接口函数。
🔥操作list对象的接口函数(opeartions)
splice
从x中转移元素到容器中,并将它们插入到指定的位置(功能相当于剪切)。
这实际上是将这些元素插入到容器中,并从x中移除它们,从而改变两个容器的大小。这个操作不涉及任何元素的构造或销毁。元素被转移,无论x是左值还是右值,或者value_type是否支持移动构造。
entire list (1)
void splice (iterator position, list& x);
single element (2)
void splice (iterator position, list& x, iterator i);
element range (3)
void splice (iterator position, list& x, iterator first, iterator last);
第一个版本(1)将x中的所有元素转移到容器中position指向的元素之前。
第二个版本(2)仅将x中由i指向的元素转移到容器中position指向的元素之前。
第三个版本(3)将x中迭代器范围[first,last)
中的元素转移到容器中position指向的元素之前。
代码案例:
// splicing lists
#include <iostream>
#include <list>
int main ()
{
std::list<int> mylist1, mylist2;
std::list<int>::iterator it;
// set some initial values:
for (int i=1; i<=4; ++i)
mylist1.push_back(i); // mylist1: 1 2 3 4
for (int i=1; i<=3; ++i)
mylist2.push_back(i*10); // mylist2: 10 20 30
it = mylist1.begin();
++it; // 迭代器指向 2
mylist1.splice (it, mylist2); // mylist1: 1 10 20 30 2 3 4
// mylist2 (empty)
// "it" 迭代器仍然指向 2 (第五个元素)
mylist2.splice (mylist2.begin(),mylist1, it);
// mylist1: 1 10 20 30 3 4
// mylist2: 2
// "it"迭代器现在失效了
it = mylist1.begin();
std::advance(it,3); // "it" 迭代器现在指向 30
mylist1.splice ( mylist1.begin(), mylist1, it, mylist1.end());
// mylist1: 30 3 4 1 10 20
std::cout << "mylist1 contains:";
for (it=mylist1.begin(); it!=mylist1.end(); ++it)
std::cout << ' ' << *it;
std::cout << '\n';
std::cout << "mylist2 contains:";
for (it=mylist2.begin(); it!=mylist2.end(); ++it)
std::cout << ' ' << *it;
std::cout << '\n';
return 0;
}
remove
void remove (const value_type& val);
该函数(list::remove
)从容器中移除所有与给定值val
相等的元素。这会调用这些对象的析构函数,并通过移除的元素数量来减少容器的大小。
与成员函数list::erase
不同,list::erase
是通过元素的位置(使用迭代器)来删除元素的,而list::remove
则是通过元素的值来删除元素的。
还存在一个类似的函数list::remove_if
,它允许通过除了相等性比较之外的条件来确定是否移除一个元素。
代码案例:
// remove from list
#include <iostream>
#include <list>
int main()
{
int myints[] = { 17,89,7,14 };
std::list<int> mylist(myints, myints + 4);
mylist.remove(89);
std::cout << "mylist contains:";
for (std::list<int>::iterator it = mylist.begin(); it != mylist.end(); ++it)
std::cout << ' ' << *it;
std::cout << '\n';
return 0;
}
remove_if
template <class Predicate>
void remove_if (Predicate pred);
该函数从容器中移除所有使得所给定谓词(Predicate)pred
返回true的元素。这会调用这些对象的析构函数,并通过移除的元素数量来减少容器的大小。
对于容器中的每个元素(其中i是指向该元素的迭代器),该函数会调用pred(*i)
。列表中任何使得pred(*i)
返回true的元素都将从容器中移除。
pred:可以是一个函数指针或函数对象,它接受一个与
forward_list
对象中元素类型相同的值,并返回一个布尔值。对于要从容器中移除的值,该谓词返回true;对于保留在容器中的值,返回false。这样,你可以通过提供一个这样的谓词来定制forward_list中元素的移除规则。
代码案例:
// list::remove_if
#include <iostream>
#include <list>
// 函数:
bool single_digit(const int& value) { return (value < 10); }
// 仿函数,一个类伪装成的函数:
struct is_odd {
bool operator() (const int& value) { return (value % 2) == 1; }
};
int main()
{
int myints[] = { 15,36,7,17,20,39,4,1 };
std::list<int> mylist(myints, myints + 8); // 15 36 7 17 20 39 4 1
mylist.remove_if(single_digit); // 15 36 17 20 39
mylist.remove_if(is_odd()); // 36 20
std::cout << "mylist contains:";
for (std::list<int>::iterator it = mylist.begin(); it != mylist.end(); ++it)
std::cout << ' ' << *it;
std::cout << '\n';
return 0;
}
unique
(1)
void unique();
(2)
template <class BinaryPredicate>
void unique (BinaryPredicate binary_pred);
没有参数的版本(1)会移除容器中每个连续相等元素组中除了第一个元素以外的所有元素。
请注意,当一个元素只有当它与它前面紧挨着的元素相等时,才会从列表中移除。因此,这个函数特别适用于已排序的列表。
第二个版本(2)接受一个特定的比较函数作为参数,用于确定元素的“唯一性”。实际上,可以实现任何行为(而不仅仅是相等性比较),但请注意,该函数会对所有元素对(其中i是元素迭代器,从第二个元素开始)调用binary_pred(*i,*(i-1))
,如果谓词返回true,则将i从列表中移除。
被移除的元素的空间会被释放。
代码案例:
// list::unique
#include <iostream>
#include <cmath>
#include <list>
// a binary predicate implemented as a function:
bool same_integral_part(double first, double second)
{
return (int(first) == int(second));
}
// a binary predicate implemented as a class:
struct is_near {
bool operator() (double first, double second)
{
return (fabs(first - second) < 5.0);
}
};
int main()
{
double mydoubles[] = { 12.15, 2.72, 73.0, 12.77, 3.14,
12.77, 73.35, 72.25, 15.3, 72.25 };
std::list<double> mylist(mydoubles, mydoubles + 10);
mylist.sort(); // 2.72, 3.14, 12.15, 12.77, 12.77,
// 15.3, 72.25, 72.25, 73.0, 73.35
mylist.unique(); // 2.72, 3.14, 12.15, 12.77
// 15.3, 72.25, 73.0, 73.35
mylist.unique(same_integral_part); // 2.72, 3.14, 12.15
// 15.3, 72.25, 73.0
mylist.unique(is_near()); // 2.72, 12.15, 72.25
std::cout << "mylist contains:";
for (std::list<double>::iterator it = mylist.begin(); it != mylist.end(); ++it)
std::cout << ' ' << *it;
std::cout << '\n';
return 0;
}
merge
(1)
void merge (list& x);
(2)
template <class Compare>
void merge (list& x, Compare comp);
合并有序链表:
该函数通过将x中的所有元素按其各自的有序位置转移到容器中,来将x合并到列表中(两个容器都应当已经是有序的)。
这实际上移除了x中的所有元素(x变为空),并将它们插入到容器中的有序位置(容器的大小会增加转移的元素数量)。这个操作不需要构造或销毁任何元素:它们是被转移的,无论x是左值还是右值,或者value_type是否支持移动构造。
带有两个参数的模板版本(2)具有相同的行为,但是它们接受一个特定的谓词(comp)来执行元素之间的比较操作。这个比较应当产生元素的一个严格弱序(即,一个一致的传递性比较,不考虑其自反性)。
这个函数要求列表容器在调用之前已经根据值(或根据comp)对其元素进行了排序。对于无序列表的替代方案,请参见list::splice
。
假设存在这样的排序,x中的每个元素都根据其值插入到由<运算符或comp
定义的严格弱序所对应的位置。结果中,等价元素的顺序是稳定的(即,等价元素保持它们在调用之前的相对顺序,并且现有元素优先于从x中插入的等价元素)。
如果&x == this
(即,尝试将一个列表合并到它自己中),则该函数什么也不做。
代码案例:
// list::merge
#include <iostream>
#include <list>
// compare only integral part:
bool mycomparison(double first, double second)
{
return (int(first) < int(second));
}
int main()
{
std::list<double> first, second;
first.push_back(3.1);
first.push_back(2.2);
first.push_back(2.9);
second.push_back(3.7);
second.push_back(7.1);
second.push_back(1.4);
first.sort();
second.sort();
first.merge(second);
// (second is now empty)
second.push_back(2.1);
first.merge(second, mycomparison);
std::cout << "first contains:";
for (std::list<double>::iterator it = first.begin(); it != first.end(); ++it)
std::cout << ' ' << *it;
std::cout << '\n';
return 0;
}
sort
(1)
void sort();
(2)
template <class Compare>
void sort (Compare comp);
排序list对象中的元素:
该函数会对列表中的元素进行排序,改变它们在容器中的位置。
排序操作通过应用一个算法来完成,该算法使用 < 运算符(在版本 (1) 中)或 comp(在版本 (2) 中)来比较元素。这个比较应当产生元素的一个严格弱序(即,一个一致的传递性比较,不考虑其自反性)。
排序后,等价元素的顺序是稳定的:即,等价元素保持它们在调用之前的相对顺序。
整个操作不涉及任何元素对象的构造、销毁或复制。元素在容器内部进行移动。
#include<iostream>
#include<list>
class great
{
public:
bool operator()(int x, int y)
{
return x > y;
}
};
int main()
{
std::list<int> lt({ 9,3,4,7,1 });
for (auto e : lt)std::cout << e << " ";
std::cout << std::endl;
lt.sort();
for (auto e : lt)std::cout << e << " ";
std::cout << std::endl;
lt.sort(great());
for (auto e : lt)std::cout << e << " ";
std::cout << std::endl;
return 0;
}
该排序的底层是堆排序,速度会比快排慢上2~3倍。同时algorithm中的sort不支持排序list对象的元素,因为链表由于其自生结构特点难以实现快排的逻辑。
reverse
void reverse();
反转list对象中的元素。
代码案例:
#include<iostream>
#include<list>
int main()
{
std::list<int> lt({ 9,3,4,7,1 });
for (auto e : lt)std::cout << e << " ";
std::cout << std::endl;
lt.reverse();
for (auto e : lt)std::cout << e << " ";
std::cout << std::endl;
return 0;
}
结语
本篇文章所讲到的list内容出自于同一个模块,由于其排序和合并的方式涉及到了仿函数的传递,所以内容和篇幅稍微会大一些。相信学完本篇内容之后,能对list的使用和C++有更充分的了解。
博主后续还会产出更多有趣的的内容,感谢大家的支持。♥