排列
根据给定字符串,打印其所有排列串。
输入:s = "abc"
打印:["abc","acb","bac","bca","cab","cba"]
public static void permutation(String s)
{
if (s == null || s.length() == 0) {
return;
}
dfs(0, s, new char[s.length()], new int[s.length()]);
}
/**
* 输出字符串的全排列
* @param layer 当前下标层数
* @param s 字符串
* @param tmpStr 缓存结果串的字符数组
* @param checked 标记数组,标记哪个字符被访问过的
*/
private static void dfs(int layer, String s, char [] tmpStr, int [] checked)
{
if (layer == s.length()) {
System.out.println(tmpStr);
}
for (int i = 0 ; i < s.length() ; i++)
{
if (checked[i] == 0)
{
tmpStr[layer] = s.charAt(i);
checked[i] = 1;
dfs(layer + 1, s, tmpStr, checked);
checked[i] = 0;
}
}
}
去重排列
上个排列方法中存在一个问题,就是当串中有重复字符时,会输出重复的排列串。
例如:s = "aab"
,输出结果为:
aab
aba
aab
aba
baa
baa
结果中有三对重复的排列。那么如何给排列串去重?
第一种方法,将结果串放到Set<String>
中,可以实现去重的操作。但这不是理想的解决方案,理想的解决方案是一旦发现重复,马上剪枝,不会等到最后再来判断是否重复。
所以可以使用一个Set<Character>
中存放当前层中使用过的字符,如果出现同样的字符,直接剪掉。
稍微改动下dfs方法即可:
private static void dfs(int layer, String s, char [] tmpStr, int [] checked)
{
if (layer == s.length()) {
System.out.println(tmpStr);
}
Set<Character> set = new HashSet<>();
for (int i = 0 ; i < s.length() ; i++)
{
// 如果当前字符出现过,直接剪枝
if (checked[i] == 0 && !set.contains(s.charAt(i)))
{
tmpStr[layer] = s.charAt(i);
set.add(s.charAt(i)); // 将当前字符添加到set中
checked[i] = 1;
dfs(layer + 1, s, tmpStr, checked);
checked[i] = 0;
}
}
}
这样的话,输出结果就是:
aab
aba
baa
组合
上面两种是排列操作,那么如果要求一个字符串的组合串呢?
输入:s = "abc"
打印:["a","b","c","ab","ac","bc", "abc"]
public static void combination(String s)
{
if (s == null || s.length() == 0) {
return;
}
for (int i = 1 ; i <= s.length() ; i++)
{
partDfs(0, i, s, new char[i], new int[s.length()]);
}
}
/**
* 打印目标串的排列串
* @param layer 下标层数
* @param maxLen 终止层数
* @param s 目标串
* @param tmpStr 临时存放结果
* @param checked 标记数组
*/
private static void partDfs(int layer, int maxLen, String s, char [] tmpStr, int [] checked)
{
if (layer == maxLen) {
System.out.println(tmpStr);
return;
}
for (int i = 0 ; i < s.length() ; i++)
{
if (checked[i] == 0)
{
tmpStr[layer] = s.charAt(i);
// 这里设置占有后,不需要还原
checked[i] = 1;
partDfs(layer + 1, maxLen, s, tmpStr, checked);
}
}
}
去重组合
上面一种常规组合方式也存在问题,例如传入的是s = "aaa"
,那么输出结果是["a", "a", "a", "aa", "aa", "aaa"]
,这样就有好多重复的,期望结果是["a", "aa", "aaa"]
。
处理方法和去重排列一样的,使用Set<Character>
去重。
修改后的partDfs
方法:
private static void partDfs(int layer, int maxLen, String s, char [] tmpStr, int [] checked)
{
if (layer == maxLen) {
System.out.println(tmpStr);
return;
}
Set<Character> set = new HashSet<>();
for (int i = 0 ; i < s.length() ; i++)
{
if (checked[i] == 0 && !set.contains(s.charAt(i)))
{
tmpStr[layer] = s.charAt(i);
set.add(s.charAt(i));
// 这里设置占有后,不需要还原
checked[i] = 1;
partDfs(layer + 1, maxLen, s, tmpStr, checked);
}
}
}
这样,输出结果就是去重的。
完整代码
代码为去重版本。
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
/**
* @Auther: Erekilu
* @Date: 2020-07-17
*/
public class Permutation
{
public static void main(String[] args)
{
permutation("aab");
combination("aaa");
}
// 排列
public static void permutation(String s)
{
if (s == null || s.length() == 0) {
return;
}
dfs(0, s, new char[s.length()], new int[s.length()]);
}
/**
* 输出字符串的全排列
* @param layer 当前下标层数
* @param s 字符串
* @param tmpStr 缓存结果串的字符数组
* @param checked 标记数组,标记哪个字符被访问过的
*/
private static void dfs(int layer, String s, char [] tmpStr, int [] checked)
{
if (layer == s.length()) {
System.out.println(tmpStr);
}
Set<Character> set = new HashSet<>();
for (int i = 0 ; i < s.length() ; i++)
{
if (checked[i] == 0 && !set.contains(s.charAt(i)))
{
tmpStr[layer] = s.charAt(i);
set.add(s.charAt(i));
checked[i] = 1;
dfs(layer + 1, s, tmpStr, checked);
checked[i] = 0;
}
}
}
// 组合
public static void combination(String s)
{
if (s == null || s.length() == 0) {
return;
}
for (int i = 1 ; i <= s.length() ; i++)
{
partDfs(0, i, s, new char[i], new int[s.length()]);
}
}
/**
* 打印目标串的排列串
* @param layer 下标层数
* @param maxLen 终止层数
* @param s 目标串
* @param tmpStr 临时存放结果
* @param checked 标记数组
*/
private static void partDfs(int layer, int maxLen, String s, char [] tmpStr, int [] checked)
{
if (layer == maxLen) {
System.out.println(tmpStr);
return;
}
Set<Character> set = new HashSet<>();
for (int i = 0 ; i < s.length() ; i++)
{
if (checked[i] == 0 && !set.contains(s.charAt(i)))
{
tmpStr[layer] = s.charAt(i);
set.add(s.charAt(i));
// 这里设置占有后,不需要还原
checked[i] = 1;
partDfs(layer + 1, maxLen, s, tmpStr, checked);
}
}
}
}