List (双向链表)

来源:互联网 发布:谢国芳 知乎 编辑:程序博客网 时间:2024/04/29 20:41

参考:http://www.360doc.com/content/11/0526/14/2150347_119530996.shtml

一、List是一个线性链表结构,它的数据由若干个节点构成,每一个节点都包括一个信息块(即实际存储的数据)、一个前驱指针和一个后驱指针。它无需分配指定的内存大小且可以任意伸缩,这是因为它存储在非连续的内存空间中,并且由指针将有序的元素链接起来。

由于其结构的原因,list 随机检索的性能非常的不好,因为它不像vector 那样直接找到元素的地址,而是要从头一个一个的顺序查找,这样目标元素越靠后,它的检索时间就越长。检索时间与目标元素的位置成正比。
虽然随机检索的速度不够快,但是它可以迅速地在任何节点进行插入和删除操作。因为list 的每个节点保存着它在链表中的位置,插入或删除一个元素仅对最多三个元素有所影响,不像vector 会对操作点之后的所有元素的存储地址都有所影响,这一点是vector 不可比拟的。

二、list 的特点:
1、不使用连续的内存空间这样可以随意地进行动态操作;
2、可以在内部任何位置快速地插入或删除,当然也可以在两端进行push和pop 。
3、不能进行内部的随机访问,即不支持[ ] 操作符和vector.at() ;
Lists将元素按顺序储存在链表中,与向量(vectors)相比,它允许快速的插入和删除,但是随机访问却比较慢.


三、List 构造函数

1、我们已经象这样定义了list:  list<int> Fred;
2、同时初始化它的元素: 

  // define a list of 10 elements and initialise them all to 0  

 list<int> Fred(10, 1);   // list now contains 1,1,1,1,1,1,1,1,1,1

3、定义一个list并用另一个STL容器的一个范围来初始化它,这个STL容器不一定是一个list,仅仅需要是元素类型相同的的容器就可以。 
vector<int> tempVector;  
tempVector.push_back(4);  
tempVector.push_back(5); 
  // define a list and initialise it with the elements in Harry  
 list<int> tempList(tempVector.begin(), tempVector.end()); 

  // tempList now contains 1,2

四、其他成员函数

1.assign() 给list赋值
语法:
void assign( input_iterator start, input_iterator end );
//以迭代器start和end指示的范围为list赋值
void assign( size_type num, const TYPE &val );
//赋值num个以val为值的元素。
2.back() 返回最后一个元素的引用
3.begin() 返回指向第一个元素的迭代器
4.clear() 删除所有元素
5.empty() 如果list是空的则返回true
6.end() 返回末尾的迭代器
7.erase() 删除一个元素
语法:
iterator erase( iterator loc );//删除loc处的元素
iterator erase( iterator start, iterator end ); //删除start和end之间的元素
8.front() 返回第一个元素的引用
9.get_allocator() 返回list的配置器
10.insert() 插入一个元素到list中
语法:
iterator insert( iterator loc, const TYPE &val );
//在指定位置loc前插入值为val的元素,返回指向这个元素的迭代器,

void insert( iterator loc, size_type num, const TYPE &val );
//定位置loc前插入num个值为val的元素
void insert( iterator loc, input_iterator start, input_iterator end );
//在指定位置loc前插入区间[start, end)的所有元素
11.max_size() 返回list能容纳的最大元素数量
12.merge() 合并两个list
语法:

void merge( list &lst );//把自己和lst链表连接在一起
void merge( list &lst, Comp compfunction );
//指定compfunction,则将指定函数作为比较的依据。
13.pop_back() 删除最后一个元素
14.pop_front() 删除第一个元素
15.push_back() 在list的末尾添加一个元素
16.push_front() 在list的头部添加一个元素
17.rbegin() 返回指向第一个元素的逆向迭代器
18.remove() 从list删除元素
语法:
void remove( const TYPE &val );
//删除链表中所有值为val的元素
19.remove_if() 按指定条件删除元素
20.rend() 指向list末尾的逆向迭代器
21.resize() 改变list的大小
语法:
void resize( size_type num, TYPE val );
//把list的大小改变到num。被加入的多余的元素都被赋值为val22.
22.reverse() 把list的元素倒转
23.size() 返回list中的元素个数
24.sort() 给list排序//通常的sort()算法需要随机存取的iterator,但是List不能随机存取,这就是为什么我们需要一个特别的list成员函数sort()。

