快速排序和sort——不稳定排序的举例

1,快速排序

一般的快速排序都是不稳定排序,每个人写快速排序写的可能都不太一样,根据具体代码很容易找到是不稳定排序的证据。

如果快速排序改一改,在按照大小分左右两拨的时候,把相等的元素都取出来,并且按照顺序贴进去,应该能做到稳定排序,

不过这只能算快速排序的修改版,一般说的快速排序不包括这种写法。

2,sort

C++中的 sort函数、函数指针、仿函数 https://blog.csdn.net/nameofcsdn/article/details/104823002

sort是是快速排序、堆排序、插入排序三者的结合,其中快速排序、堆排序是不稳定排序,插入排序是稳定排序。

为了找到sort排序是不稳定排序的例子,我自己构造了一些用例,不过并没有成功

3,稳定排序在线评测

在和同事交流过程中,我得到了这个题目:

1451. 重新排列句子中的单词

题意是根据字符串的长度排序,而且要稳定排序。

https://blog.csdn.net/nameofcsdn/article/details/106196224

其中我的比较函数是

bool cmp(nodes x, nodes y)
{
    if(x.s.length()==y.s.length())return x.k<y.k;
    return x.s.length()<y.s.length();
}

只要把 if(x.s.length()==y.s.length())return x.k<y.k; 这句话去掉再重新提交就能知道sort是不是稳定排序了。

提交之后挂了,于是我得到了这个用例

"Jlhvvd wfwnphmxoa qcuucx qsvqskq cqwfypww dyphntfz hkbwx xmwohi qvzegb ubogo sbdfmnyeim tuqppyipb llwzeug hrsaebveez aszqnvruhr xqpqd ipwbapd mlghuuwvec xpefyglstj dkvhhgecd kry"

把它转化为长度数组:6 10 6 7 8 8 5 6 6 5 10 9 7 10 10 5 7 10 10 9 3

利用我封装的sort输出ID的程序;

//给vector拓展,加上id并排序
template<typename T>
bool cmp(T x,T y)
{
    return x<y;
}
template<typename T>
vector<pair<T,int>> sortWithId(vector<T>v)
{
    vector<pair<T,int>>ans;
    ans.resize(v.size());
    for(int i=0;i<v.size();i++)ans[i].first=v[i],ans[i].second=i;
    sort(ans.begin(),ans.end(),[](pair<T,int>p1,pair<T,int>p2){return cmp(p1.first,p2.first);});
    return ans;      
}

我输出了这个数组的排序ID:

看起来没毛病啊!

于是我把这个题目代码放本地运行:

结果是对的啊!

同一份代码,在OJ和本地运行结果不一样,只能是因为环境不一样,可能是编译器相关,也可能是库函数不一样。

4,STL版本

我想起来在我试图在网上搜索sort不稳定的用例的时候,我看到这样一篇博客:

https://blog.csdn.net/qq_40482358/article/details/80210819

文中提到STL是有不同版本的,而且他贴出来的代码和我在vs2010里面看到的确实不一样。

所以我就想到,应该是因为OJ的sort函数和我本地的sort函数不一样。

进一步,OJ的sort函数肯定是不稳定排序,但我本地的sort函数呢?至少目前还没有找到不稳定的用例。

5,sort重写

为了利用OJ验证我本地的sort函数是不是稳定排序,我需要用我本地的sort函数替代OJ里面的sort函数。

经过简单的处理,我得到了这样的代码:

#define _STD	::std::
const int _ISORT_MAX = 32;
  #define _DEBUG_LT_PRED(pred, x, y)	pred(x, y)

template<class _BidIt,
	class _Pr> inline
	void _Insertion_sort(_BidIt _First, _BidIt _Last, _Pr _Pred)
	{	// insertion sort [_First, _Last), using _Pred
	for(auto i=_First;i!=_Last;i++)for(auto j=i+1;j!=_Last;j++)
    	if (!_DEBUG_LT_PRED(_Pred, *i, *j))
		_STD iter_swap(i, j);
	}
