C++ next_permutation 源码分析

来源:互联网 发布:软件测试压力测试 编辑:程序博客网 时间:2024/06/07 10:24
template<class _BidIt> inlinebool _Next_permutation(_BidIt _First, _BidIt _Last){ // permute and test for pure ascending, using operator<//-----------------------------------------------\_DEBUG_RANGE(_First, _Last);_BidIt _Next = _Last;if (_First == _Last || _First == --_Next)   return (false);//上面这一块是检查边界范围的//-----------------------------------------------/for (; ; )   { // find rightmost element smaller than successor   _BidIt _Next1 = _Next;   if (_DEBUG_LT(*--_Next, *_Next1))    { // swap with rightmost element that's smaller, flip suffix    _BidIt _Mid = _Last;    for (; !_DEBUG_LT(*_Next, *--_Mid); )     ;    std::iter_swap(_Next, _Mid);//本身带的注释已经说得很明白,从最右边开始比较两两相邻的元素,直至找到右边比左边大的一对,左边那个//就是将要被替换的,再从最右边开始找比这个元素大的第一个,交换他们两个    std::reverse(_Next1, _Last);//交换之后,翻转交换元素的后面的所有元素    return (true);    }   if (_Next == _First)    { // pure descending, flip all    std::reverse(_First, _Last);    return (false);    }   }}

关键是确定一个排列的下一个排列是什么,我看着明白却说不明白,于是转贴一段,以下来自 http://www.cppblog.com/yindf/archive/2010/02/24/108312.html

abcd next_permutation -> abdc

那么,为什么abcd的下一个是abdc而不是acbd呢?

说简单一点,用 1,2,3,4 代替 a,b,c,d,可以得到:

原排列                  中间转换               值
1,2,3,4         3,2,1             ((3 * (3) + 2) * (2) + 1) * (1) = 23
1,2,4,3         3,2,0             ((3 * (3) + 2) * (2) + 0) * (1) = 22
1,3,2,4         3,1,1             ((3 * (3) + 1) * (2) + 1) * (1) = 21
1,3,4,2         3,1,0             ((3 * (3) + 1) * (2) + 0) * (1) = 20
1,4,3,2         3,0,1             ((3 * (3) + 0) * (2) + 1) * (1) = 19
.                   .                      .
.                   .                      .
.                   .                      .
4,3,2,1         0,0,0             ((0 * (3) + 0) * (2) + 0) * (1) = 0
                               |       |      |                       |                    |                   |
                               |      |                              |                    |
                               |                                     |


上面的中间转换指的是:每一个数字后面比当前位数字大的数字的个数。比如:

1,3,4,2 中,1 后面有(3, 4, 2) 他们都大于1,所以第一位是 3
                               3 后面有(4, 2), 但只有4大于3,所以第二位是 1
                               4 后面有(2), 没有比4 大的,所以第三位是 0
                               最后一位后面肯定没有更大的,所以省略了一个0。

经过这种转换以后,就得到了一种表示方式(中间转换),这种表达方式和原排列一一对应,可以相互转化。

仔细观察这种中间表达方式,发现它的第一位只能是(0,1,2,3),第二位只能是(0,1,2),第三位只能是(0,1)。通常,数字是用十进制表示的,计算机中用二进制,但是现在,我用一种特殊的进制来表示数:

第一位用1进制,第二位用2进制。。。

于是就得到了这种中间表示方式的十进制值。如:

                                                              阶                  
                                            |                |                  |
1,1,0    ---->   ((1 * (3) + 1) * (2) + 0) * (1) = 8

3,1,0     ---->   ((3 * (3) + 1) * (2) + 0) * (1) = 20

这样,就可以得到一个十进制数和一个排列之间的一一对应的关系。
现在排列数和有序的十进制数有了一一对应的关系(通过改变对应关系,可以使十进制数升序)。
原创粉丝点击