语法:
void sort();//为链表排序,默认是升序
void sort( Comp compfunction );//采用指定函数compfunction来判定两个元素的大小。
25.splice() 合并两个list
语法:
void splice( iterator pos, list &lst );//把lst连接到pos的位置
void splice( iterator pos, list &lst, iterator del );//插入lst中del所指元素到现链表的pos上
void splice( iterator pos, list &lst, iterator start, iterator end );//用start和end指定范围。
26.swap() 交换两个list
语法:
void swap( list &lst );// 交换lst和现链表中的元素
27.unique() 删除list中重复的元素
语法:
void unique();//删除链表中所有重复的元素
void unique( BinPred pr );// 指定pr,则使用pr来判定是否删除。


五、用STL的通用函数处理list的元素

1、用for循环来处理list中的元素
    我们想要遍历一个list,比如打印一个中的所有对象来看看list上不同操作的结果。要一个元素一个元素的遍历一个list,我们可以这样做: 

/* || How to print the contents of a simple STL list. Whew! */  #include <iostream.h> #include <string> #include <list>  int main (void) {list<string> Milkshakes; list<string>::iterator MilkshakeIterator; Milkshakes.push_back("Chocolate");  Milkshakes.push_back("Strawberry"); Milkshakes.push_front("Lime"); Milkshakes.push_front("Vanilla");   // print the milkshakes  Milkshakes.push_front("The Milkshake Menu"); Milkshakes.push_back("*** Thats the end ***");  for(MilkshakeIterator=Milkshakes.begin(); MilkshakeIterator!=Milkshakes.end();++MilkshakeIterator) {  // dereference the iterator to get the element    cout << *MilkshakeIterator << endl;  }  }

这个程序定义了一个iterator,MilkshakeIterator。我们把它指向了这个list的第一个元素。这可以调用Milkshakes.begin()来作到,它会返回一个指向list开头的iterator。然后我们把它和Milkshakes.end()的返回值来做比较,当我们到了那儿的时候就停下来。 
容器的end()函数会返回一个指向容器的最后一个位置的iterator。当我们到了那里,就停止操作。我们不能不理容器的end()函数的返回值。我们仅知道它意味着已经处理到了这个容器的末尾,应该停止处理了。所有的STL容器都要这样做。 
在上面的例子中,每一次执行for循环,我们就重复引用iterator来得到我们打印的字符串。 
这个list容器,就象你所想的,它不支持在iterator加一个数来指向隔一个的对象。就是说,我们不能用Milkshakes.begin()+2来指向list中的第三个对象,因为STL的list是以双链的list来实现的,它不支持随机存取。vector和deque(向量和双端队列)和一些其他的STL的容器可以支持随机存取。

2、用STL的通用算法for_each来处理list中的元素

/* || How to print a simple STL list MkII */ #include <iostream.h> #include <string> #include <list>#include <algorithm>void PrintIt (string& StringToPrint){        cout << StringToPrint << endl;}int main (void){ list<string>FruitAndVegetables; FruitAndVegetables.push_back("carrot"); FruitAndVegetables.push_back("pumpkin"); FruitAndVegetables.push_back("potato"); FruitAndVegetables.push_front("apple"); FruitAndVegetables.push_front("pineapple");for_each (FruitAndVegetables.begin(), FruitAndVegetables.end(), PrintIt); }


在这个程序中我们使用STL的通用算法for_each()来遍历一个iterator的范围,然后调用PrintIt()来处理每个对象。我们不需要初始化、比较和给iterator增量。for_each()为我们漂亮的完成了这些工作。我们执行于对象上的操作被很好的打包在这个函数以外了,我们不用再做那样的循环了,我们的代码更加清晰了。 
  for_each算法引用了iterator范围的概念,这是一个由起始iterator和一个末尾iterator指出的范围。起始iterator指出操作由哪里开始,末尾iterator指明到哪结束,但是它不包括在这个范围内。 

3、用STL的通用算法count()来统计list中的元素个数。
      STL的通用算法count()和count_it()用来给容器中的对象记数。就象for_each()一样,count()和count_if() 算法也是在iterator范围内来做的。 
    
      让我们在一个学生测验成绩的list中来数一数满分的个数。这是一个整型的List。 

/* || How to count objects in an STL list */ #include <list> #include <algorithm>  int main (void) {  list<int> Scores; Scores.push_back(100); Scores.push_back(80); Scores.push_back(45);  Scores.push_back(75);  Scores.push_back(99); Scores.push_back(100);  int NumberOf100Scores(0); NumberOf100Scores = count (Scores.begin(), Scores.end(), 100); cout << "There were " << NumberOf100Scores << " scores of 100" << endl; }

