学计算机这么久了还不会这个,有点说不过去。今天看了看。算法还是比较容易理解的。
比如,可以使用递归将问题切割为较小的单元进行排列组合,例如1 2 3 4的排列可以分为
1 [2 3 4] 、2 [1 3 4]、3 [1 2 4]、4 [1 2 3]进行排列,
这边利用旋转法,先将旋转间隔设为0,将最右边的数字旋转至最左边,并逐步增加旋转的间隔,例如:
1 2 3 4 -> 旋转1 -> 继续将右边2 3 4进行递归处理
2 1 3 4 -> 旋转1 2 变为 2 1-> 继续将右边1 3 4进行递归处理
3 1 2 4 -> 旋转1 2 3变为 3 1 2 -> 继续将右边1 2 4进行递归处理
4 1 2 3 -> 旋转1 2 3 4变为4 1 2 3 -> 继续将右边1 2 3进行递归处理
代码如下:
public class test { public static void main(String args[]) { int[] cs = new int[5]; for (int j = 0; j < cs.length; j++) { cs[j] = j + 1; } getPerm(cs, 0); } public static void getPerm(int[] cs, int i) { if (i < cs.length - 1) { for (int j = i; j < cs.length; j++) { int temp = cs[j]; // 右移数据 for (int k = j; k > i; k--) cs[k] = cs[k - 1]; cs[i] = temp; getPerm(cs, i + 1); //每次都要把数据恢复之后才可以进行下一次排列 for (int k = i; k < j; k++) cs[k] = cs[k + 1]; cs[j] = temp; } } else {// 如果i是cs.length()-1的话,那么说明已经排列到最后一个元素,可以不用继续排列了,输出该数组既可以 for (int m = 0; m < cs.length; m++) System.out.print(cs[m]); System.out.println(); } } }
如果要求按照大小输出的话,只需要在排序之前,对需要排序的数字进行排序。
--------------------------------------------------------魂歌----------------------------------------------------------
正式开始找工作了。不论找实习还是工作,都要复习一下算法。这次自己写了一下这个算法,还算写的不错吧,思路比较清晰。看来算法就是越用越熟练了。
package zoer; import java.util.ArrayList; import java.util.List; public class CopyOfT { static int count = 0; public static void main(String args[]) { get("1234", new ArrayList<String>()); System.out.println("全排列个数为:"+count); } public static void get(String s, List<String> list) { if (s.length() == 1) { count++; for (int i = 0; i < list.size(); i++) { System.out.print(list.get(i)); } System.out.println(s); } else { for (int i = 0; i < s.length(); i++) { list.add("" + s.charAt(0)); String sub = s.substring(1, s.length()); get(sub, list); s = sub + s.charAt(0); list.remove(list.size() - 1); } } } }
每次把下一个字符提到字符串的最前端,对剩余的字串做相同的操作。直到剩余的子字符串的长度为1的时候,输出得到的全排列结果。
上面的代码中,s = sub + s.charAt(0);的意思是说,第一个字符处理完毕之后,把它加到字符串的末尾,然后处理第二个字符。依次类推直到所有的字符都处理完毕【这里说的处理,具体就是指把一个字符摘出来,对其他剩余的字符串做全排列】。
------------------------------------------------------------------------------
上面的算法是对一个非重复字符串的全排列,但是没有按照原来的字符中字符顺序来输出,主要是因为在移动字符的时候,直接s = sub + s.charAt(0);这样的结果是第一个字符直接跑到最后去了。而不是跟相应的字符进行交换,那么按照下面的代码,即可实现按照”字符原来“的顺序进行输出。
package zoer; import java.util.ArrayList; import java.util.List; public class CopyOfT { static int count = 0; public static void main(String args[]) { get("1234", new ArrayList<String>()); System.out.println("全排列个数为:" + count); } public static void get(String s, List<String> list) { if (s.length() == 1) { count++; for (int i = 0; i < list.size(); i++) { System.out.print(list.get(i)); } System.out.println(s); } else { for (int i = 0; i < s.length(); i++) { list.add("" + s.charAt(0)); String sub = s.substring(1, s.length()); get(sub, list); if (i != s.length() - 1) { s = s.charAt(i + 1) + s.substring(0, i + 1) + s.substring(i + 2, s.length()); } list.remove(list.size() - 1); } } } }
不过上面的这个算法是求不重复字串的全排列。重复的还要再考虑下,,,
---------------------------------------------有重复的情况--------------------------------------------
这里考虑了有重复的情况。如果重复的字符在字符串中不是第一次出现,那么就不用递归这个字符了。用一个数组来记录每一个字符在字符串中第一次出现的位置。
实现代码如下:
package zoer; import java.util.ArrayList; import java.util.List; public class CopyOfT { static int count = 0; public static void main(String args[]) { String s = "1223"; get(s, new ArrayList<String>()); System.out.println("全排列个数为:" + count); } public static void get(String s, List<String> list) { if (s.length() == 1) { count++; for (int i = 0; i < list.size(); i++) { System.out.print(list.get(i)); } System.out.println(s); } else { int index[] = new int[s.length()]; for (int i = 0; i < index.length; i++) { index[i] = s.indexOf(s.charAt(i));// 用index数组记录字符串中相应位置的字符在字符串中第一次出现的位置 } for (int i = 0; i < s.length(); i++) { String sub = s.substring(1, s.length()); // 如果一个数不在他第一次出现的位置,那么这个字符是重复的,跳过这个字符;否则才递归 if (i == index[i]) {// 查看第i个字符在字符串中出现的位置是不是与i相同,如果不同,说明是重复的 list.add("" + s.charAt(0)); get(sub, list); list.remove(list.size() - 1); } s = sub + s.charAt(0); } } } }