码迷,mamicode.com
首页 > 其他好文 > 详细

[STL] list的使用

时间:2016-04-17 22:53:25      阅读:144      评论:0      收藏:0      [点我收藏+]

标签:

1. list功能
list是双向循环链表,每一个元素都知道前面一个元素和后面一个元素.list对象自身提供了两个pointer用来指向第一个和最后一个元素.每个元素都有pointer指向前一个和后一个元素.如果想要插入新元素,只需要操纵对应的pointer即可.因此list在几个方面与array,vector不同:
- list不支持随机访问,如果你要访问第5个元素,就得顺着串链逐一爬过前4个元素.所以在list中随机访问一个元素是很缓慢的,然而你可以从两端开始航行整个list,所以访问第一个或最后一个元素速度很快.
- 任何位置上(不只两端)执行元素的插入或删除都非常快,因为无需移动任何其他元素.实际上内部只是进行了一些pointer操作而已.
- 插入和删除动作并不会造成其他元素的各个pointer,reference和iterator失效.
- list对于异常的处理方式是:要么操作成功,要么什么都不发生,绝对不会陷入”只成功一半”这种进退维谷的境地.

list所提供的成员函数反映出它与array,vector不同:
- list提供front(),push_front()和pop_front(),back(),push_back()和pop_back()等操作函数.
- 由于不支持随机访问,list既不提供下表操作符,也不提供at().
- list并未提供容量,空间重新分配等操作函数,因为完全无必要.每个元素都有自己的内存,在这个元素删除前一直有效.

2.list操作
在使用list前必须包括头文件#include
本文例子中到的两个list对象c1,c2分别为c1(10,20,30) c2(40,50,60)。还有一个迭代器it = list::iterator用来指向c1或c2元素。

1)、list的构造函数和析构函数

list<Elem>c;             // 构造函数,产生一个空list,没有任何元素
list<Elem>c(c2);         // copy 构造函数,建立c2的同类型list,并成为c2的一份拷贝
list<Elem>c = c2;        // copy 构造函数,建立一个新的list作为c2的拷贝
list<Elem>c(rv);         // move 构造函数,建立一个新的list,取rvalue rv的内容(始自C++11)
list<Elem>c = c2;        // move 构造函数,建立一个新的list,取rvalue rv的内容(始自C++11)
list<Elem>c(initlist);   // 建立一个list,以初值列initlist的元素为初值(始自C++11)
list<Elem>c = initlist;  // 建立一个list,以初值列initlist的元素为初值(始自C++11)    
list<Elem>c(n);          // 利用元素的default构造函数生成一个大小为n的list
list<Elem>c(n,elem);     // 建一个含n个元素的list,值都是elem
list<Elem>c(c1.begin(),c1.end());  // c含c1一个区域的元素[First, _Last)。
c.~list();             // 销毁所有元素,释放内存

2)、list的非易型操作

c.empty()    // 返回容器是否为空(相当于size()==0但也许较快)
c.size()     // 返回目前的元素个数
c.max_size() // 返回元素个数之最大可能量
c1 == c2     // 返回c1是否等于c2(对每个元素调用==)
c1 != c2     // 返回c1是否不等于c2(相当于!(c1 == c2))
c1 > c2      // 返回c1是否大于c2
c1 >= c2     // 返回c1是否大于等于c2
c1 < c2      // 返回c1是否小与c2
c1 <= c2     // 返回c1是否小与等于c2

3)、assign() 给list赋值:

c.assign(n,elem);   // 复制n个elem,赋值给c
c.assign(beg,end);  // 将区间[beg,end)内的元素赋值给c
c.assign(initlist); // 将初值列initlist所有元素赋值给c

4)、swap() 交换两个list

c1.swap(c2);  // 置换c1和c2的数据
swap(c1,c2);  // 置换c1和c2的数据

5)、list元素直接访问

front() 返回第一个元素  (不检查是否存在第一个元素)
int i=c1.front(); // i=10

back() 返回最后一个元素 (不检查是否存在最后一个元素)
int i=c1.back();  // i=30

6)、迭代器相关函数

begin() 返回一个迭代器,指向第一个元素
it=c1.begin();    // *it=10

end() 返回一个迭代器,指向最后一个元素的下一位置
it=c1.end(); //*(--it)=30;

rbegin() 返回一个反向迭代器, 指向反向迭代器第一个元素 
list<int>::reverse_iterator riter=c1.rbegin(); // *riter=30

