打印全排列是个有点挑战的编程问题。STL提供了stl::next_permutation完美的攻克了这个问题。

可是,假设不看stl::next_permutation,尝试自己解决,怎么做?

非常自然地,使用递归的办法:

1. 单个元素的排列仅仅有1个。

2. 多个元素的排列能够转化为:

以每一个元素为排列的首个元素,加上其它元素的排列。

有了思路,就能够编码了。

第一个版本号:

void printAllPermutations(const std::string&
prefix, int set[], int n)
{
         using namespace std;
         char buffer[12];
         for (int i=0;
i<n; ++i)
        {
                string tmp_prefix(prefix);
                 int ei
= set[i];
                _itoa_s(ei, buffer, 12, 10);
                tmp_prefix += buffer;
                 if (n
== 1)
                {
                        cout << tmp_prefix.c_str() << endl;
                }
                 else
                {
                        tmp_prefix += "
" ;

                         //
shift set[0,i) to right by 1
                         for (int j=i-1;
j>=0; --j)
                        {
                                set[j+1] = set[j];
                        }

                        printAllPermutations(tmp_prefix, set+1, n-1);

                         //
shift set[0,i) to left by 1
                         for (int j=0;
j<i; ++j)
                        {
                                set[j] = set[j+1];
                        }
                        set[i] = ei;
                }
        }
}

測试:

int myints[] = {1,2,3,4};
printAllPermutations( "" ,
myints, 4);

通过。

这样的方法的缺点是产生了大量的string对象。

怎么避免呢?

第二个版本号:

void printAllPermutations2(int set[], int n, int from)
{
         using namespace std;
         for (int i=from;
i<n; ++i)
        {
                 int ei
= set[i];
                 if (from
== n-1)
                {
                         //
it is possible use callback here instead of printing a permutation
                         for (int j=0;
j<n; ++j)
                        {
                                cout << set[j] << '
' ;
                        }
                        cout << endl;
                }
                 else
                {
                         //
shift set[from,i) to right by 1
                         for (int j=i-1;
j>=from; --j)
                        {
                                set[j+1] = set[j];
                        }
                        set[from] = ei;

                        printAllPermutations2(set, n, from+1);

                         //
shift set[from,i) to left by 1
                         for (int j=from;
j<i; ++j)
                        {
                                set[j] = set[j+1];
                        }
                        set[i] = ei;
                }
        }
}

測试:

int myints[] = {1,2,3,4};
printAllPermutations2(myints, 4, 0);

通过。

第二个版本号相比第一个版本号的还有一个改进是能够非常easy地改变成回调函数的形式,扩展函数的用途。而不不过打印排列。

似乎非常不错了。

可是和stl::next_permutation相比,以上的方案就太逊了。

1. stl::next_permutation支持部分排列,而不必是全排列。你能够从不论什么一个排列開始,能够随时退出next_permutation循环。

2. stl::next_permutation支持多重集的排列。比如:

int myints[]
= {1,2,2,2};
do {
   std::cout << myints[0] << '
' << myints[1] << ' ' <<
myints[2] << ' ' << myints[3]
<< '\n';
} while (
std::next_permutation(myints,myints+4) );

输出:

1 2 2 2

2 1 2 2

2 2 1 2

2 2 2 1

没有反复的排列。

stl::next_permutation这么强大,非常值得看看它到底是怎么实现的。

// TEMPLATE FUNCTION next_permutation
template < class _BidIt> inline
         bool _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 );
                        }
                }
        }

template < class _BidIt> inline
         bool next_permutation(_BidIt
_First, _BidIt _Last)
        {        // permute
and test for pure ascending, using operator<
         return _Next_permutation(_CHECKED_BASE(_First),
_CHECKED_BASE(_Last));
        }

代码不长,但须要研究才干理解。

非常多算法都是这种。

这个算法能够概括为:

假设仅仅有零个或一个元素,返回false,表示回到全排列的起点。

否则。从右边開始。找到第一个不是递减的元素,即E(i) < E(i+1),从E(i+1)一直到E(n)都是不增的。

        假设找到。从右边開始。找到大于E(i)的那个元素E(x)【一定会找到】,交换E(i)和E(x),然后把E[i+1, n]范围内的元素反转。

返回true。

       假设找不到,把整个范围内的元素反转,返回false,表示回到全排列的起点。

为什么这个算法可行呢?看以下1 2 3 4的全排列。

能够非常easy地看到,

假设把每一个排列看成一个数,那么下一个排列大于上一个排列。

由上可知,第一个排列是最小排列【不减排列】。最后一个排列是最大排列【不增排列】。

最小排列和最大排列是反序的关系。

算法的关键:从E(i+1)一直到E(n)都是不增的。

这个特性说明,这一范围的元素的排列是一个最大排列,下一个排列必然是找到这一范围内大于这一范围的前一元素的元素,交换这两个元素,交换后E[i+1, n]仍为不增排列【最大排列】。反转之后,变成最小排列。这样处理后得到的排列正好是E[0,n]的下一个排列。

