算法笔记 //05_有重复元素的排列问题(针对字母排序)

★问题描述:

设 R = { r1, r2, ……, rn } 是要进行排列的 n 个元素。其中元素 r1 ,r2 ,……,rn 可能相同。试设计一个算法,列出 R 的所有不同排列。
给定 n 以及待排列的 n 个元素。计算出这 n 个元素的所有不同排列。

★举例

简单举例:aabb
则排列为:aabb ,abab, abba, baab, baba, bbaa(共6种)

**

★算法思想

① 当读取完用户输入的所有字母,考虑用将字母转换成数值的方法间接来进行排列;
② 将这些字母存储进一个数组 read[],将每个字母 -96 转换成数字,这时候考虑到用户可能会输入重复字母(某个或某些),因此考虑用一个数组 f[] 来作为计数器,记录所有字母的重复次数;
③ 记录完所有字母重复的次数之后,来进行排序,这里排序的思想是:对于重复的字母(即 f[]>0 时),不是考虑一次将其加入排序,而是一个一个地取,先取出第一个,f[] - 1,然后进入下一层遍历排序,继续取其他的字母,若其他字母也有重复(也是 f[] > 0),继续进入下一层遍历排序(重复此步骤),直到这一层遍历结束,返回上一层遍历排序,f[] + 1;
④遍历排序完一层,就输出此次排序的结果,情况总数记录数组 quantity[] +1,而且输出结果是一个一个字母输出,并非一次输出所有字母,因此也是循环输出,输入几个字母就循环几次。

**

★C++ 代码如下(Visual Studio 2017 调试运行,这里只写了字母排序):

// 输入字符的时候,个数要输入正确再按回车,字符之间不要有空格或回车
#include "stdafx.h"

#include 
#include 
#include 

using namespace std;

int f[100], a[1000], n;	//数组 a[1000] 为记录排列组合情况数组,f[100]作为一个计数器,整数 n 是来询问一共要输入几个字母 
int quantity = 0;    //定义一个变量 quantity 来记录方案总数
char read[100]; 	//定义字符串数组 read[] 记录用户输入字符

void dfs(int depth)	//定义函数 dfs(深度优先算法)(输出排列组合情况函数),定义深度depth
{

	if (depth == n + 1 && n != 0) //当层次深度达到了最底层时,比如说用户输入了 4 个字母,程序运行完第 5 层时,说明此次遍历排序完成,输出排序结果,然后情况数 + 1
	{
		quantity ++;      //将情况数 + 1 
		for (int r = 1;r <= n;r ++)   //不断循环遍历,一个一个字母地输出,不加空格,所以结果四个字母紧挨着,但是并非一次输出四个字母的
			printf("%c", a[r] + 96);	//将数字转换为字符并输出所有排列组合情况(标准 ascII 码,a 对应 97,A 对应 65)
		cout << endl;
		return;
	}
	for (int r = 1;r <= 26;r ++)	//依次读取 f[] 每个位置的数值(即每个字母重复的次数,见主函数中 f[read[i]-96] 注释);26:26个字母组多也就是 26 个位置
		if (f[r] > 0)     //如果这个位置数值大于零   在这里是第二个位置
		{
			a[depth] = r;     //a[]开始记录字母,先用数字来记录  
			f[r] --;     //相应的 f[] 数组 第二个位置的数值大小 -1
			dfs(depth + 1);   //递归使用
			f[r] ++;     //上面 dfs 结束此层的遍历之后,f[] + 1,回到上一层,取出下一个位置的字母
		}
}

int main()
{
	cout << "Please input the number of letters you're gonna range:" << endl;
	cin >> n;
	cout << "\nPlease input the letters(no space and no entering until the letters' quantity is enough):" << endl;
	cin >> read;
	for (int i = 0;i < n;i ++) //输入几个字母就循环几遍,每个字母都要被遍历到
		f[read[i] - 96] ++;    //记录每一个字母在字符串中出现了几次  ,read[i]为读取每个位置上的字母,然后将字母 -96 转化为数字,这个数字最大是26(因为一共26个字母),这样的话有重复的字母出现后,f[]数组相对应位置的数值大小就会重复加 1,可以记录到所有字母重复的次数
	cout << "\nResults:" << endl;		//输出排列组合情况
	dfs(1);		//在这里预定义深度为 1,上一条语句用 f[] 记录了各个字母重复的次数
	cout << "\nTotal number:" << endl;    //输出总情况数
	cout << quantity << endl;
	cout << "__________________________________________________________" << endl;
	system("pause");
	return 0;
}


算法笔记 //05_有重复元素的排列问题(针对字母排序)_第1张图片

你可能感兴趣的:(算法&数据结构の笔记)