rend() 返回一个反向迭代器, 指向反向迭代器最后一个元素的下一个位置 
list<int>::reverse_iterator riter=c1.rend(); // *(--riter)=10

cbegin() 返回一个const迭代器, 指向第一个元素 (始自C++11)
list<int>::const_iterator citer=c1.cbegin(); // *citer=10且为const

cend() 返回一个const迭代器, 指向最后一个元素的下一个位置 (始自C++11)
list<int>::const_iterator citer=c1.cend(); // *(--citer)=30且为const

crbegin() 返回一个const反向迭代器, 指向反向迭代器第一个元素 (始自C++11)
list<int>::const_reverse_iterator criter=c1.crbegin(); // *criter=30且为const

crend() 返回一个const反向迭代器, 指向反向迭代器最后一个元素的下一个位置 (始自C++11)
list<int>::const_reverse_iterator criter=c1.crend(); // *(--criter)=10且为const

7)、clear() 删除所有元素

c1.clear();   //c1为空  c1.size为0;

8)、erase() 删除一个元素 , 注意事项参照下面”注意事项1”

c1.erase(pos);     // 移除pos位置上的元素,返回下一个元素的位置
c1.erase(beg,end); // 移除区间[beg,end)内所有元素,返回下一个元素的位置

9)、remove() 从list删除元素

c.remove(val);     // 移除所有其值为val的元素

10)、remove_if() 按指定条件删除元素

c1.remove_if(op); // 移除所有"造成op(elem)结果为true"的元素

11)、resize() 改变list的大小

c1.resize(num)      // 将元素数量改为num(如果size()变大,多出来的新元素都需以default构造函数完成初始化)
c1.resize(num,elem) // 将元素数量改为num(如果size()变大,多出来的新元素都是elem的拷贝)

12)、insert() 插入一个元素到list中

c.insert(pos,elem);     // 在iterator位置pos之前插入一个elem拷贝,并返回新元素的位置
c1.insert(pos,n,elem);  // 在iterator位置pos之前插入n个elem拷贝,并返回第一个新元素的位置(或返回pos--如果没有新元素的话)
c1.insert(pos,beg,end); // 在iterator位置pos之前插入区间[beg,end)内所有元素的一份拷贝,并返回第一个新元素的位置(或返回pos--如果没有新元素的话)
c1.insert(pos,initlist);// 在iterator位置pos之前插入初值列initlist内所有元素的一份拷贝,并返回第一个新元素的位置(或返回pos--如果没有新元素的话;始自C++11)

例如:
c1.insert(++c1.begin(),100);   // c1(10,100,20,30)
c1.insert(c1.begin(),2,200);   // c1(200,200,20,30);
c1.insert(++c1.begin(),c2.begin(),--c2.end()); // c1(10,40,50,20,30);
c1.insert(++c1.begin(),{100,200});   // c1(10,100,200,20,30)

13)、emplace() 插入以args为初值的元素

c.emplace(pos,args...)  // 在iterator位置pos之前插入一个以args为初值的元素,并返回新元素位置(始自C++11)
c.emplace_back(args...) // 在末尾插入一个以args为初值的元素,不返回任何东西(始自C++11)
c.emplace_front(args...) // 在起点插入一个以args为初值的元素,不返回任何东西(始自C++11)

14)、pop_back() 删除最后一个元素 ,但是不返回它

c1.pop_back();  // c1(10,20);

15)、pop_front() 删除第一个元素 ,但是不返回

c1.pop_front(); // c1(20,30)

16)、push_back() 在list的末尾添加一个元素

c1.push_back(100); // c1(10,20,30,100)

17)、push_front() 在list的头部添加一个元素

c1.push_front(100); // c1(100,10,20,30)

18)、merge() 合并两个list 并使之默认升序(也可改)

c.merge(c2)    // 假设c和c2容器都包含op()准则下的已排序元素,将c2中的全部元素转移到c,并保证合并后的list仍已排序
c.merge(c2,op) // 假设c和c2容器都包含已排序元素,将c2中的全部元素转移到c,并保证合并后的list在op()准则下已排序

例如:
c2.merge(c1);    //c1现为空;c2现为c2(10,20,30,40,50,60)
c2.merge(c1,greater<int>()); //同上,但c2现为降序

19)、reverse() 把list的元素倒转

