list的sort是用什么方法如何实现的? 不是快速排序吧?

vzeder 2015-11-25 11:03:47
std::sort作用于random_accessible的容器,而list不是,所以list有自己的排序算法。
问题是: std::sort使用的快速排序,或者stable_sort使用的归并排序,在迭代的过程中都需要划定一个分治的范围,也就是要有一个划分的点,对可以随机访问的容器而言是很容易了。而对于list而言,要找到某个分治点,总是要前向或者后向遍历才能找到,对吧。
这样一来岂不是常规的qsort/heap_sort算法对于std::list都没有效果了? std::list::sort内部到底是怎么实现的呢,效率又如何?
谢谢。
...全文
273 3 打赏 收藏 转发到动态 举报
写回复
用AI写文章
3 条回复
切换为时间正序
请发表友善的回复…
发表回复
cut_cat 2015-11-26
  • 打赏
  • 举报
回复
vs2013的实现:
template<class _Pr2>
		void sort(_Pr2 _Pred)
		{	// order sequence, using _Pred
		if (2 <= this->_Mysize)
			{	// worth sorting, do it
			const size_t _MAXBINS = 25;
			_Myt _Templist(this->_Getal()), _Binlist[_MAXBINS + 1];
			size_t _Maxbin = 0;

			while (!empty())
				{	// sort another element, using bins
				_Templist._Splice_same(_Templist.begin(), *this, begin(),
					++begin(), 1);

				size_t _Bin;
				for (_Bin = 0; _Bin < _Maxbin && !_Binlist[_Bin].empty();
					++_Bin)
					{	// merge into ever larger bins
					_Binlist[_Bin].merge(_Templist, _Pred);
					_Binlist[_Bin].swap(_Templist);
					}

				if (_Bin == _MAXBINS)
					_Binlist[_Bin - 1].merge(_Templist, _Pred);
				else
					{	// spill to new bin, while they last
					_Binlist[_Bin].swap(_Templist);
					if (_Bin == _Maxbin)
						++_Maxbin;
					}
				}

			for (size_t _Bin = 1; _Bin < _Maxbin; ++_Bin)
				_Binlist[_Bin].merge(_Binlist[_Bin - 1],
					_Pred);	// merge up

			_Analysis_assume_(0 < _Maxbin);

			splice(begin(), _Binlist[_Maxbin - 1]);	// result in last bin
			}
		}
vs2015中的实现:
template<class _Pr2>
		iterator _Sort(iterator _First, iterator _Last, _Pr2 _Pred,
			size_type _Size)
		{	// order [_First, _Last), using _Pred, return new first
			// _Size must be distance from _First to _Last
		if (_Size < 2)
			return (_First);	// nothing to do

		iterator _Mid = _STD next(_First, _Size / 2);
		_First = _Sort(_First, _Mid, _Pred, _Size / 2);
		_Mid = _Sort(_Mid, _Last, _Pred, _Size - _Size / 2);
		iterator _Newfirst = _First;

		for (bool _Initial_loop = true; ; _Initial_loop = false)
			{	// [_First, _Mid) and [_Mid, _Last) are sorted and non-empty
			if (_DEBUG_LT_PRED(_Pred, *_Mid, *_First))
				{	// consume _Mid
				if (_Initial_loop)
					_Newfirst = _Mid;	// update return value
				splice(_First, *this, _Mid++);
				if (_Mid == _Last)
					return (_Newfirst);	// exhausted [_Mid, _Last); done
				}
			else
				{	// consume _First
				++_First;
				if (_First == _Mid)
					return (_Newfirst);	// exhausted [_First, _Mid); done
				}
			}
		}
另外有一篇博客给的不知是什么版本编译器的实现:http://blog.csdn.net/qq276592716/article/details/7932483 都是用的归并排序的思想,不过通过迭代器互换或者移动的方式替换了传值,算是变种吧,具体细节的差别是随着 编译器的发展改变的,似乎越越来越能看懂了。。效率方面就不清楚了
paschen 版主 2015-11-26
  • 打赏
  • 举报
回复
摘自:http://blog.csdn.net/lijun5635/article/details/23963707 stl中的list被实现为环状的双向链表,设置一个“哨”node作为end( )。list没有使用标准sort算法,而是实现自身的sort,本质上是mergesort(侯捷解释的是错的),但是采用了一个特殊的形式: 普通的mergesort直接将待排序的序列一分为二,然后各自递归调用mergesort,再使用Merge算法用O(n)的时间将已排完序的两个子序列归并,从而总时间效率为n*lg(n)。(mergesort是很好的排序算法,绝对时间很小,n*lg(n)之前的系数也很小,但是在内存中的排序算法中并不常见,我想可能主要还是因为耗空间太多,也是O(n)) list_sort所使用的mergesort形式上大不一样:将前两个元素归并,再将后两个元素归并,归并这两个小子序列成为4个元素的有序子序列;重复这一过程,得到8个元素的有序子序列,16个的,32个的。。。,直到全部处理完。主要调用了swap和merge函数,而这些又依赖于内部实现的transfer函数(其时间代价为O(1))。该mergesort算法时间代价亦为n*lg(n),计算起来比较复杂。list_sort中预留了64个temp_list,所以最多可以处理2^64-1个元素的序列,这应该足够了:) 为何list不使用普通的mergesort呢?这比较好理解,因为每次找到中间元素再一分为二的代价实在太大了,不适合list这种非RandomAccess的容器。 为何list不使用标准sort算法呢(标准sort要求RandomAccessIterator)?至少普通的quicksort我觉得应该是可以的,具体原因等查查标准算法实现再来说了。 下面把gcc4.02中list_sort的实现贴上: template<typename _Tp, typename _Alloc> void list<_Tp, _Alloc>:: sort() { // Do nothing if the list has length 0 or 1. if (this->_M_impl._M_node._M_next != &this->_M_impl._M_node && this->_M_impl._M_node._M_next->_M_next != &this->_M_impl._M_node) { list __carry; list __tmp[64]; list * __fill = &__tmp[0]; list * __counter; do { __carry.splice(__carry.begin(), *this, begin()); for(__counter = &__tmp[0]; __counter != __fill && !__counter->empty(); ++__counter) { __counter->merge(__carry); __carry.swap(*__counter); } __carry.swap(*__counter); if (__counter == __fill) ++__fill; } while ( !empty() ); for (__counter = &__tmp[1]; __counter != __fill; ++__counter) __counter->merge(*(__counter - 1)); swap( *(__fill - 1) ); } } 对它的复杂度分析只能简单说说了,实际工作在草稿纸上完成: 假设总元素个数N=2^n-1。 首先merge函数的复杂度为O(m),因此最后一步的for循环复杂度为 求和i:2~n{2^i-1}=O(N)的时间。 再看do_while循环,tmp[0]有1个元素,tmp[1]有2个元素,。。。,tmp[n-1]有2^(n-1)个元素,他们都是通过merge而后再swap(为常数时间)到最终层次的,因此总复杂度为:求和i:1~n{i*2^(i-1)}=O((n-1)*2^n)=O(N*lgN)。 因此总时间复杂度为N*lgN。
flying_music 2015-11-26
  • 打赏
  • 举报
回复
用的是改进版本的归并排序,可参看http://blog.csdn.net/csufuyi/article/details/6540315

64,649

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