全排列解法总结(Java语言)

最近在准备蓝桥杯,所以今天想对全排列的一些解法进行总结,这样也方便于以后的复习。

样例问题
      这里我选择了一个较为容易理解的题目进行全排列的总结。题目是输入一个字符串,例如"abcd",通过全排列得出该字符串中字符的所有组合方式。

一、迭代法

      思路分析:这题使用的是迭代法,我的思路是首先创建一个old_list来存储上一次循环中保存的所有字符串。然后对old_list进行初始化,将字符串的0号位添加到old_list中,这其实也相当于完成了第一次循环,将第一次循环得到的所有字符串添加到old_list中。之后从1号位对字符串x进行遍历,取出i号位上的值赋值给临时变量temp并创建一个new_list来存放当次循环得到的字符串。这里必须要创建一个new_list来存放当次循环得到的字符串,不能直接存入到old_list,这样的话会造成重复。然后就是循环遍历old_list中的字符串,对于上一次循环得到的字符串,当次循环的字符总共有三种插入方式,分别是:1、在字符串的前面加上字符 2、在字符串的末尾加上字符 3、在字符串的中间加上字符。当遍历结束后,将new_list赋值给old_list,这样便于下一次循环。最后输出结果。
在这里插入图片描述

public static void main(String[] args) {
		String x = "abcd";
		全排列总结 a = new 全排列总结();
		ArrayList<String> sort1 = a.sort1(x);
		System.out.println(sort1.size());
		System.out.println(sort1);
	}
	//迭代法
	public static ArrayList<String> sort1(String x) {
		ArrayList<String> old_list = new ArrayList<>();					//创建一个List,用来存放上一次循环完成后的字符串
		
		old_list.add(x.charAt(0) + "");									//初始化,将0号位的字符存入list
		
		for (int i = 1; i < x.length(); i++) {							//从一号位开始遍历字符串
			String temp = x.charAt(i) + "";
			ArrayList<String> new_list = new ArrayList<>();				//创建新的list来存放新一轮的字符串
			
			for (String old : old_list) {								//循环遍历old_list,取出所有字符串
				new_list.add(temp + old);								//第一种情况,在字符串的前面加上字符
				new_list.add(old + temp);								//第二种情况,在字符串的末尾加上字符
				for (int j = 1; j < old.length(); j++) {
					new_list.add(old.substring(0, j) + temp + old.charAt(j));	//第三种情况,在字符串的中间加上字符
				}
			}
			
			old_list = new_list;										//将new_list中的字符串存给old_list,开始下一次循环
		}
		return old_list;												//返回结果

	}


二、递归法

      思路分析:这题使用的是递归法,是迭代法的改编版。因为是递归法,所以肯定要将一部分的事情交给现在做,另一部分交给递归去完成。所以我们可以先将字符串x的0~n-1号位上的字符排列出所有情况,然后再将n号位上的字符插入到所有情况中,这样就完成了 全排列。如下面代码所示。先创建一个old_list来存放第n个字符插入后的所有情况。然后调用递归,获取0~n-1位上字符串的所有排列情况。然后将第n号位上的字符插入进去即可。插入方法有三种情况,同迭代法是一样的。最后返回结果

public static void main(String[] args) {
		String x = "abcd";
		全排列总结 a = new 全排列总结();
		ArrayList<String> arr = new ArrayList<>();
		ArrayList<String> sort2 = a.sort2( x, x.length()-1, arr);
		System.out.println(sort2.size());
		System.out.println(sort2);
	}
	// 递归法
	public ArrayList<String> sort2(String x, int n, ArrayList<String> arr) {
		if (n == 0) {										//为递归设置出口
			arr.add(x.charAt(0) + "");						//初始化list
			return arr;
		}
		ArrayList<String> old_list = new ArrayList<>();			//为n号位字符创建一个list
		ArrayList<String> new_list = sort2(x, n - 1, arr);		//调用递归,得到1~n-1号位已经排列好全部情况的list

		for (String old : new_list) { 							// 循环遍历new_list,将字符串n号位的字符插入到全部情况中
			old_list.add(x.charAt(n) + old); 					// 第一种情况,在字符串的前面加上字符
			old_list.add(old + x.charAt(n)); 					// 第二种情况,在字符串的末尾加上字符
			for (int j = 1; j < old.length(); j++) {
				old_list.add(old.substring(0, j) + x.charAt(n) + old.charAt(j)); // 第三种情况,在字符串的中间加上字符
			}
		}

		return old_list;										//返回结果
	}


