欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

[STL] list的使用

程序员文章站 2022-05-10 11:30:06
1. list功能 list是双向循环链表,每一个元素都知道前面一个元素和后面一个元素.list对象自身提供了两个pointer用来指向第一个和最后一个元素.每个元素都有poin...

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的构造函数和析构函数

listc;             // 构造函数,产生一个空list,没有任何元素
listc(c2);         // copy 构造函数,建立c2的同类型list,并成为c2的一份拷贝
listc = c2;        // copy 构造函数,建立一个新的list作为c2的拷贝
listc(rv);         // move 构造函数,建立一个新的list,取rvalue rv的内容(始自C++11)
listc = c2;        // move 构造函数,建立一个新的list,取rvalue rv的内容(始自C++11)
listc(initlist);   // 建立一个list,以初值列initlist的元素为初值(始自C++11)
listc = initlist;  // 建立一个list,以初值列initlist的元素为初值(始自C++11)    
listc(n);          // 利用元素的default构造函数生成一个大小为n的list
listc(n,elem);     // 建一个含n个元素的list,值都是elem
listc(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::reverse_iterator riter=c1.rbegin(); // *riter=30

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

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

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

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

crend() 返回一个const反向迭代器, 指向反向迭代器最后一个元素的下一个位置 (始自C++11)
list::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()); //同上,但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()); //同上,但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的基本用法,实际使用中还需要注意一下几点,后续遇到再加补充.

注意事项: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进行操作是非法的,程序会出错。