template<class _RanIt,
	class _Pr> inline
	void _Med3(_RanIt _First, _RanIt _Mid, _RanIt _Last, _Pr _Pred)
	{	// sort median of three elements to middle
	if (_DEBUG_LT_PRED(_Pred, *_Mid, *_First))
		_STD iter_swap(_Mid, _First);
	if (_DEBUG_LT_PRED(_Pred, *_Last, *_Mid))
		_STD iter_swap(_Last, _Mid);
	if (_DEBUG_LT_PRED(_Pred, *_Mid, *_First))
		_STD iter_swap(_Mid, _First);
	}

template<class _RanIt,
	class _Pr> inline
	void _Median(_RanIt _First, _RanIt _Mid, _RanIt _Last, _Pr _Pred)
	{	// sort median element to middle
	if (40 < _Last - _First)
		{	// median of nine
		size_t _Step = (_Last - _First + 1) / 8;
		_Med3(_First, _First + _Step, _First + 2 * _Step, _Pred);
		_Med3(_Mid - _Step, _Mid, _Mid + _Step, _Pred);
		_Med3(_Last - 2 * _Step, _Last - _Step, _Last, _Pred);
		_Med3(_First + _Step, _Mid, _Last - _Step, _Pred);
		}
	else
		_Med3(_First, _Mid, _Last, _Pred);
	}

template<class _RanIt,
	class _Pr> inline
	_STD pair<_RanIt, _RanIt>
		_Unguarded_partition(_RanIt _First, _RanIt _Last, _Pr _Pred)
	{	// partition [_First, _Last), using _Pred
	_RanIt _Mid = _First + (_Last - _First) / 2;
	_Median(_First, _Mid, _Last - 1, _Pred);
	_RanIt _Pfirst = _Mid;
	_RanIt _Plast = _Pfirst + 1;

	while (_First < _Pfirst
		&& !_DEBUG_LT_PRED(_Pred, *(_Pfirst - 1), *_Pfirst)
		&& !_Pred(*_Pfirst, *(_Pfirst - 1)))
		--_Pfirst;
	while (_Plast < _Last
		&& !_DEBUG_LT_PRED(_Pred, *_Plast, *_Pfirst)
		&& !_Pred(*_Pfirst, *_Plast))
		++_Plast;

	_RanIt _Gfirst = _Plast;
	_RanIt _Glast = _Pfirst;

	for (; ; )
		{	// partition
		for (; _Gfirst < _Last; ++_Gfirst)
			if (_DEBUG_LT_PRED(_Pred, *_Pfirst, *_Gfirst))
				;
			else if (_Pred(*_Gfirst, *_Pfirst))
				break;
			else
				_STD iter_swap(_Plast++, _Gfirst);
		for (; _First < _Glast; --_Glast)
			if (_DEBUG_LT_PRED(_Pred, *(_Glast - 1), *_Pfirst))
				;
			else if (_Pred(*_Pfirst, *(_Glast - 1)))
				break;
			else
				_STD iter_swap(--_Pfirst, _Glast - 1);
		if (_Glast == _First && _Gfirst == _Last)
			return (_STD pair<_RanIt, _RanIt>(_Pfirst, _Plast));

		if (_Glast == _First)
			{	// no room at bottom, rotate pivot upward
			if (_Plast != _Gfirst)
				_STD iter_swap(_Pfirst, _Plast);
			++_Plast;
			_STD iter_swap(_Pfirst++, _Gfirst++);
			}
		else if (_Gfirst == _Last)
			{	// no room at top, rotate pivot downward
			if (--_Glast != --_Pfirst)
				_STD iter_swap(_Glast, _Pfirst);
			_STD iter_swap(_Pfirst, --_Plast);
			}
		else
			_STD iter_swap(_Gfirst++, --_Glast);
		}
	}

