您的位置:首页 > 编程语言 > C语言/C++

C++容器用法简介——list

2016-08-06 17:17 507 查看

C++容器用法简介——list

翻译自cplusplus

原文链接

一、简介

        List是一种的顺序容器,它允许你在任何地方以常量的时间完成插入或者删除操作(因为链表在删除或增加的的时候只是简单的修改一下指针的指向,是在O(1)的时间内完成的),List的迭代器是双向的。
        List容器在实现上采用双向链表,这种链表能存储一些位置不同且不相关的元素(这里原文没看懂随便翻译的啦~),元素通过自己的指针域来指向下一个结点,或者上一个结点,通过这种方式可以使链表保持一定的顺序。
        List容器和forward_listC++11以上版本新增的)很像,唯一的区别是forward_list是单向链表,所以forward_list的迭代器是单向的,不过forward_list拥有更小的存储空间和更高的效率(因此我们在使用链表的时候,如果明确知道这是一个单向的链表应该优先使用forward_list
       和其他的标准顺序容器(比如array(数组 C++11),vector(向量),deque(双端队列))相比,List获得迭代器的时候(实际上就是当指针指向这个地方的时候,如果迭代器没有到位,实际上这些操作时间复杂度也是O(n),因为你首先需要找到它们,原因在一下段中给出了),在任何位置的插入,移动,获取元素操作会更加的高效。所以在算法中经常出现,比如排序算法(不知道这里的algorithms,是指常规意义的算法还是说algorithm这个头文件

       但是,不管是List还是forward_list和其他顺序容器相比,它们失去了随机读取的特性,例如:为了访问列表里面的第6个元素,你不得不花费线性(时间复杂度O(n))的时间,使用迭代器从List的某个已知位置(头或者尾这种地方)一直循环到这里。另外由于需要存储下一个结点或者上一个结点的位置信息,需要单独使用一个或者多个指针,会消耗过多的内存空间。尤其是使用大的链表存储小容量的数据时。(比如在链表中每个结点放置一个char,指针所占的空间已经超过数据了,指针的大小一般是4个字节,64位的电脑的话应该是8个字节,但是一个char才1个字节

二、容器特点

1.序列

每个List里面都是严格的线性顺序,每个元素可以通过它们的位置来访问。

2.双向链表

每个元素都存储了关于下一个和上一个元素位置的信息,允许在常数时间内对特定的元素完成插入和删除操作(甚至是整个链表),但是不能随机访问(比如数组a[1]

3.动态内存分配

List通过动态内存分配需要的存储空间(C++的new,或者是C语言的malloc

三、函数用法示例

1、构造与析构(C++11版本)

const allocator_type& alloc = allocator_type()这句话是STL里面为了合理分配空间定义的类,作为默认参数,如果需要使用自己的分配方式请参阅std::allocator

//default (1)
//构造一个空的链表。
explicit list (const allocator_type& alloc = allocator_type());
list<int>list1;

//fill (2)
//构造一个拥有n个元素的链表,每个元素的值为val(如果提供的话,没有一般就是按照全局变量的方式初始化)
explicit list (size_type n);
list (size_type n, const value_type& val,
const allocator_type& alloc = allocator_type());
list<int>list2(10);      //0->0->0->......->0
list<int>list2(10, 1);  //1->1->1->.......->1

//range (3)
//从区域<tt>[first,last)</tt>中构造一个链表,顺序保持不变
template <class InputIterator>
list (InputIterator first, InputIterator last,
const allocator_type& alloc = allocator_type());
int num[] = {1, 2, 3, 4};
list<int>list3(num, num + 2);    //1->2

//copy (4)
//从另一个链表中复制相同的元素,且顺序不变,拷贝构造函数
list (const list& x);
list (const list& x, const allocator_type& alloc);
list<int>list4_1(10, 1);
list<int>list4_2(list4_1);

//move (5)
//C++11提供的move语义,可以是效率更高,避免复制
list (list&& x);
list (list&& x, const allocator_type& alloc);

//initializer list (6)
//C++11允许构造函数和其他函数把初始化列表当做参数。
list (initializer_list<value_type> il,
const allocator_type& alloc = allocator_type());
//list<int>list6 = {1, 2, 3, 4, 5, 6};

2.迭代器函数

2.1 begin()

返回容器中第一个元素的迭代器,和front()不同,begin()返回的只是一个指向第一个元素的双向迭代器而已,如果容器为空返回的迭代器是无效的。

例如:

list<int>l1 = {1, 2, 3, 4, 5};
cout << *l1.begin() << endl;   //输出1


2.2 cbegin()

返回一个const_iterator,与前者的区别是const_iterator只能用于指向容器中的元素但是不能修改它,实际上begin是一个重载函数,当容器中数据是const时,自动返回const_iterator,但是cbegin()只能返回const_iterator。

list<int>l1 = {1, 2, 3, 4, 5};
cout << *l1.cbegin() << endl;   //输出1
*l1.begin() = 2;
cout << *l1.begin() << endl;    //输出2
*l1.cbegin() = 1;      //error error: assignment of read-only location


2.3 end()与cend()

[align=left]返回一个超过容器中最后一个元素的位置迭代器,所以事实上不指向任何元素,返回的引用不可用,(之所以返回一个指向最后一个元素后一个的迭代器,事实上可以简化计算,理由参考链接,其实可以发现在很多编程语言里面都是使用前闭后开区间),end()一般和begin()配合来表示整个容器。
cend()的解释和cbegin()类似,另外end()也是重载函数。
[/align]

2.4 rbegin() rend() crbegin() crend()

这四个函数和上述四个函数唯一的区别在于r字母,表示reverse(翻转,颠倒),rbegin()返回一个指向最后一个元素的迭代器,rend()返回一个超过第一个元素的迭代器,实际上这四组函数就是相当于把整个容器反过来使用(内存中并没有变化

2.5 迭代器的说明(个人补充的)

迭代器,看上去和指针是一模一样的,支持加减(不是通用的),自增自减,以及不等于操作,还有*解引用操作,配合上述的函数可以实现遍历整个容器的功能

list<int>l1 = {1, 2, 3, 4, 5};
for(auto it = l1.begin(); it != l1.end(); ++it)
{
cout << *it << endl;
}
//输出1 2 3 4 5
list<int>l1 = {1, 2, 3, 4, 5};
for(auto it = l1.rbegin(); it != l1.rend(); ++it)   //这里应该是用++,因为rbegin相当于已经把整个链表反过来了
{
cout << *it << endl;
}

//输出5 4 3 2 1

3.容量相关

3.1 empty()

返回容器是否为空(当它的大小是0的时候),empty()函数并不能清空链表,只是一个返回bool型的const函数

list<int>l1;
list<int>l2(10, 0);
cout << boolalpha << l1.empty() << endl;  //true
cout << boolalpha << l2.empty() << endl;  //false
//boolalpha是bool变量输出true和false而不是0 1

3.2 size()

返回容器中元素的个数,时间复杂度O(1),返回值类型是size_type,也就是unsigned int。

例如上面的代码段应该输出 0 和 10

3.3 max_size()

这个函数返回容器在已知系统和标准库的限制下能够容纳的元素的最大值,但是容器自身并不会保证自己不超过这个值,当到达这个值的时候申请动态空间将会失败

cplusplus的例子:

// list::max_size
#include <iostream>
#include <list>

int main ()
{
unsigned int i;
std::list<int> mylist;

std::cout << "Enter number of elements: ";
std::cin >> i;

if (i<mylist.max_size()) mylist.resize(i);
else std::cout << "That size exceeds the limit.\n";

return 0;
}
//这里max_size()来检查resize需要的空间是否已经超过最大值


4.元素访问

4.1 front() 和 back()

返回链表中第一个元素(最后一个元素)的引用。

front()和成员begin()不一样,虽然他们都指向同一元素,但是front()返回的是一个直接引用。

back()和成员end()不一样,back()返回的就是最后一个元素,同样也是直接引用

在一个空的容器上面调用这个函数会发生意想不到的后果。(是这么翻译的吧,嘿嘿

这是一个重载函数,如果容器里面的元素本来就是const类型,那么返回的引用也是const_reference

list<int>l1(10, 4);
cout << l1.front() << endl;   //输出 4
l1.front() = 6;     //由于返回的引用所以可以作为右值使用
cout << l1.front()<< endl;   //输出6
cout << l1.back() << endl;   //输出4
l1.back() = 8;
cout << l1.back() << endl;  //输出8
//链表结构4->6->4->4->8


5.修改相关(核心)

5.1 assign()

assign() 函数用于分配新的元素到容器中,替换现有的元素,并且修改它的大小(四级没过的我,一直单纯的认为assign是签署的意思

//range (1)
//和构造函数类似,通过指定迭代器来重新分配
template <class InputIterator>
void assign (InputIterator first, InputIterator last);

//fill (2)
//和构造函数一样啦~
void assign (size_type n, const value_type& val);

//initializer list (3)
//C++11特有的通过初始化列表分配
void assign (initializer_list<value_type> il);
list<int>l1;
l1.assign( {1, 2, 3} );

cplusplus的例子
#include <iostream>
#include <list>

int main ()
{
std::list<int> first;
std::list<int> second;

first.assign (7,100);                      // 7个值为100的整数

second.assign (first.begin(),first.end()); // 复制first

int myints[]={1776,7,4};
first.assign (myints,myints+3);            // 通过数组重新分配元素

std::cout << "Size of first: " << int (first.size()) << '\n';
std::cout << "Size of second: " << int (second.size()) << '\n';
return 0;
}

5.2 emplace_front() (C++11)和 push_front()  ,emplace_back()(C++11)和push_back()

在链表的最开始(最后)插入一个新的元素,右边是这个链表之前的第一个(最后一个)元素,两个函数都会改变链表的大小,使其增大1。

emplace_front()(emplace_back())和push_front()(push_back())区别在于push_front()是先是构造一个临时的局部对象,然后再通过拷贝的方式把这个临时对象拷贝到容器内,如果我们使用emplace_front()则是直接在容器空间内直接创建,不必构建临时变量并拷贝了。

但是这并不意味着emplace_front()可以取代push_front(),当链表的元素是基本数据类型(int, double, char, long long)时,必须使用push_front(),因为这些类型没有构造函数。

list<int>l1(2, 5);
for(auto it: l1)
cout << it << endl;   //输出5 5
//l1.emplace_place(1); //error: 'class std::list<int>' has no member named 'emplace_place'|
l1.push_front(1); //OK
for(auto it: l1)
cout << it << endl;   //输出1 5 5
class Node
{
public:
int x, y;
Node(int x, int y)
: x(x), y(y) {}
friend ostream& operator << (ostream& os, Node& A)
{
return os << A.x << " " << A.y;
}
};

int main()
{
list<Node>l1(2, {1, 2});
for(auto it : l1)
cout << it << endl;    //输出1 2   1 2
l1.push_front(Node(2, 2));
l1.push_back(Node(3, 3));
 for(auto it : l1)
cout << it << endl;     //输出2 2 1 2  1 2 3 3
l1.emplace_front(2, 4);    //表面上只是省略了Node这个单词,但是实际上实现方式已经变了,后者效率更高
l1.emplace_back(1, 5);
 for(auto it : l1)
cout << it << endl;     //输出2 4 2 2 1 2 1 2 1 5
return 0;
}

5.3 pop_front() 和 pop_back()

删除(去掉)链表的第一个元素(最后一个),这个操作将会导致链表的大小减1,这个函数会负责清除被删除元素的空间

cplusplus例子
#include <iostream>
#include <list>

int main ()
{
std::list<int> mylist;
mylist.push_back (100);
mylist.push_back (200);
mylist.push_back (300);

std::cout << "Popping out the elements in mylist:";  //弹出mylist的元素
while (!mylist.empty())
{
std::cout << ' ' << mylist.front();
mylist.pop_front();
}

std::cout << "\nFinal size of mylist is " << mylist.size() << '\n';   //输出0

return 0;
}

5.4 emplace()(C++11)

emplace()函数和emplace_back() emplace_front()的区别就是emplace()可以在任何地方插入新的元素,并且也是在容器空间内就地构造,不会产生临时变量和拷贝。

不像其他标准的顺序容器,list和forward_list被设计来用于高效的插入和删除元素的,即使元素在中间。

相同的函数还有insert() 范围更广,功能更多。emplace同样不能用于插入基本数据类型。

class Node
{
public:
int x, y;
Node(int x, int y)
: x(x), y(y) {}
friend ostream& operator << (ostream& os, Node& A)
{
return os << A.x << " " << A.y;
}
};

int main()
{
list<Node>l1(2, {1, 2});
for(auto it : l1)
cout << it << endl;
l1.emplace(l1.begin(), 2, 4);
for(auto it : l1)
cout << it << endl;    //输出2 4 1 2 1 2

auto fr = l1.begin();
fr++, fr++;    //迭代器后移2个元素  注意list的迭代器不能 + -
l1.emplace(fr, 3, 8);
for(auto it : l1)
cout << it << endl;    //输出2 4 1 2 3 8 1 2
return 0;
}

一个有意思的例子:

class Node
{
public:
int x, y;
Node(int x, int y)
: x(x), y(y) {}
friend ostream& operator << (ostream& os, Node& A)
{
return os << A.x << " " << A.y;
}
};

int main()
{
list<Node>l1(1, {1, 2});
auto fr = l1.begin();
fr++, fr++,fr++, fr++, fr++, fr++, fr++;   //这里迭代器无限增大,并不会插入异常,因为实际上list是循环队列,超过范围以后自动回到第一个元素
l1.emplace(fr, 3, 8);
for(auto it : l1)
cout << it << endl;
return 0;
}

5.5 insert()

容器通过在指定的位置之前插入元素来扩大自己,这个操作会导致列表的大小增加,增加的量为插入元素的个数,insert()函数有5个重载版本(C++11新增两个),返回值都是刚刚插入的元素的迭代器。

//single element (1)
//最简单的方式,和implace类似,在迭代器指定的位置插入元素
iterator insert (const_iterator position, const value_type& val);

//fill (2)
//在迭代器指定的位置插入n个相同的元素
iterator insert (const_iterator position, size_type n, const value_type& val);

//range (3)
//将first和last迭代器换分的区间插入到postion位置上
template <class InputIterator>
iterator insert (const_iterator position, InputIterator first, InputIterator last);

//move (4)
//C++11move语义,避免产生临时变量和拷贝
iterator insert (const_iterator position, value_type&& val);

//initializer list (5)
//C++11 初始化列表方式插入到迭代器指定位置
iterator insert (const_iterator position, initializer_list<value_type> il);

list<int>l1;
auto it = l1.begin();
l1.insert(it, 1);    //1 it指向

it++;
l1.insert(it, 2, 2);    //1 -> 2 -> 2

int num[] = {4, 5, 6};
it = l1.begin();
l1.insert(it, num, num + 3); // 4->5->6->1-> 2 -> 2

l1.insert(it, {7, 8, 9});  //7->8->9->4->5->6->1-> 2 -> 2

for(auto it : l1)
cout << it << endl;
cout << endl;


5.6 erase()

删除指定位置或者指定区间的位置的元素,只有两个重载函数,这个函数会导致链表的大小减小,减小量由被删除元素个数决定,返回值和插入操作是一样,也是刚刚被删除位置的迭代器,如果链表删除到空,返回end()

erase(begin()) => pop_front()   erase(--end(), end()) => pop_back()

list<int>l1 = {1, 2, 3 ,4, 5, 6};
l1.erase(l1.begin());
l1.pop_front();
l1.erase(--l1.end(), l1.end());
l1.pop_back();
for(auto it : l1)
cout << it << endl;    //输出3 4

5.7 swap()

交换两个类型相同的链表容器的所有元素,容器大小可以不一样。(时间复杂度为O(1),因为只是修改了指针的指而已)

在调用了这个成员函数以后,在这个容器内的元素会被x替换,x的元素同时也会被这个容器原来的元素替换,所有的迭代器,引用和指针仍然有效。但是end迭代器不指向任何元素有可能会失效。

注意在有一个位于algorithm头文件中的同名非成员函数swap(), 可以实现相同的功能。

list<int>l1 = {1, 2, 3, 4};
list<int>l2 = {5, 6};
auto it1 = l1.begin();
auto it2 = l2.begin();
cout << *it1 << " " << *it2 << endl;   // 1 5
swap(l1, l2);
auto it3 = l1.begin();
auto it4 = l2.begin();
cout << *it1 << " " << *it2 << " " << *it3 << " " << *it4 <<  endl;  // 1 5 5 1说明it1,和it2并没有失效


5.8 resize()

改变容器的大小,使得它能容纳n个元素

如果参数中的n,比当前容器的大小小,元素将会减少到这个容器的前n个,删除剩下的(resize函数会负责释放空间)

如果参数中的n,比当前容器的大小大,容器通过在尾部增加元素,使其达到n这个大小,如果val指定了参数,新的元素会被初始化为val。

请注意这个函数通过插入和删除能真正的改变容器的大小

list<int>l1 = {1, 2, 3, 4};
l1.resize(2);
//l1.resize(2, 1)  缩小容器容量的时候,给出val是没有意义的
 for(auto it : l1)
cout << it <<endl;    //输出1 2
l1.resize(5, 1);
for(auto it : l1)
cout << it <<endl;   //输出1 2 1 1 1


6.9 clear()

删除容器中的所有元素,并且使大小减为1(等价于erase(begin(), end())

list<int>l1 = {1, 2, 3, 4};
l1.clear();
cout << l1.size() << endl;    //输出0


6.操作相关

6.1 splice()

该函数用于在两个list之间移动元素。

移动x的元素,并把这些元素插入到新的指定位置(实际上相当于剪切并粘贴),并且同时改变两个容器的大小。这个操作不包括任何构造或者析构方法,仅仅只是移动元素而已,不管这个元素是左值还是右值,或者这个元素是否支持move语义。

该函数有三个版本

1) 移动整个x到新的容器   时间复杂度(O(1))

2) 移动x的某一个元素到新的容器 时间复杂度(O(1))

3) 移动x的一个区间到新的容器 时间复杂度(O(n))

cplusplus的例子

// 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;                         // points to 2

mylist1.splice (it, mylist2); // mylist1: 1 10 20 30 2 3 4
// mylist2 (empty)
// "it" still points to 2 (the 5th element)

mylist2.splice (mylist2.begin(),mylist1, it);
// mylist1: 1 10 20 30 3 4
// mylist2: 2
// "it" is now invalid.
it = mylist1.begin();
std::advance(it,3);           // "it" points now to 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;
}
//输出
<pre>//mylist1 contains: 30 3 4 1 10 20
//mylist2 contains: 2



6.2 remove()

从容器中删除所有和val值相等的元素,这个函数会自动调用被删除对象的析构函数,并且会是当前容器的大小减小,这个函数于erase的不同之处是,erase只能指定位置来实现删除,而remove()通过指定值来精确的删除。

list<int>l1 = {1, 1, 2, 2};
l1.remove(1);
for(auto it : l1)
cout << it << endl;   //输出 2 2


6.3 remove_if()

和remove函数类似,不过remove_if删除使Predicate pred返回true的元素,这个函数同样也会自动调用析构并且改变大小,(Predicate pred实际上可以理解为函数指针,对于每一个元素都调用这个函数来检查

cplusplus的例子



// list::remove_if
#include <iostream>
#include <list>

// a predicate implemented as a function: 逻辑函数
bool single_digit (const int& value) { return (value<10); }

// a predicate implemented as a class:
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;
}
//输出<samp> mylist contains: 36 20</samp>


6.4 unique()

使得链表唯一化(不含重复元素,重复根据值相同或自己定义的函数来判断)

有两个版本,版本一是无参数的,会删除每个连续的重复元素集合中,除了第一个元素以外的所有其他元素。一个元素会被删除当且仅当它与前一个相邻元素相同。

版本二通过指定一个特定的比较函数来定义元素的独特性,实际上在版本二中,任何独特性都可以自己定义(不仅仅是相等关系),但是请注意这个函数会像binary_pred(*i,*(i-1))这种形式调用,i是一对元素里面的第二个元素的迭代器,当这个函数返回true的时候,将会删除i所指向的元素。

cplusplus的例子

// 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); }     //两个浮点数之间差距小于5
};

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;
}


连续重复元素的意思:

list<int>l1 = {1, 1, 2, 1};
l1.unique();
for(auto it : l1)
cout << it << endl;    //这里输出1 2 1 而不是 1 2,因为第3个1,和前面的1并不是连续的重复

6.5 merge()

该函数用于合并已经排序的两个链表。将x里面的所有元素移动到当前链表中使得当前链表仍然保持有序。

函数有两个版本,版本二可以通过指定比较函数来确定优先级。指定的这个排序函数必须提供一种弱序化的排序方式,这个版本的函数仍然需要保持两个链表有序,否则可以使用splice替换。

每个元素的大小关系,可以通过comp函数给出,或者operator  <,排序最终结果应该是固定的,当x就是链表本身是,不做任何操作。

list<int>l1 = {2, 3, 6};
list<int>l2 = {1, 4, 5};
l1.merge(l2);
for(auto it : l1)
cout << it << endl;    //输出1 2 3 4 5 6

struct Point
{
Point(int x, int y)
:x(x), y(y){}
int x, y;
bool operator < (const Point & A)   //重载<运算符
{
if(x == A.x)
return y < A.y;
return x < A.x;
}
};

int main()
{
list<Point>l1, l2;
l1.emplace_front(1, 1);
l1.emplace_front(1, 0);
l2.emplace_front(3, 3);
l2.emplace_front(2, 2);
l1.merge(l2);
for(auto it : l1)
cout << it.x << " " << it.y << endl;
return 0;
}

struct Point
{
    Point(int x, int y)
        : x(x), y(y) {}
    int x, y;
};

bool cmp(Point A, Point B)  //指定cmp函数
{
    if(A.x == B.x)
        return A.y < B.y;
    return A.x < B.x;
}

int main()
{
    list<Point>l1, l2;
    l1.emplace_front(1, 1);
    l1.emplace_front(1, 0);
    l2.emplace_front(3, 3);
    l2.emplace_front(2, 2);
    l1.merge(l2, cmp);
    for(auto it : l1)
        cout << it.x << " " << it.y << endl;
    return 0;
}


6.6 sort()

将链表中元素排序,修改他们的位置。

排序通过指定cmp函数或者operator <来操作,(方法与上列相同),整个操作不会构造或者析构任何元素,只是在容器内部移动。

struct Point
{
Point(int x, int y)
: x(x), y(y) {}
int x, y;
bool operator < (const Point& A)    //只能使用operator < 不能使用 >
{
if(x == A.x)
return y > A.y;
return x > A.x;
}
};

bool cmp(Point A, Point B)
{
if(A.x == B.x)
return A.y < B.y;
return A.x < B.x;
}

int main()
{
list<Point>l1;
l1.emplace_front(1, 1);
l1.emplace_front(1, 0);
l1.emplace_front(3, 3);
l1.emplace_front(2, 2);
l1.sort(cmp);     //通过cmp函数实现
for(auto it : l1)
cout << it.x << " " << it.y << endl;    //输出1 0 1 1 2 2 3 3
l1.sort();  //通过operator < 来实现
for(auto it : l1)
cout << it.x << " " << it.y << endl;   //输出3 3 2 2 1 1 1 0
return 0;
}


6.7 reverse

翻转元素在容器内的顺序(和rbegin,rend迭代器不同,reverse是真正的在内存中翻转过来了,时间复杂度O(n)

cplusplus的例子

// reversing list
#include <iostream>
#include <list>

int main ()
{
std::list<int> mylist;

for (int i=1; i<10; ++i) mylist.push_back(i);

mylist.reverse();

std::cout << "mylist contains:";
for (std::list<int>::iterator it=mylist.begin(); it!=mylist.end(); ++it)
std::cout << ' ' << *it;

std::cout << '\n';

return 0;
}


7.非成员函数重载

7.1 swap()

这个函数和list的swap类似,只是这个函数是重载的,函数时间复杂度是O(1),因为它只是简单的交换了一下,使得引用指向对方的数据,并不会复制或者移动任何元素。

list<int>l1 = {1, 2, 3};
list<int>l2 = {3, 4, 5};
swap(l1, l2);
cout << l1.front() << " " << l2.front()<< endl;  //输出 3 1
int a = 1, b = 3;
swap(a, b);    //b = 1, a = 3;
   double c = 1.0, d = 3.0;
   swap(c, d);    //c = 3.0, d = 1.0
   string e("1111"), f("3333");
   swap(e, f);   //e = "3333", f="1111"


7.2 运算符重载

(1)==用于比较两个链表是否相等,首先判断大小是否相等,然后再通过operator ==判断元素之间是否相等,将会在第一个不相等的地方停止

template <class T, class Alloc>
bool operator== (const list<T,Alloc>& lhs, const list<T,Alloc>& rhs);

(2)!= 和==是相反的。只要大小不同或者有一个元素不一样就是不相等的

template <class T, class Alloc>
bool operator!= (const list<T,Alloc>& lhs, const list<T,Alloc>& rhs);

(3)从第一元素开始比较当发现大于或者等于时停止,如果一个链表是另一个链表的前缀,那么长的链表大。下同

template <class T, class Alloc>
bool operator<  (const list<T,Alloc>& lhs, const list<T,Alloc>& rhs);

(4)

template <class T, class Alloc>
bool operator<= (const list<T,Alloc>& lhs, const list<T,Alloc>& rhs);

(5)

template <class T, class Alloc>
bool operator>  (const list<T,Alloc>& lhs, const list<T,Alloc>& rhs);

(6)

template <class T, class Alloc>
bool operator>= (const list<T,Alloc>& lhs, const list<T,Alloc>& rhs);

list<int>l1 = {1, 2, 3};
list<int>l2 = {1, 2};
cout << boolalpha << (l1 > l2) << endl;   //T
cout << boolalpha << (l1 == l2) << endl;   //F
cout << boolalpha << (l1 != l2) << endl;   //T


四、结语

说实话以前真没有像这样翻译这么多东西,翻译这些东西目的只是锻炼一下自己,顺便把SLT容器的所有函数整理一下。这么多单词,某道,某度的翻译结果基本靠不住,还得自己调整语序,语文差点没及格的,外加4级都没有飘过的人,真是尴尬啊,所以有很多翻译不对或者理解有问题的地方,希望大家指出。接下来我会陆续把容器相关的知识和它们的C++11版本的函数整理出来。在翻译过程中,参考了一些资料对于C++11的解释,这里就不一一感谢了(人太多了O(∩_∩)O哈哈~)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: