全排列 递归(非字典序) 深搜(字典序)

全排列问题初探,不含重复元素情况的讨论。

糊的题目:

【题目描述】

给定一个由不同的小写字母组成的字符串,输出这个字符串的所有全排列。

我们假设对于小写字母有‘a’ <‘b’ < ... <‘y’<‘z’,而且给定的字符串中的字母已经按照从小到大的顺序排列。

 

【输入】

只有一行,是一个由不同的小写字母组成的字符串,已知字符串的长度在1到6之间。

【输出】

输出这个字符串的所有排列方式,每行一个排列。要求字母序比较小的排列在前面。字母序如下定

义:

已知S=s1s2...sk,T=t1t2...tkS=s1s2...sk,T=t1t2...tk,则S<T等价于,存在p(1≤p≤k),使得s1=t1,s2=t2,...,sp−1=tp−1,sp<tps1=t1,s2=t2,...,sp−1=tp−1,sp<tp成立。

 

【输入样例】

abc

【输出样例】

 

abc
acb
bac
bca
cab
cba

看了帖子也看了书,最后把当时的代码都糊上来了,因为懒就直接ctrl+/把其他的写法个注释掉了。需要的话,可以全部copy下来斟酌一番,考虑区别与不同,会有不小收获。

按字典序的代码实现这里用的是深搜,每次调用后从0开始,先后搜索原序列,要么赋值要么记录下标,最后到临界条件一并输出。

而不能字典序的递归回溯写法,递归的函数f(n)的意义是——输出第n+1位即其后的所有全排列序。是通过for循环对原序列每个位子上的所有情况与后面原素交换位置来实现的,其中通过标记来记录是否选择过,且由于交换的规则是与该次调用中for循环里的a[i]交换,每次换位调用后需恢复,并进行下一次换位。

这个方法可以参考下面的博客。https://blog.csdn.net/summerxiachen/article/details/60579623 

