c++ 排列和字典序全排列解析

    我们知道C++里自带的有全排列,对于在算法或者acm之类的竞赛可谓是非常的好用的一个函数。那么在学习全排列和组合算法之前我们先来看一下自带的函数如何使用。

首先,对于全排列c++给出了两个函数,next_permutation 和 prev_permutation 顾名思义,next_permutaion是指按字典序排列的下一个升序排列,而prev_permutation 则是指上一个。

std::next_permutation函数原型

template <class BidirectionalIterator>

bool next_permutation (BidirectionalIterator first, BidirectionalIterator last );

 

template <class BidirectionalIterator, class Compare>

bool next_permutation (BidirectionalIterator first,BidirectionalIterator last, Compare comp);

 

注:我们只需给出需要排序的数组或者向量的开始和结尾的地址便可得到下一个排列。其中的要注意的是该函数是有返回值的,当当前排列存在以字典序顺序更高的排列则返回true,如果不存在则返回false(表示此时已经是字典序)。

对数组

next_permutation (a,a+5);

对向量:

next_permutation (v.begin() , v.end());


非字典序排列

这种方法比较简单也比较好理解,但是缺点是输出的时候不是字典序,这在很多的情况下并不适用。

思想:

对于一个数组为{1,2,3},要想得到全排列那么必须满足的是每一个元素都要放在第一个位置,即{1,*,*} {2,*,*}{3,*,*},那么第一次递归我们就确定第一个位置的值。第二次可以的看成只有两个元素的数组,同样以第一次递归的方法我们就确定了第二个位置的值,以此类推。

实现方法:

首先,我们需要一个变量m来记录这是在计算第几位的值,那么每次从m遍历到的n即为全排列剩下的值。这个算法是采用深度优先的方法,即每次确定一个的值比如{1,2,3},第一次我们确定{1,*,*},第二次{1,2,*},第三次{1,2,3},第四次呢?

这里就要用到交换,即先将2和3 交换,使得3在第二个位置,当第三个位置确定后,我们再将2和3调换回来,这样就又恢复了原来的顺序了。其实在一开始,确定每一位时都进行了交换只不过自身与自身交换没有区别而已。

#include <iostream>
using namespace std;
void swap(int &a ,int &b)
{
	int temp=a;
	a=b;
	b=temp;
}
void permutation(int arr[],int m,const int n)
{	
	if (m==n)
	{
		//输出 
		for (int i =0;i<n;i++)cout<<arr[i]<<" ";
		cout<<endl;
	}
	 else
	 {
	 	for (int i =m;i<n;i++)
	 	{
	 		//先将当前位置元素分别与其他元素交换 
	 		swap(arr[i],arr[m]);
	 		//递归下一个位置 
	 		permutation(arr,m+1,n);
	 		//重复上面的交换,恢复原来的数组顺序 
	 		swap(arr[i],arr[m]);
	 	}
	 }
}
int main ()
{
	int  a[]={1,2,3};
	int b[3];
	bool flag[3];
	permutation (a,0,3);
	
	return 0;
} 

按升序输出的全排列

下面的升序排列其实是按照上面的算法改进来的。那么首先要知道为什么上面的算法不是升序的。

上面的输出后是

1 2 3

1 3 2

2 1 3

2 3 1

3 2 1

3 1 2

我们发现当以3为首元素的时候顺序就不是字典序了,按照步骤运行一下便会发现,由于1和3交换后数组的顺序便变为3 2 1,那么继续运行就会出现先3 2 1 再 3 1 2 的现象。因此问题就是,当我们交换元素后,除了前面的交换的元素,后面的应该保持有序才能输出的时候为字典序,即1和3交换后数组的顺序如果是3 1 2 则后面的输出为字典序的。当然我们想到交换后对其排序或者依次往后移之类的操作,虽然能解决问题但是要花费大量的时间。

因此,我们可以开多一个辅助数组和一个记录该元素是否已经在辅助数组里的标记数组。这样每次左往右遍历整个数组,当当前元素未标记(即不在辅助数组的时候)将其存入辅助数组里,并将标记置为真。当递归后再将该标记改为假。

 

#include <iostream>
using namespace std;

void swap(int &a ,int &b)
{
	int temp=a;
	a=b;
	b=temp;
}
void permutation(int arr[],int b[],bool f[],int m,const int n)
{
	if (m==n)
	{
		for (int i =0;i<n;i++)cout<<b[i]<<" ";
		cout<<endl;
	}
	 else
	 {
	 	for (int i =0;i<n;i++)
	 	{
	 		if (f[i])continue;
	 		f[i]=true;
	 		b[m]=arr[i];
	 		permutation(arr,b,f,m+1,n);
	 		f[i]=false;
	 	}
	 }
}
int main ()
{
	int  a[]={1,2,3};
	int b[3];
	bool flag[3];
	permutation (a,b,flag,0,3);	
	return 0;
} 

 

 


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值