template<class _RanIt,
	class _Diff,
	class _Pr> inline
	void _Sort2(_RanIt _First, _RanIt _Last, _Diff _Ideal, _Pr _Pred)
	{	// order [_First, _Last), using _Pred
	_Diff _Count;
	for (; _ISORT_MAX < (_Count = _Last - _First) && 0 < _Ideal; )
		{	// divide and conquer by quicksort
		_STD pair<_RanIt, _RanIt> _Mid =
			_Unguarded_partition(_First, _Last, _Pred);
		_Ideal /= 2, _Ideal += _Ideal / 2;	// allow 1.5 log2(N) divisions

		if (_Mid.first - _First < _Last - _Mid.second)
			{	// loop on second half
			_Sort2(_First, _Mid.first, _Ideal, _Pred);
			_First = _Mid.second;
			}
		else
			{	// loop on first half
			_Sort2(_Mid.second, _Last, _Ideal, _Pred);
			_Last = _Mid.first;
			}
		}

	if (_ISORT_MAX < _Count)
		{	// heap sort if too many divisions
		_STD make_heap(_First, _Last, _Pred);
		_STD sort_heap(_First, _Last, _Pred);
		}
	else if (1 < _Count)
		_Insertion_sort(_First, _Last, _Pred);	// small
	}

template<class _RanIt,
	class _Pr> inline
	void sort2(_RanIt _First, _RanIt _Last, _Pr _Pred)
	{	// order [_First, _Last), using _Pred
	_Sort2((_First), (_Last), _Last - _First, _Pred);
	}

其中_Insertion_sort函数是我自己写的,其他是从我本地IDE移植过来的。

PS:原谅我写了一个选择排序

把题目里面的sort(v.begin(),v.end(),cmp);改成sort2(v.begin(),v.end(),cmp);提交,发现可以通过。

再把cmp比较函数里面的if(x.s.length()==y.s.length())return x.k<y.k;这句话注释掉,发现解答错误。

这是我们需要的sort不稳定的用例吗?显然不是!

6,稳定分支控制

sort是是快速排序、堆排序、插入排序三者的结合,其中快速排序、堆排序是不稳定排序,插入排序是稳定排序。

而被我改写之后,就变成的快速排序、堆排序、我的选择排序,选择排序是不稳定排序。

其中堆排序的分支在数据量很小时是调用不到的,所以我又把_Insertion_sort改写成了冒泡排序,这个是稳定排序:

template<class _BidIt,
	class _Pr> inline
	void _Insertion_sort(_BidIt _First, _BidIt _Last, _Pr _Pred)
	{	// insertion sort [_First, _Last), using _Pred
	for(auto i=_First;i!=_Last;i++)for(auto j=_First;j-_First<_Last-i-1;j++)
    	if (_DEBUG_LT_PRED(_Pred, *(j+1), *j))
		_STD iter_swap(j, (j+1));
	}

再次提交,我得到了这个用例:

Npwfcxhzs diftsrym vzkdo uxwlha eyocyxo vdsdiyryer xqqghawjp rnpowjs wmqerrs egrriqsivq rvtlymg paecqitg klukaw otyerkmmnk chyenhxez trpge iqfcxax qwwpz amcnilipmj xwespxz jqzqbs riytunnotn ktbbursmxl xyqhan jitujj etkxlxdmx csljl zbbyu vxyntzvpa qrtufudlap hwhvjcjv yqfzij jlhet vuwiyf wozdclg xqegx kwyvqfwve lkchg oxxzctzolz efwrp xflitpgxl gwpiomzlh mcirgs uwkcquzu lxqtbe emyjgor fszjyon xwnopy gzni

把它长度提取出来:

9 8 5 6 7 10 9 7 7 10 7 8 6 10 9 5 7 5 10 7 6 10 10 6 6 9 5 5 9 10 8 6 5 6 7 5 9 5 10 5 9 9 6 8 6 7 7 6 4

还是用我的程序,调用sort并输出ID:

经过打断点确认了没有调用到堆排序,插入排序也是稳定的,所以这就是sort函数不稳定的用例。

准确的说,是sort函数里面的快速排序不稳定的用例。

猜你喜欢

转载自blog.csdn.net/nameofcsdn/article/details/107216764