【说人话的算法小课堂】按字典序升序枚举n个元素的所有全排列(算法步骤 + 正确性证明)

在这里插入图片描述

全排列

定义

从n个元素中取出全部的n个进行排列,就得到全排列。

算法:按字典序升序枚举n个元素的所有全排列

设集合{a_1,a_2,…,a_n}的一个全排列为P=p_1,p_2,…,p_(j-1),p_j,p_(j+1),…,p_n。
【1】从排列的右端开始,找出第一个比右边数字小的数字的序号j。即:令
i=n-1,n-2,…
当第一次满足p_i<p_(i+1)时,停止,并记j=i。
如果没有找到符合条件的i,意味着整个排列的字典序已经最大,算法结束。
【2】在p_j右边的数字中,找出所有比p_j大的数字中最小的数字p_k,即
p_k=min⁡〖{p_i |p_i>p_j, i=j+1,…,n}〗
k=i
如果存在多个符合条件的p_i,则记k为这些数中下标最大的那个数的下标。
【3】交换p_j,p_k。
【4】将排列最右端的递减区间倒转。

证明

下面证明:当原排列的字典序未达到最大时,按照上述算法构造出来的新排列(即下一个排列)一定比原排列的字典序更大,且字典序的增量最小。
把原排列划分成若干个子排列,每一段子排列要么递增,要么递减,要么既不递增也不递减。对于最右侧的一段(易知:该段的长度至少为2):
(1)若该段递增,则易证:执行一次此算法后,最右侧的两个数会交换位置,于是最右边的两个数构成了一个递减的子数列。转入情况(2)。
(2)若该段递减,则这段递减的子排列的字典序已经最大,也就是说不能在不改变这段子排列左侧的情况下,再把这段子排列的字典序变得更大。因此,若要构造下一个排列,必须要动用其它的元素。
设字典序已经最大的最右侧的那一段子排列的左侧有d个元素。在“需要增大前d个元素构成的排列的字典序”的条件下,为使整个排列的字典序的增量尽可能小,易得:前(d-1)个元素的顺序应当保持不变。
为了使新排列的字典序增大,又只能改变第d个到第n个元素的顺序,而第(d+1)到第n个元素构成的子排列的字典序已经最大,所以第d个位置的元素必须替换为比该位置原有的元素p_d更大的元素。
为了使字典序增量尽量小,与d互换位置的元素要比d大得尽可能小。所以算法的第【3】步是正确的。
并且,还要令第(d+1)到第n个元素构成的子排列的字典序要最小。
易证:由【1】找到的右端的数一定是降序。经过【3】后,右端这些数仍然是降序。
所以,将第(d+1)到第n个元素直接反转,就能使它们构成的排列的字典序最小。所以算法的第【4】步是正确的。
当第【2】步找到多个p_i时,易证:只有将第【1】步找到的p_j与下标最大的p_i对调,才能使新排列的字典序最小。综合上述证明过程,得到算法的第【1】【2】步也是正确的。
(3)若该段既不递增也不递减,易证:只有将第【1】步找到的p_j与最右侧的元素对调,才能使新排列的字典序最小。QED.

特殊数据的处理

经检验,当参与枚举的元素只有1个或2个时,算法能够给出正确结果并正常结束。

代码

template<class _Ty> bool NextPermutation(_Ty* const _First, _Ty* const _Last) {
    
    
	static_assert(is_arithmetic<_Ty>::value, "Only arithmetic type (int, double ...) are allowed.");
	_Ty* i = nullptr, * j = nullptr, * k = nullptr;
	for (i = _Last - 2; i >= _First; --i) {
    
    
		if (*i < *(i + 1)) {
    
     j = i; break; }
	}
	if (j == nullptr) {
    
     reverse(_First, _Last); return false; }
	_Ty m = numeric_limits<_Ty>::max();
	for (i = j + 1; i < _Last; ++i) {
    
    
		if (*i > * j && *i <= m) {
    
     m = *i; k = i; }
	}
	swap(*j, *k);
	reverse(j + 1, _Last);
	return true;
}

说明:若对已经达到最大字典序的全排列继续枚举下一个排列,则反转排列为字典序最小的排列,并返回false。当前排列的字典序未达到最大时,返回true。算法执行的过程中,参与排列的区间[_First,_Last)的存储内容会改变。

猜你喜欢

转载自blog.csdn.net/COFACTOR/article/details/109234849