返回顶部

【STL基础】序列式容器之list

参照自文档http://www.cplusplus.com/reference/array/,教程http://c.biancheng.net/view/6688.html,和书籍《STL源码剖析》(侯捷)

定义:

template < class T, class Alloc = allocator<T> > class list;

相较于vector的连续线性空间,list显得复杂许多,它的好处是每次插入或删除一个元素,就配置或释放一个空间。对于任何位置的插入或删除元素,list永远是常数时间。

list本身是一个双向链表结构。使用 list 容器的缺点是,它不能像 array 和 vector 那样,通过位置直接访问元素。

使用需要引入头文件和使用std命名空间。

(一)初始化

方式一:创建了一个空的 list容器,并未分配空间。

std::list<double> values;

方式二:创建并指定元素个数,此时这二十个元素都被初始化为0.0。

std::list<double> values(20);

方式三:创建并指定元素个数和初始化值,此时这二十个元素都被初始化为1.0。

std::list<double> values(20, 1.0);

方式四:直接拷贝其他的list。

std::list<char> value1(5, 'c');
std::list<char> value2(value1);

也可以选择性复制一部分元素(利用迭代器函数)。

std::array<int, 5> value1 {1, 2, 3, 4, 5};
std::list<int> value2(std::begin(value1), std::begin(value1 + 3);//将保存1,2,3

(二)迭代器函数

  • begin():返回指向容器中第一个元素的双向迭代器。
  • end():返回指向容器最后一个元素后一个位置的双向迭代器。
  • rbegin():返回指向最后一个元素的双向迭代器
  • rend():返回指向第一个元素前一个位置的双向迭代器
  • cbegin():和 begin() 功能相同,只不过在其基础上增加了 const 属性,不能用于修改元素。
  • cend()、crbegin()、crend():依此类推

    前缀带c的返回的迭代器是const类型,因此只能通过它读取而不能修改元素。

与array、vector、deque 容器的迭代器相比,list 容器迭代器最大的不同在于,其配备的迭代器类型为双向迭代器,而不再是随机访问迭代器。这意味着,假设 p1 和 p2 都是双向迭代器,则它们支持使用 ++p1、 p1++、 p1--、 p1++、 *p1、 p1==p2 以及 p1!=p2 运算符,但不支持以下操作(其中 i 为整数):

(1)p1[i]:不能通过下标访问 list 容器中指定位置处的元素。

(2)p1-=i、 p1+=i、 p1+i 、p1-i:双向迭代器 p1 不支持使用 -=、+=、+、- 运算符。

(3)p1<p2、 p1>p2、 p1<=p2、 p1>=p2:双向迭代器 p1、p2 不支持使用 <、 >、 <=、 >= 比较运算符。

值得一提的是,list 容器在进行插入(insert())、接合(splice())等操作时,都不会造成原有的 list 迭代器失效,甚至进行删除操作,而只有指向被删除元素的迭代器失效,其他迭代器不受任何影响。

(三) 容量函数

  • size():返回容器中当前元素的数量,其值始终等于初始化 array 类的第二个模板参数 N。
  • max_size():返回元素个数的最大值。这通常是一个很大的值,一般是 2^32-1,所以我们很少会用到这个函数。
  • empty():判断容器是否为空。

(四)元素访问函数

  • front():返回容器中第一个元素的直接引用,可以用于修改元素,但是也只能修改第一个元素。
  • back():返回容器中最后一个元素的直接引用,可以用于修改元素。

(五)修改器函数

  • assign():用新元素替换原有内容。
  • push_front():向 list 容器首个元素前添加新元素。
  • push_back():向 list 容器最后一个元素后添加新元素。
  • emplace_front():在容器首个元素前直接生成新的元素。
  • emplace_back():在容器最后一个元素后直接生成新的元素
  • pop_front():删除位于 list 容器头部的一个元素。
  • pop_back():删除位于 list 容器尾部的一个元素。
  • emplace():在容器的指定位置直接生成新的元素。
  • insert():在指定的位置插入一个或多个元素,具体用法如下:
    • iterator insert(pos,elem):在迭代器 pos 指定的位置之前插入一个新元素 elem,并返回表示新插入元素位置的迭代器。
    • iterator insert(pos,n,elem):在迭代器 pos 指定的位置之前插入 n 个元素 elem,并返回表示第一个新插入元素位置的迭代器。
    • iterator insert(pos,first,last):在迭代器 pos 指定的位置之前,插入其他容器(例如 array、vector、deque 等)中位于 [first,last) 区域的所有元素,并返回表示第一个新插入元素位置的迭代器。
    • iterator insert(pos,initlist):在迭代器 pos 指定的位置之前,插入初始化列表(用大括号 { } 括起来的多个元素,中间有逗号隔开)中所有的元素,并返回表示第一个新插入元素位置的迭代器。
//使用示例
std::list<int> values{ 1,2 };
//第一种格式用法
values.insert(values.begin() , 3);//{3,1,2}
//第二种格式用法
values.insert(values.end(), 2, 5);//{3,1,2,5,5}
//第三种格式用法
std::array<int, 3> test{ 7,8,9 };
values.insert(values.end(), test.begin(), test.end());//{3,1,2,5,5,7,8,9}
//第四种格式用法
values.insert(values.end(), { 10,11 });//{3,1,2,5,5,7,8,9,10,11}
for (auto p = values.begin(); p != values.end(); ++p)
{
    cout << *p << " ";
}
  • splice():将其它 list 容器中的元素添加到当前 list 容器中指定位置处。
    • void splice (iterator position, list& x):将 x 容器中存储的所有元素全部移动当前 list 容器中 position处。
    • void splice (iterator position, list& x, iterator i):将 x 容器中 i 指向的元素移动到当前容器中 position 处。
    • void splice (iterator position, list& x, iterator first, iterator last):将 x 容器 [first, last) 范围内所有的元素移动到当前容器 position 处。
//创建并初始化 2 个 list 容器
list<int> mylist1{ 1,2,3,4 }, mylist2{10,20,30};
list<int>::iterator it = ++mylist1.begin(); //指向 mylist1 容器中的元素 2
//调用第一种语法格式
mylist1.splice(it, mylist2); // mylist1: 1 10 20 30 2 3 4
                             // mylist2:
                             // it 迭代器仍然指向元素 2,只不过容器变为了 mylist1
//调用第二种语法格式,将 it 指向的元素 2 移动到 mylist2.begin() 位置处
mylist2.splice(mylist2.begin(), mylist1, it);   // mylist1: 1 10 20 30 3 4
                                                // mylist2: 2
                                                // it 仍然指向元素 2

//调用第三种语法格式,将 [mylist1.begin(),mylist1.end())范围内的元素移动到 mylist.begin() 位置处                  
mylist2.splice(mylist2.begin(), mylist1, mylist1.begin(), mylist1.end());//mylist1:
                                                                         //mylist2:1 10 20 30 3 4 2

  • erase():删除 vector 容器中 pos 迭代器指定位置处的元素,并返回指向被删除元素下一个位置元素的迭代器。该容器的大小(size)会减 1,但容量(capacity)不会发生改变。
//erase()源码
//清除position位置的元素
iterator erase(iterator position){
    link_type next_node = link_type(position.node->next);
    link_type prev_node = link_type(position.node->prev);
    prev_node->next = next_node;
    next_node->prev = prev_node;
    destroy_node(position.node);
    return iterator(next_node);
}
  • swap():交换两个容器的所有元素。
  • remove(val):删除容器中所有等于val的元素
//remove()的源码
template <class T, class Alloc>
void list<T, Alloc>::remove(const T$ value){
    iterator first = begin();
    iterator last = end;
    while(first != last){
        iterator next = first;
        ++next;
        if(*first == value) erase(first);
        first = next;
    }
}
  • remove_if():删除容器中满足条件的元素。
//使用实例
std::list<int> mylist{ 15, 36, 7, 17, 20, 39, 4, 1 };
//删除 mylist 容器中能够使 lamba 表达式成立的所有元素。
mylist.remove_if([](int value) {return (value < 10); }); //{15 36 17 20 39}
  • clear():移除所有的元素,容器大小变为 0。
//clear()源码
void clear(){
		erase(begin(), end());
}
  • unique():删除容器中相邻的重复元素,只保留一份。
//unique()源码
template <class T, class Alloc>
void list<T, Alloc>::unique(){
    iterator first = begin();
    iterator last = end;
    if(first == last) return;
    iterator next = first;
    while(++next != last){
        if(*first == *next) 
            erase(next);
        else
            first = next;
        next = first;
    }
}
  • merge():合并两个事先已排好序的 list 容器,并且合并之后的 list 容器依然是有序的。
//merge()源码
template <class T, class Alloc>
void list<T, Alloc>::merge(list<T, Alloc>& x){
    iterator first1 = begin();
    iterator last1 = end;
    iterator first2 = x.begin();
    iterator last2 = x.end;
    //前提是两个list均是有序
    while(first1 != last1 && first2 != last2){
        if(*first2 < *first1){
            iterator next = first2;
            transfer(first1, first2, ++next);
            first2 = next;
        }else{
            ++first1;
        }
        if(first2 != last2) transfer(last1, first2, last2);
    }
}
  • sort():通过更改容器中元素的位置,将它们进行排序。list不能使用STL算法的sort,必须使用自带的sort()函数,本函数采用快排的思路。
//sort()源码
template <class T, class Alloc>
void list<T, Alloc>::sort(){
    if(node->next == node || link_type(node->next)->next ==node)
        return;
    list<T, Alloc> carry;
    list<T, Alloc> counter[64];
    int fill = 0;
    while(!empty()){
        carry.splice(carry.begin(), *this, begin());
        int i = 0;
        while(i < fill && !counter[i].empty()){
            counter[i].merge(carry);
            carry.swap(counter[i++]);
        }
        carry.swap(counter[i]);
        if(i == fill) ++fill;
    }
    for(int i = 1; i < fill; ++i){
        counter[i].merge(counter[i-1]);
    }
    swap(counter[fill-1]);
}
  • reverse():反转容器中元素的顺序。
//reverse()源码
template <class T, class Alloc>
void list<T, Alloc>::reverse(){
    if(node->next == node || link_type(node->next)->next ==node)
        return;
    iterator first = begin();
    ++first;
    while(first != end()){
        iterator old = first;
        ++first;
        tranfer(begin(), old, first);
    }
}
posted @ 2020-11-13 23:43  藤原豆腐店の張さん  阅读(117)  评论(0编辑  收藏  举报