关于全排列算法

首先介绍:邻位互换算法

原理:首先会先给数据排序(从小到大),然后给这些数据赋上移动数据,比如最开始都给他们赋值为-1表示能向左移,其实在这里只用一个新数组来存储移位数据就可以了,比如我们现在3是可以左移的,3的位置是2,然后通过去找移位数组里面的值,发现对于位置2的值为-1,只用拿2-1就可以得到,3移位后的位置了。

 

 

其次如果3移动到最左边就不能再移位了,这个时候要去找剩余元素里面最大的元素,通过移动它一次,然后再改变3的移动方向,继续移位。(注意移动方向的元素,必须要小于移动元素)最后会发现都不能再移动了。

上代码:

# include <cstdio>
# include <algorithm>
# include <cstring>
# include <cmath>

using namespace std;

const int N_MAX = 10;

int n;
int a[N_MAX + 10];
int face[N_MAX + 10];

bool canMove(int x)
{
	int y = x + face[x];
	return y >= 1 && y <= n && a[x] > a[y];//判断移位是否已经到达边界,并且移动的下一个元素要小于此时移动的元素
}

bool permutation()
{
	int a_max = 0, pos = -1;
	for (int i = 1; i <= n; i++) {
		if (!canMove(i) || a_max > a[i]) continue;
		a_max = a[i], pos = i;
	}
	if (pos == -1) return false;
	int pos_to = pos + face[pos];
	swap(a[pos], a[pos_to]);
	swap(face[pos], face[pos_to]);//!!!很重要,元素头上那个箭头要跟着元素移动哦
	for (int i = 1; i <= n; i++)
		if (a[i] > a_max) face[i] = -face[i];
	return true;
}

int main()
{
	scanf("%d", &n);
	for (int i = 1; i <= n; i++)
		a[i] = i;
	memset(face, -1, sizeof(face));
	do {
		for (int i = 1; i <= n; i++)
			printf("%5d", a[i]);
		printf("\n");
	} while (permutation());
	return 0;
}

 递归实现算法:

原理:比如现在我们有一个数组{1,2,3,4}要求它的全排列,其实可以想象为先求首元素是1的2,3,4的全排列,然后首元素为2的1,3,4的全排列。。。。。。

2,3,4的全排列又可以想象成去求首元素是2的3,4的排列,依次减治下去!!!

上代码:

#include <stdio.h> 

int n = 0;                            //n用来计全排列的总数
int main()                         //主函数
 {   int list[] = {3,5,8,2};    
     perm(list, 0, 3);
     printf("total:%d\n", n);     //输出n的个数
    
 } 


void perm(int list[], int begin, int end)      //产生list[begin:end]的所有排列
 {    
     int i;    
     if  (begin == end)        //begin等于end,即list中只有一个元素时
     {         
         for(i = 0; i <= begin; i++)            
             printf("%d ", list[i]);         //打印出这个排列
         printf("\n");        
         n++;                 //n的个数加一,表示又有一种情况产生了
     }    
     else    
     {        
         for(i = begin; i <= end; i++)   
         {            
             swap(&list[begin], &list[i]);             
             perm(list, begin + 1, end);            
             swap(&list[begin], &list[i]);        //必须要在递归下去的时候将已经移位的数据进行还原,比如1,2,3,4你进行到了2,3交换的时候,然后进入递归此时数组为1,3,2,4就只用再交换2,4就行了,然后退回到递归之前,这个时候你要还原数组为1,2,3,4这时2,就会和4交换。因此所有情况都会遍历
         }    
     }
 }

void swap(int *a, int *b)           //用来交换两个变量值的函数
 {    
     int m;    
     m = *a;    
     *a = *b;    
     *b = m;
 } 

  

posted @ 2020-03-31 11:11  Swithun  阅读(256)  评论(0)    收藏  举报