需注意的是得思考为什么这样无法实现每次输出都按照字典序,由于交换代码的实现,拿三个以元素abc为原序列为例,当第一个位子的元素于三个位子元素交换后(也就是当前n == 0, 第二个for循环里的i等于2时)就直接往后调用f(0+1),而这一次的输出效果就是cba,而不是我们想要的先输出cab。其中本质的原因在于f(n)函数中每次n与跨k(k>=1)个元素交换位置时(循环到i == n+k时),直接就向后递归,然后第一次交换后直接就以一种情况先输出,再来回溯,继续判断符合条件输出。这样输出的顺序就会与字典需不相符。(换个角度其实可以看出其整体的交换输出方式,就像一个输出模板,内部数字小标是123 132 213 231 321 312依次将第一个位子的元素与2、3交换后,然后直接输出a[1]a[2]a[3] a[1]a[3]a[2] a[2]a[1]a[3] ...挺有意思。

表达能力有限,可能讲得不够清楚,(虽然我就是这么想的,) 看到了要是让你有疑惑,可以指出(当然更多的是仔细再想想

我尽力讨论清楚。

//#include<iostream>
//#include<cstring>
//#include<cstdio>
//using  namespace std;
//char ans[101], s[101];
//bool b[123];//由于b[a[i]],a[i]字母范围是97~122。 
//int len;
//
//void dfs(int  n) {
//	if(n == len) {
//		for(int i = 0; i < len; ++i)
//			cout << ans[i];
//		cout << endl;
//	}
//	else //考虑有else与没else的区别#1
//		for(int j = 0; j < len; j++){
//			if(!b[s[j]]){
//				b[s[j]] = 1;
//				ans[n] = s[j];
//				dfs(n+1);
//				b[s[j]] = 0;
//			}
//		}
//}
//int main() {
//	cin >> s;
//	len = strlen(s);
//	dfs(0);
//}

/*
	极简版
*/ 
//#include<iostream>
//#include<cstring>
//using namespace std;
//char s[100];
//int c[100], b[100];//记录排好序的字母的下标顺序 
//int len;
//
//int dfs(int n) {
//	if(n == len){
//		for(int i = 0; i < len; i++)
//			cout << s[c[i]];
//		cout << endl;
//	}
//	for(int i = 0; i < len; i++) {
//		if(!b[i]) {
//			b[i] = 1;
//			c[n] = i;
//			dfs(n+1);
//			b[i] = 0;
//		}
//	}
//}
//int main() {
//	cin >> s;
//	len = strlen(s);
//	dfs(0);
//}

#include<iostream>
#include<cstring>
using namespace std;
char s[100];
int c[100], b[100];//记录排好序的字母的下标顺序 
int len;

int dfs(int n) {
	for(int i = 0; i < len; i++) {
		if(!b[i]) {
			b[i] = 1;
			c[n] = i;
			dfs(n+1);
			b[i] = 0;
		}
	}
	
	if(n == len - 1){//if可以写到后面,但条件需变动,想想本质的区别#2 
		for(int i = 0; i < len; i++)
			cout << s[c[i]];
		cout << endl;
	}

}
int main() {
	cin >> s;
	len = strlen(s);
	dfs(0);
}

//#include<iostream>
//#include<cstring>
//using namespace std;
//char s[101];
//int c[101], b[101], len;
//int dfs(int n) {
//	for(int i = 0; i < len; ++i) {
//		if(b[i] == 0) {
//			b[i] = 1;
//			c[n] = i;
//			if(n == len - 1){
//				for(int i = 0; i < len; i++)
//					cout << s[c[i]];
//				cout << endl;
//			}
//			else
//				dfs(n+1);
//			b[i] = 0;
//		}
//	}
//}
//int main(){
//	cin >> s;
//	len = strlen(s);
//	dfs(0);
//}

 
//#include<iostream>
//#include<cstring>
//using namespace std;
//char a[100];
//int len;
不能字典序呈现 
//void swap(char *a, char *b) { 
//	char temp;
//	temp = *a;
//	*a = *b;
//	*b = temp;
//} 
//int f(int n){
//	if(n == len) {
//		for(int i = 0; i < len; ++i)
//			cout << a[i];
//		cout << endl;
//	}
//	for(int i = n; i < len ; i++) {
//		swap(a[i], a[n]);
//		f(n+1);
//		swap(a[i], a[n]);
//	}
//}
//
//int main() {
//	scanf("%s", a);
//	len = strlen(a);
//	f(0);
//}

//下面这时错误的代码,最开始,思路不清晰时写下的,回过头来看可以反映些问题
//#include<cstring>
//using namespace std;
//
//int a[5] = {0};
//int k = 0;
//
//void f(int n) { 
//	while(k < 5 && a[n] == 1) {
//		n = (n + 1) % 5;
//		k++;
//	}
//	if(k == 5) {
//		cout << endl;
//		k = 0;
//		memset(a, 0, sizeof(a));
//		return;
//	}
//	cout << n;
//	k++;
//	for(int i = n; i < 5; i++) {
//		a[n] = 1;
//		f(n+1);
//		a[n] = 0;
//	}
//
//}
//
//int main() {
//	f(0);
//}

 

思考:
#1:不管由还是没有if都会执行,但判断成立后,没else则会继续执行后面的语句,
虽然这里没影响但如果本意不希望再执行下面的代码则需用else. 
#2当当前参数n等于len-1时,也就意味这上面的for循环将填满最后一个位置的数,
for循环结束后就得输出结果(这里再次体现该函数的意义——在a[i]的第n位(0~len-1)上填满足条件的数. 

//小经验:

//1.当被变量弄混淆时,需弄清各变量在代码中的含义 ,各施其职,得分清后再考虑去,一语双关。
//2.递归实现时,的结果输出需注意,你确定是要在每次调用过程中同时来输出答案吗(还希望每次换行后,
//当前输出的前面自动cout出之前的结果。)?还是整理好思路方法,一并输出答案呢。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值