三、交换法

      思路分析:这题使用的是交换法,运用了回溯的思想。首先是将字符串转换为字符数组,并排列。然后调用sort3方法,每次都循环遍历从k号位到字符串末尾的每一个字符。swap(arr,k,i)意味将第i号位上的的值,交换到第k号位。然后递归调用,直到当k==arr.length时就将排列好的arr添加到全局变量res中,完成一次排列。这里调用完之后必须swap(arr,i,k)将数组复原,然后进行下一次循环。最后得到结果
在这里插入图片描述

	public static void main(String[] args) {
		String x = "abcd";
		全排列总结 a = new 全排列总结();
		char[] array = a.toArray(x);
		ArrayList<String> sort3 = a.sort3(array, 0);
		System.out.println(sort3.size());
		System.out.println(sort3);
	}
	// 交换法
	public char[] toArray(String x) {							//将字符串转换为字符数组
		char[] arr = x.toCharArray();
		Arrays.sort(arr);
		return arr;
	}
	
	ArrayList<String> res = new ArrayList<>();					//定义一个全局变量来存放所有排列情况

	public ArrayList<String> sort3(char[] arr, int k) {
		if (k == arr.length) {
			res.add(new String(arr));
		}
		for (int i = k; i < arr.length; i++) {					//每次都循环遍历每一个未被遍历过的数
			swap(arr, k, i);									//将第i号位上的值和k号位上的值交换
			
			sort3(arr, k + 1);									//从k+1号位开始调用递归,知道遍历完所有数
			
			swap(arr, i, k);									//回溯,将字符串复原
		}
		return res;

	}

	public void swap(char[] arr, int k, int i) {				//交换位置
		char temp = arr[k];
		arr[k] = arr[i];
		arr[i] = temp;
	}


四、前缀法

      思路分析:这题使用的是前缀法,我为本题加了一个条件(字符串必须有序),在全排列中,按照顺序排列,并且输出第num个排列好的数。Main方法调用sort4(),进入for循环,每次都遍历数组全部,然后遇到if语句,判断数组中某字符的出现次数如果大于在字符串中出现的次数,那说明需要把字符添加到prefix中。最后当字符串prefix的长度等于数组的长度时,即一次排列完成,总数加一。当cnt==num时则输出结果。

	public static void main(String[] args) {
		String x = "abcd";
		全排列总结 a = new 全排列总结();
		a.sort4("",x.toCharArray());
	}
	int cnt=0;										//总数
	final int num=3;								//需要获得第几个的值
	//前缀法
	public void sort4(String prefix,char[] arr) {
		if(prefix.length()==arr.length) {						//递归出口
			cnt++;
			if(cnt==num) {										//当字符串prefix的长度等于数组的长度时
				System.out.println(prefix);						//输出结果
				System.exit(0);;
			}
		}
		for(int i=0;i<arr.length;i++) {								//每次都遍历数组全部元素
			char ch=arr[i];
			if(count(prefix.toCharArray(),ch)<count(arr,ch)) {		//判断数组中某字符的出现次数如果大于在字符串中出现的次数
				sort4(prefix+ch,arr);
			}
		}
	}
	
	public int count(char[] arr,char ch) {					//判断字符在数组中出现的次数
		int count=0;
		for(int i=0;i<arr.length;i++) {
			if(arr[i]==ch) {
				count++;
			}
		}
		return count;
	}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
硬币问题通常是指给定一定面值的硬币和一个目标金额,从这些硬币中选择最少的硬币数量来凑出目标金额。以下是一种Java解法。 我们可以使用动态规划的思想来解决这个问题。假设给定硬币面值数组coins和目标金额amount,我们定义一个动态规划数组dp,其中dp[i]表示凑出金额i所需要的最少硬币数量。 首先,我们初始化dp数组为无穷大,并将dp[0]设为0,表示凑出金额0所需的硬币数量为0。 然后,我们遍历金额从1到目标金额amount的每个值。对于每个金额i,我们遍历硬币面值数组coins,如果硬币面值小于等于i,并且使用当前硬币面值凑出金额amount-i的数量dp[amount-i]加上当前硬币的数量1比dp[i]小的话,我们更新dp[i]为dp[amount-i]+1。 最终,我们返回dp[amount]即可得到凑出目标金额所需要的最少硬币数量。 下面是Java实现代码示例: ```java public class CoinChange { public int coinChange(int[] coins, int amount) { int[] dp = new int[amount + 1]; Arrays.fill(dp, Integer.MAX_VALUE); dp[0] = 0; for (int i = 1; i <= amount; i++) { for (int coin : coins) { if (coin <= i && dp[i - coin] != Integer.MAX_VALUE) { dp[i] = Math.min(dp[i], dp[i - coin] + 1); } } } return dp[amount] == Integer.MAX_VALUE ? -1 : dp[amount]; } } ``` 以上就是使用Java解决硬币问题的一个简单实现。这段代码的时间复杂度为O(amount * n),其中n是硬币面值的数量。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值