Permutation

 //FILE:   permutation.cpp
//AUTHOR: shenan
//DATE:   2008.12.24

#include <iostream>
#include <string>
#include <functional>
#include <algorithm>
using namespace std;

/*
假设数列 d1,d2,d3,d4……

范围由[first,last)标记,调用next_permutation使数列逐次增大,这个递增过程按照字典序。
例如,在字母表中,abcd的下一单词排列为abdc,但是,有一关键点,
如何确定这个下一排列为字典序中的next,而不是next->next->next……

若当前调用排列到达最大字典序,比如dcba,就返回false,同时重新设置该排列为最小字典序。

返回为true表示生成下一排列成功。下面着重分析此过程:

根据标记从后往前比较相邻两数据,若前者小于(默认为小于)后者,标志前者为X1(位置PX)表示将被替换,
再次重后往前搜索第一个不小于X1的数据,标记为X2。交换X1,X2,然后把[PX+1,last)标记范围置逆。完成。

要点:为什么这样就可以保证得到的为最小递增。

从位置first开始原数列与新数列不同的数据位置是PX,并且新数据为X2。[PX+1,last)总是递减的,
[first,PX)没有改变,因为X2>X1,所以不管X2后面怎样排列都比原数列大,反转[PX+1,last)使此子数列(递增)为最小。
从而保证的新数列为原数列的字典序排列next。
*/
bool nextPermutaion(string::iterator First, string::iterator Last)
{// permute and test for pure ascending, using operator<
 string::iterator Next = Last;
 if(First == Last || First == Next)
  return false;

 for (;;)
 {// find rightmost element smaller than successor
  string::iterator Next1 = Next;
  if (*--Next < *Next1)
  {// swap with rightmost element that's smaller, flip suffix
   string::iterator Mid = Last;
   for (; !(*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;
  }
 }
}

int main()
{

 string str = "ABC";
 cout<<"Permutaion of "<<str<<":/r/n";

 //sort(str.begin(), str.end(), greater<char>());
 //cout<<str<<endl;
 //while(prev_permutation(str.begin(), str.end())) 

 //sort(str.begin(), str.end(), less<char>());
 //cout<<str<<endl;
 //while(next_permutation(str.begin(), str.end()))

 sort(str.begin(), str.end(), less<char>());
 cout<<str<<endl;
 while(nextPermutaion(str.begin(), str.end()))
  cout<<str<<endl;

 return 0;
}
/*
如何实现输出任意字符串所有可能的排列
上周见到了一道题,实现可输入任意字符串,可给出其所有可能排列组合的情况。
想了半天,用自己所了解的知识都是处理不了(当然长久不用,很生疏了,再加之水平本就不高),
在网上搜搜,得出了结果,贴出解决方法来,不太跟得上时代发展的同志们可以借鉴一下。
 
其实也并没有多难,现在C++语言中提供了现成的算法来解决排列组合问题,
它们分别是next_permutation 和prev_permutation ,需要注意的是 "如果要走遍所有的排列,你必须先将元素排序"。
 
以下为转载:
 
<<C++标准程序库>>这本书,在看到"变序性算法"部分的时候,
发现两个函数next_permutation, prev_permutation对于我们平时处理排列组合的问题很有帮助,
根据书上的介绍写了两个个测试函数:
void func1()
{
    vector<int> v;
    INSERT_ELEMENTS(v, 1,3);

    PRINT_ELEMENTS( v, "myself: ");
 
    while( next_permutation( v.begin(), v.end() ) )
    {
 
        PRINT_ELEMENTS( v, "");
    }
}


void func2()
{
    vector<int> v;

    INSERT_ELEMENTS(v, 1,3);

    PRINT_ELEMENTS( v, "myself: ");

    sort(v.begin(), v.end(), greater<int>() ); //增加排序(降序)
 
    while( prev_permutation( v.begin(), v.end() ) )
    {
 
        PRINT_ELEMENTS( v, "");
    }
}


如果以后再遇到类似问题,我们就如此如此,不用再费脑筋,人家有现成的函数,直接拿来用就是了。

 

另外普及一下:

下面这段才是真真的算法,STL里面的源码
template inline
 bool next_permutation(_BidIt _First, _BidIt _Last)
 { // permute and test for pure ascending, using operator<
 _BidIt _Next = _Last;
 if (_First == _Last || _First == --_Next)
 return (false);

 for (; ; )
 { // find rightmost element smaller than successor
 _BidIt _Next1 = _Next;
 if (*--_Next < *_Next1)
 { // swap with rightmost element that's smaller, flip suffix
 _BidIt _Mid = _Last;
 for (; !(*_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);
 }
 }
 }


Ps: "STL":Standard Template Library,标准模板库(摘录bbs.csdn.net)
这是最早由Alexander Stepanov和Meng Lee(好像是华人的名字哦)完成,
于1994年提交给ANSI/ISO 标准C++委员会并通过而成为标准C++的一部分。望文生义即可知这是一个代码库标准,
不是语法标准。简单地说,STL是以C++中的模板语法为基础建立起来的一套包含基础数据结构和算法的代码库。
STL的特点是实现了“类型参数化”,即STL的代码中可处理任意自定义类型的对象,如果不使用模板技术的话,
这是一件相当困难的事。也因为这个原因,在最新的java及C#语法中均加入了对模板语法的支持,可见其重要性。
另外一个有关STL重要的话题是GP(Generic Programming),泛型。这是与面向对象相并列的另外的一个编程模型,
它以模板为基础,弱化了实体类型的差异,简化了编程时问题抽象的模型,提供了更好的封装性和弹性,
对于繁杂的面向对象编程毫无疑问是一种解脱,至少是精神上的。GP是最近几年软件架构的一个研究热点,
但国内真正的应用似乎并不多见。
*/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值