全排列与字典序排列

首先,全排列是一个比较简单的问题,但我却没有真正的去实现过全排列。

让我独自思考全排列的话,如将 “ abcd“ 进行全排列,这种简单的全排列也能将我难住,因为真的没有考虑过这种问题。思考了一会,我只能给出以下比较麻烦的算法:

//字符串全排列
void printRE(char* str,int index,char s[],int length){
	if(index == length)
		printf("%s \n",str);
	else{
		bool exsist = false;
		for(int i = 0 ; i < length;i++){
			exsist = false;
			for(int j= 0; j < index;j++){
				if(s[i] == str[j]){
					exsist = true;
					break;
				}
			}
			if(!exsist){
				str[index] = s[i];
				printRE(str,index+1,s,length);
			}
		}
	}
}
void PrintAll(char s[],int length){
	char* str = new char[length+1];
	str[length] = '\0';
	printRE(str,0,s,length);
	delete[] str;
}

这里使用一个字符串数组str来当作栈使用,实现了递归中的回溯。这里对字符是否使用过的判断就是遍历栈中元素,看之前部分的字符串中是否已用某些字符。

其实这些天多看了许多算法许多思路后,反而陷入了一种不知道解题方法就完全不去思考的状态,唉,脑子坏掉拉。

这里更简单的一种思路是,全排列即从第1个字母开始,与之后的字母交换,然后递归的输出全部:

void printALLA(char* str,char *begin){
	if('\0' == *begin)
		printf("%s \n",str);
	else{
		for(char* p =  begin;*p != '\0';p++){
			swap(*p,*begin);
			printALLA(str,begin+1);
			swap(*p,*begin);//返回原来的状态。
		}
	}

}

这是递归的方法。

非递归一般来说,字符串全排列可以使用字典序排列来实现。

这里字典序排列的算法很有意思,要记一下:

对于进行字典序排序的字符串 str ,从右向做寻找第一个小于右边元素的点 str[i]; 然后在i到字符串末尾寻找一个大于str[i]的最小字符 str[j], 交换str[i] 和 str[j]. 并将i之后的子串颠倒,得到一个新的字典序排序的结果。之后在这个结果的基础上去寻找下一个排序。

这个算法是很有意思的,这样的精致的思路是如何产生的,我无法知道,但是我了解到这样做的用意。首先,一开始字符串是已经排序好的字典序的开始状态,然后每一次循环的开始,都是将字符串分为两段,左边为 未进行字典排序的子段,而右边是已经进行字典排序的子段,而且这个子段之前的字典序已经被遍历并输出,这里寻找第一个小于右边的元素 str[i] ,而这个顺序之前的字典序已被输出的情况下,且右边字段必定是从大到小排序的,这样右边子段的字典序也被全部输出,这也说明对于str[i] 之前和包括自身已经全部遍历,这时要将str[i]换一个更大的元素来进行排列,而这个数就是右边字段中大于str[i]的最小元素str[j],交换str[i]
和str[j] ,,遍历以str[j] 开始的新的字典序,而这时候将右边字段倒转,因为之前是从大到小排序的,交换后就是从小到大排序,而这也就是str[j]为开始时,右边子段的第一个字典序。如此反复即可输出全部的字典序。

然后简单的写出字典序排列的代码:

void printLexOrder(char s[],int length){
	int charBarrel[128];
	memset(charBarrel,0,128*4);
	for(int i = 0 ; i < length;i++){
		charBarrel[ s[i] ] ++;
	}
	int i,j;
	i = 0;
	char* str = new char[length+1];
	str[length] = '\0';
	for(j = 0 ; j < 128;j++){
		while(charBarrel[j]  > 0){
			charBarrel[j]--;
			str[i++] = j;
		}
	}

	int  min;//最小的大于i的字符 的下标
	char* stack = new char[length];
	int stacktop = 0;//使用一个栈来实现字符串的颠倒
	printf("%s \n",str);
	while(i > -1){
		j = length-1;
		i = j - 1;
		while(str[i] >= str[j]){
			i--;
			j--;
			if(i<0)
				break;
		}
		if(i<0)break;//跳出两层循环
		j = i+1;
		min = j;
		while(j<length){
			if(str[j] > str[i] && str[j] <= str[min]){
				min = j;
			}
			j++;
		}
		j = min;
		stack[stacktop] = str[i];
		str[i] = str[j];
		str[j] = stack[stacktop];
		for(j = i+1;j<length;j++)
			stack[stacktop++] = str[j];
		while(stacktop > 0)
			str[++i] = stack[--stacktop];
		printf("%s \n",str);
	}
	delete[] stack, str;
}
时间: 2024-10-11 17:29:13

全排列与字典序排列的相关文章

hdu1027(n个数的按字典序排列的第m个序列)

