【转】std::list中size()方法的时间复杂度

  标准STL容器List(Linux GNU,sgi的实现),其size()函数的要遍历所有list中的元素来获得链表长度,来看看它的实现:

1 size_type size() const {
2     size_type __result = 0;
3     distance(begin(), end(), __result);
4     return __result;
5   }

  其通过调用distance()函数来计算元素个数,而distance方法的实现如下:

1 template <class _InputIterator, class _Distance>
2 inline void distance(_InputIterator __first, 
3                      _InputIterator __last, _Distance& __n)
4 {
5   __STL_REQUIRES(_InputIterator, _InputIterator);
6   __distance(__first, __last, __n, iterator_category(__first));
7 }

  又封了一层_distance(),看看它做了什么:

 1 template <class _InputIterator>
 2 inline typename iterator_traits<_InputIterator>::difference_type
 3 __distance(_InputIterator __first, _InputIterator __last, input_iterator_tag)
 4 {
 5   typename iterator_traits<_InputIterator>::difference_type __n = 0;
 6   while (__first != __last) {
 7     ++__first; ++__n;
 8   }
 9   return __n;
10 }

  原来是遍历!为什么获得链表长度必须要遍历全部的链表元素才能获得,而不是用一个变量来表示呢?sgi设计list的思路何以如此与众不同呢

  原来作者是为了

splice(iterator position, list& x, iterator first, iterator last);

  方法所取的折衷。list 是链表结构,它的优势就在于可以 O(1) 的时间复杂度任意插入甚至拼接 list 片段。list::splice() 是一个很强大的功能,它可在任意位置拼接两个 list。如果我们在类内部以一个变量储存 list 的长度,那么splice()之后新list的长度就需要遍历待移动元素的first和last间的长度(然后把链表A保存的链表长度减去first和last之间的长度)。于是作者考虑将size()方法设计为O(N),就无需在splice()方法执行时做遍历了。

1 void splice(iterator __position, list&, iterator __first, iterator __last) {
2     if (__first != __last) 
3       this->transfer(__position, __first, __last);
4   }

  再看看transfer干了些什么:

 1 void transfer(iterator __position, iterator __first, iterator __last) {
 2     if (__position != __last) {
 3       // Remove [first, last) from its old position.
 4       __last._M_node->_M_prev->_M_next     = __position._M_node;
 5       __first._M_node->_M_prev->_M_next    = __last._M_node;
 6       __position._M_node->_M_prev->_M_next = __first._M_node; 
 7  
 8       // Splice [first, last) into its new position.
 9       _List_node_base* __tmp      = __position._M_node->_M_prev;
10       __position._M_node->_M_prev = __last._M_node->_M_prev;
11       __last._M_node->_M_prev     = __first._M_node->_M_prev; 
12       __first._M_node->_M_prev    = __tmp;
13     }
14   }

  作者确实是考虑splice执行时,不用遍历,而是仅仅移动几个指针就可以了,因此牺牲了size的效率!

 

  转自:https://blog.csdn.net/russell_tao/article/details/8572000

posted @ 2020-09-02 21:42  阿玛尼迪迪  阅读(1661)  评论(0编辑  收藏  举报