1 2 3 4

1 2 4 3

1 3 2 4

1 2

1 4 2 3

1 4 3 2

2 1 3 4

2 1 4 3

2 3 1 4

2 3 4 1

2 4 1 3

2 4 3 1

3 1 2 4

3 1 4 2

3 2 1 4

3 2 4 1

3 4 1 2

3 4 2 1

4 1 2 3

4 1 3 2

4 2 1 3

4 2 3 1

4 3 1 2

4 3 2 1

打印全排列和stl::next_permutation的更多相关文章

  1. STL - next_permutation 全排列函数

    学习: http://blog.sina.com.cn/s/blog_9f7ea4390101101u.html http://blog.csdn.net/ac_gibson/article/deta ...

  2. STL next_permutation(a,a+n) 生成一个序列的全排列。满足可重集。

    /** 题目: 链接: 题意: 思路: */ #include <iostream> #include <cstdio> #include <vector> #in ...

  3. STL next_permutation 全排列

    调用方法: ]={,,,}; )){ ;i<;i++) printf("%d ",arr[i]); puts(""); } 测试效果: 注:可以看到1 2 ...

  4. STL next_permutation和prev_permutation函数

    利用next_permutation实现全排列升序输出,从尾到头找到第一个可以交换的位置, 直接求到第一个不按升序排列的序列. #include <iostream> #include & ...

  5. STL next_permutation 算法原理和自行实现

    目标 STL中的next_permutation 函数和 prev_permutation 两个函数提供了对于一个特定排列P,求出其后一个排列P+1和前一个排列P-1的功能. 这里我们以next_pe ...

  6. STL next_permutation 算法原理和实现

    转载自:https://www.cnblogs.com/luruiyuan/p/5914909.html 目标 STL中的next_permutation 函数和 prev_permutation 两 ...

  7. C++ 全排列函数 std::next_permutation与std::prev_permutation

    C++ STL中提供了std::next_permutation与std::prev_permutation可以获取数字或者是字符的全排列,其中std::next_permutation提供升序.st ...

  8. 全排列(STL)

    输入一个整数n,输出1~n的全排列(是不是很水) 在此记录stl做法 #include<bits/stdc++.h> using namespace std; ]; int main(){ ...

  9. C++中全排列算法函数next_permutation的使用方法

    首先,先看对next_permutation函数的解释: http://www.cplusplus.com/reference/algorithm/next_permutation/?kw=next_ ...

随机推荐

  1. emacs使用本地emacs server模式打开远程文件

    使用emacs的用户都知道,一般要打开远程机器上的文件要使用TrampMode模式,调用方式例如以下: C-x C-f /remotehost:filename RET (or /method:use ...

  2. UNIX环境高级编程(5):文件I/O(1)

    UNIX系统中的大多数文件I/O仅仅须要用到5个函数:open.read.write.lseek以及close.本章说明的函数常常称为"不带缓冲的I/0",术语不带缓冲指的是每一个 ...

  3. iis browse的时候,直接通过本地的局域网ip打开页面

    http://www.codepal.co.uk/show/make_IIS_work_with_local_IP_addresses_instead_of_localhost 只需要设置一下webs ...

  4. CXF WebService中传递复杂对象(List、Map、Array)

    转自:https://wenku.baidu.com/view/047ce58ed0d233d4b14e69eb.html 现在开始介绍传递复杂类型的对象.如JavaBean.Array.List.M ...

  5. ThinkPHP5+Redis单例型购物车

    <?php /** * Redis + 单例型购物车 * param $basket 存储商品信息 * param $ins 存储实例化对象 */ namespace lib; use redi ...

  6. Beta冲刺提交—星期五

    课程链接: https://edu.cnblogs.com/campus/xnsy/SoftwareEngineeringClass2 作业要求链接: https://edu.cnblogs.com/ ...

  7. python 3.x 学习笔记17(协程以及I/O模式)

    1.协程(微线程)协程是一种用户态的轻量级线程.协程拥有自己的寄存器上下文和栈.协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈.因此: 协程能保留上一 ...

  8. 子线程创建AlertDialog错误

    Can't create handler inside thread that has not called Looper.prepare()

  9. JEE Spring-boot 简单的ioc写法。

    什么是ioc,就是你可能会有一些生活必需品,这些东西你必须要用才能存活.但是你不是每天都回去买,去哪一家点去买.而这些用品会一直放在哪里,每一个商店就是一个容器,包裹着这些物品. 创建ioc项目,首先 ...

  10. 如何创建一个asp页面

    Active Server Pages(ASP)文件是以 .asp 为扩展名的文本文件,这个文本文件可以包括下列部分的任意组合: 文本 HTML 标记 ASP 脚本命令 创建 .asp 文件非常容易. ...