一、题目描述及输入输出要求
•问题描述:设R={r1, r2, ..., rn}是要进行排列的n个元素。其中元素r1、r2、...、rn可能相同。试设计一个算法,列出R的所有不同排列。
•算法设计:给定n及待排列的n个元素。计算出这n个元素的所有不同排列。
•数据输入:由文件input.txt提供输入数据。文件的第1行是元素个数n,1≤n≤500。接下来的1行是待排列的n个元素。
•结果输出:将计算出的n个元素的所有不同排列输出到文件output.txt。文件最后1行中的数是排列总数。
输入文件示例 输出文件示例
input.txt output.txt
4 aacc
aacc acac
acca
caac
caca
ccaa
6
二、题目分析及算法设计思路
题目中要求列出所有不同的排列,输入中存在相同元素,所以要考虑不能产生重复排列。输入实际上就是告诉程序参与排列的字母是什么和它的个数,所以可以考虑创建能盛放26个数字的数组zi,下标分别对应26个不同的英文字母,数字代表字母的个数。ways数组用来保存每个位置的字母,即用来保存排列方案。
设计思路:
考虑用深度优先搜索来写。考虑第一个位置填写什么字母,依次遍历26个英文字母,先监测当前这个字母的个数,若为0则遍历下一个字母,若不为0则把字母放入ways中,同时zi[i]--(因为已经该字母使用过一次了,不能再用了),调用dfs函数考虑第二个位置填写什么字母……直到第n个位置,把ways输出。当然还要回溯,把zi[i]恢复原来的值,即zi[i]++,
否则会影响下一个方案的填写。
三、代码和实验结果截图
#include<iostream>
#include<string>
#include<fstream>
#include<math.h>
using namespace std;
ifstream in("d://input.txt");
ofstream out("d://output.txt");
int zi[26];
char ways[500];
int c = 0;
void dfs(string str,int k,int n) {//k表示当前位置,n表示字母数量
if (k==n) {
for (int i = 0; i < n; i++) {
out << ways[i];
}
out << endl;
c ++;
return;
}
for (int i = 0; i < 26; i++) {//遍历26个英文字母,i代表字母的编号
if (zi[i] != 0) {
ways[k] = 'a'+i;
zi[i]--;
dfs(str, k + 1, n);
zi[i] ++;//回溯
}
}
}
int main() {
string str;
int n;
in >> n;
in >> str;
for (int i = 0; i < n; i++) {//记录str中每个字母的个数
zi[str[i] - 'a']++;
}
dfs(str, 0, n);
out << c << endl;
in.close();
out.close();
return 0;
}
① 测试一
input.txt:
output.txt:
② 测试二
input.txt
output.txt:
四、
- 总共有n个位置,c用来记录排列的个数,ways数组用来保存每个位置的字母,当元素总共有n个时输出,c累加1。考虑第一个位置填写什么字母,依次遍历26个英文字母,先监测当前这个字母的个数,若为0则遍历下一个字母,若不为0则把字母放入ways中,同时zi[i]--(因为已经该字母使用过一次了,不能再用了),调用dfs函数考虑第二个位置填写什么字母……直到第n个位置,把ways输出。当然还要回溯,把zi[i]恢复原来的值,即zi[i]++,否则会影响下一个方案的填写。
- 算法时间复杂度分析:
设输入元素的个数为n
初始化zi数组时间复杂度为o(n),
计算dfs函数最坏时间复杂度:T(n)=n!=o(n^n)+o(n),
综上整体最坏时间复杂度为:T(n)=o(n)+o(n)+o(n^n)=o(n^n).