Java实现排列、组合

排列

根据给定字符串,打印其所有排列串。

输入: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);
			}
		}
	}

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值