The count() algorithm counts the number of objects equal to a certain value. In the above example it checks each integer object in a list against 100. It increments the variable NumberOf100Scores each time a container object equals 100. The output of the program is count()算法统计等于某个值的对象的个数。上面的例子它检查list中的每个整型对象是不是100。每次容器中的对象等于100,它就给NumberOf100Scores加1。
这是程序的输出: 
There were 2 scores of 100


4、用STL的通用算法count_if()来统计list中的元素个数

(1)count_if()是count()的一个更有趣的版本。他采用了STL的一个新组件,函数对象。count_if() 带一个函数对象的参数。函数对象是一个至少带有一个operator()方法的类。有些STL算法作为参数接收函数对象并调用这个函数对象的operator()方法。 

函数对象被约定为STL算法调用operator时返回true或false。它们根据这个来判定这个函数。举个例子会说的更清楚些。count_if()通过传递一个函数对象来作出比count()更加复杂的评估以确定一个对象是否应该被记数。

在这个例子里我们将数一数牙刷的销售数量。我们将提交包含四个字符的销售码和产品说明的销售记录。 

/* || Using a function object to help count things */ #include <string>  #include <list> #include <algorithm> const string ToothbrushCode("0003");  class IsAToothbrush {  public:     bool operator() ( string& SalesRecord )    {        return SalesRecord.substr(0,4)==ToothbrushCode;    }  }; int main (void){     list<string> SalesRecords;     SalesRecords.push_back("0001 Soap");     SalesRecords.push_back("0002 Shampoo");     SalesRecords.push_back("0003 Toothbrush");     SalesRecords.push_back("0004 Toothpaste");     SalesRecords.push_back("0003 Toothbrush");     int NumberOfToothbrushes(0);     NumberOfToothbrushes = count_if (SalesRecords.begin(), SalesRecords.end(), IsAToothbrush());    cout << "There were " << NumberOfToothbrushes << " toothbrushes sold" << endl;}

这是这个程序的输出: 
There were 2 toothbrushes sold(一共卖了两把牙刷)
这个程序是这样工作的:定义一个函数对象类IsAToothbrush,这个类的对象能判断出卖出的是否是牙刷。如果这个记录是卖出牙刷的记录的话,函数调用operator()返回一个true,否则返回false。 
count_if()算法由第一和第二两个iterator参数指出的范围来处理容器对象。它将对每个 IsAToothbrush()返回true的容器中的对象增加NumberOfToothbrushes的值。 最后的结果是NumberOfToothbrushes这个变量保存了产品代码域为"0003"的记录的个数,也就是牙刷的个数。 

注意:count_if()的第三个参数IsAToothbrush(),它是由它的构造函数临时构造的一个对象。你可以把IsAToothbrush类的一个临时对象传递给count_if()函数。count_if()将对该容器的每个对象调用这个函数。 

(2)使用count_if()的一个更加复杂的函数对象。
 我们可以更进一步的研究一下函数对象。假设我们需要传递更多的信息给一个函数对象。我们不能通过调用operator来作到这点,因为必须定义为一个list的中的对象的类型。 然而我们通过为IsAToothbrush指出一个非缺省的构造函数就可以用任何我们所需要的信息来初始化它了。 例如,我们可能需要每个牙刷有一个不定的代码。我们可以把这个信息加到下面的函数对象中: 

/* || Using a more complex function object */ #include <iostream.h>  #include <string> #include <list> #include <algorithm>  class IsAToothbrush {  public:     IsAToothbrush(string& InToothbrushCode) : ToothbrushCode(InToothbrushCode) {}     bool operator() (string& SalesRecord)    {         return SalesRecord.substr(0,4)==ToothbrushCode;    }  private:      string ToothbrushCode; }; int main (void) {     list<string> SalesRecords;    SalesRecords.push_back("0001 Soap");     SalesRecords.push_back("0002 Shampoo");     SalesRecords.push_back("0003 Toothbrush");     SalesRecords.push_back("0004 Toothpaste");     SalesRecords.push_back("0003 Toothbrush");     string VariableToothbrushCode("0003");    int NumberOfToothbrushes(0);     count_if (SalesRecords.begin(), SalesRecords.end(), IsAToothbrush(VariableToothbrushCode), NumberOfToothbrushes);    cout << "There were "<< NumberOfToothbrushes << " toothbrushes matching code " << VariableToothbrushCode << " sold" << endl; }

程序的输出是: 
There were  2 toothbrushes matching code 0003 sold
这个例子演示了如何向函数对象传递信息。你可以定义任意你想要的构造函数,你可以再函数对象中做任何你想做的处理,都可以合法编译通过。 
函数对象真的扩展了基本记数算法。 




原创粉丝点击