题目信息:给出n,m,求n个数的按字典序排列的第m个序列 http://acm.hdu.edu.cn/showproblem.php?pid=1027 AC代码: /** *全排列的个数(次序) */ #include<iostream> #include<cstdio> #include<algorithm> int a[1001],x; using namespace std; void print(int n){ for(int i=1;i<n;i++){

全排列算法(字典序法、SJT Algorithm 、Heap&#39;s Algorithm)

一.字典序法 1) 从序列P的右端开始向左扫描,直至找到第一个比其右边数字小的数字,即. 2) 从右边找出所有比大的数中最小的数字,即. 3) 交换与. 4) 将右边的序列翻转,即可得到字典序的下一个排列. 5) 重复上面的步骤,直至得到字典序最大的排列,即左边数字比右边的大的降序排列. 二.SJT Algorithm 初始状态为. 1) 找到最大的可移动数m(当一个数指向一个比它小的数是,该数就是可移动数) 2) 交换m和m所指向的数 3) 改变所有比m大的数的方向 4) 重复上面的步骤,直至

51nod1364 最大字典序排列

不断的在cur的后面找最大的符合条件的数扔到cur的前面. 用线段树维护操作就可以了. #include<cstdio> #include<cstring> #include<cctype> #include<algorithm> using namespace std; #define rep(i,s,t) for(int i=s;i<=t;i++) #define dwn(i,s,t) for(int i=s;i>=t;i--) #defin

输出n的全排列的字典序编号为k的全排列

n个元素{1,2,•••,n}有n!个不同的排列.将这n!个排列按字典序排列.并编号为0,1,2.....,n!-1.每 个排列的编号为其字典序的值.例如.当n=3时,其字典排序为:123,132,213,232,312,321,这六个数的字典序值分别为 0,1,2,3,4,5,现给定任意n,输出字典序为k的排列(0<=k<=n!-1). 这题 也是来源于今年的美团面试题.. 一开始 我也无从下手.. 后来 看了http://www.cnblogs.com/submarine/archive/

51Node 1364--- 最大字典序排列(树状数组)

51Node  1364--- 最大字典序排列(树状数组) 1364 最大字典序排列 基准时间限制:1 秒 空间限制:131072 KB 分值: 80 难度:5级算法题  收藏  关注 给出一个1至N的排列,允许你做不超过K次操作,每次操作可以将相邻的两个数交换,问能够得到的字典序最大的排列是什么? 例如:N = 5, {1 2 3 4 5},k = 6,在6次交换后,能够得到的字典序最大的排列为{5 3 1 2 4}. Input 第1行:2个数N, K中间用空格分隔(1 <= N <= 1

51nod 1364 最大字典序排列(线段树)

1364 最大字典序排列基准时间限制:1 秒 空间限制:131072 KB 分值: 80 难度:5级算法题 给出一个1至N的排列,允许你做不超过K次操作,每次操作可以将相邻的两个数交换,问能够得到的字典序最大的排列是什么? 例如:N = 5, {1 2 3 4 5},k = 6,在6次交换后,能够得到的字典序最大的排列为{5 3 1 2 4}. Input 第1行:2个数N, K中间用空格分隔(1 <= N <= 100000, 0 <= K <= 10^9). 第2至N + 1行

十三:全排列-区间数排列

问题:全排列-区间数排列题目描述对n和m之间的数进行全排列(包括n和m,且:0<n<m<30),并且输出所有的排列结果输入两个整数,分别为n和m输出n和m之间所有数的全排列结果样例输入5 7样例输出5 6 75 7 66 5 76 7 57 5 6 7 6 5 1 #include<stdio.h> 2 #include<string.h> 3 int n,m; 4 void fun(int b[],int k){ 5 int i,j; 6 int t; 7 if

[Swift-2019力扣杯春季决赛]2. 按字典序排列最小的等效字符串

给出长度相同的两个字符串:A 和 B,其中 A[i] 和 B[i] 是一组等价字符.举个例子,如果 A = "abc" 且 B = "cde",那么就有 'a' == 'c', 'b' == 'd', 'c' == 'e'. 等价字符遵循任何等价关系的一般规则: 自反性:'a' == 'a' 对称性:'a' == 'b' 则必定有 'b' == 'a' 传递性:'a' == 'b' 且 'b' == 'c' 就表明 'a' == 'c' 例如,A 和 B 的等价信息

[51nod-1364]最大字典序排列

[51nod-1364]最大字典序排列 Online Judge:51nod-1364 Label:线段树,树状数组,二分 题目描述 题解: 根据题意很容易想到60%数据的\(O(N^2logN)\)暴力做法,即每次从大数往小数找,如果它能在m步内换到当前位置就把它换到前面去,然后再把选中的位置设为0,可以用树状数组在\(O(logN)\)完成. 0.O(N^2logN)暴力 cin>>n; for(int i=1;i<=n;i++){ int x; cin>>x,pos[x