c1.reverse(); // c1(30,20,10)

20)、sort() 给list排序, 默认升序(可自定义)

c.sort() // 以operator<为准则对所有元素排序
c.sort(op) // 以op()为准则对所有元素排序

例如:
c1.sort();  // c1(10,20,30)
c2.sort(greater<int>()); //同上,但c1现为降序

21)、splice() 合并两个list

c.splice(pos,c2) // 将c2内的所有元素转移到c之内,迭代器pos之前
c.splice(pos,c2,c2pos) // 将c2内的c2pos所指元素转移到c内pos之前
c.splice(pos,c2,c2beg,c2end) // 将c2内的[c2beg,c2end)区间内所有元素转移到c内pos之前

例如:
c1.splice(++c1.begin(),c2); //c1(10,40,50,60,20,30) c2为空 全合并
c1.splice(++c1.begin(),c2,++c2.begin()); //c1(10,50,20,30) ; c2(40,60) 指定元素合并
c1.splice(++c1.begin(),c2,++c2.begin(),c2.end()); //c1(10,50,60,20,30); c2(40) 指定范围合并

22)、unique() 删除list中重复的元素

c.unique() // 如果存在若干相邻而数值相同的元素,就移除重复元素,只留一个
c.unique(op) // 如果存在若干相邻元素都使op()的结果为true,则移除重复元素,只留一个

例如:
c1.unique(); // 假设c1开始为(-10,10,10,20,20,-10),则之后为c1(-10,10,20,-10)

以上就是list的基本用法,实际使用中还需要注意一下几点,后续遇到再加补充.

注意事项:

  1. erase使用注意 参考:http://blog.sina.com.cn/s/blog_66f74d9f0100om0f.html
    常用的删除容器中元素的方法是如下(方法1):
    list< int> List;
    list< int>::iterator iter;
    for( iter = List.begin(); iter != List.end(); )
    {
    if(1)
    {
    iter = List.erase( iter );
    }
    else
    {
    iter++;
    }
    }

    也可以这样写(方法2):
    list< int> List;
    list< int>::iterator iter;
    for( iter = List.begin(); iter != List.end(); )
    {
    if(1)
    {
    List.erase( iter++ );
    }
    else
    {
    iter++;
    }
    }

    有一种错误的写法(注意同方法2比较)
    list< int> List;
    list< int>::iterator iter;
    for( iter = List.begin(); iter != List.end(); )
    {
    if(1)
    {
    List.erase( iter );
    }

        iter++;
    

    }

    我们看一下erase()函数的源代码(仅列出release下的代码)。
    iterator erase(iterator _Where)
    { // erase element at _Where
    _Nodeptr _Pnode = (_Where++)._Mynode();

    if (_Pnode != _Myhead)
        {    // not list head, safe to erase
        _Nextnode(_Prevnode(_Pnode)) = _Nextnode(_Pnode);
        _Prevnode(_Nextnode(_Pnode)) = _Prevnode(_Pnode);
        this->_Alnod.destroy(_Pnode);
        this->_Alnod.deallocate(_Pnode, 1);
        --_Mysize;
        }
    return (_Where);
    }
    

函数在返回的时候,是返回当前迭代器的下一个节点。所以当 iter = List.erase( iter ); 执行以后,迭代器自动指向了下一个元素。而对于入参中的iter,所指的地址已经被销毁,所以写的时候,应该注意加上前面的iter =
那另外的一种写法,List.erase( iter++ ); 为什么也是对的呢?
研究了一下,这里需要讲一下++运算符的操作.
_Myt_iter& operator++()
{ // preincrement
++((_Mybase_iter )this);
return (*this);
}

    _Myt_iter operator++(int) 
        {    // postincrement
        _Myt_iter _Tmp = *this;
        ++*this;
        return (_Tmp);
        }

++实际上可以看做是一个函数。
对于++在后的情况(例如i++),函数在运行的时候,将运算的数据i已经改变,但是函数的返回值是操作之前的数据,所以在我们看来,i++好像是先进行了i的读取,才+1。

回到迭代器,List.erase( iter++ );就没有问题了。

对于那种错误的方法,List.erase( iter );在执行以后,iter所指的对象已经被销毁,所以再对iter进行操作是非法的,程序会出错。

[STL] list的使用

标签:

原文地址:http://blog.csdn.net/sunxueping/article/details/51170890

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!