Java 排列組合的有趣算法
1. 用1、2、3、4、5這五個數字,用java寫一個main函數,打印出所有不同的排列,如:51234、41235等。- public class TestQuestion {
- static int[] bits = new int[] { 1, 2, 3, 4, 5 };
- /**
- * @param args
- */
- public static void main(String[] args) {
- sort("", bits);
- }
- private static void sort(String prefix, int[] a) {
- if (a.length == 1) {
- System.out.println(prefix + a[0]);
- }
- for (int i = 0; i < a.length; i++) {
- sort(prefix + a[i], copy(a, i));
- }
- }
- private static int[] copy(int[] a,int index){
- int[] b = new int[a.length-1];
- System.arraycopy(a, 0, b, 0, index);
- System.arraycopy(a, index+1, b, index, a.length-index-1);
- return b;
- }
- }
2. 對第一題增加難度,用1、2、2、3、4、5這六個數字,用java寫一個main函數,打印出所有不同的排列,如:512234、412345等,要求:"4"不能在第三位,"3"與"5"不能相連。
基本思路:
2-1. 把問題歸結為圖結構的遍歷問題。圖遍歷思想:實際上6個數字就是六個結點,把六個結點連接成無向連通圖,對于每一個結點求這個圖形的遍歷路徑,所有結點的遍歷路徑就是最后對這6個數字的排列組合結果集。
2-2. 顯然這個結果集還未達到題目的要求。從以下幾個方面考慮:
2-2-1. 3,5不能相連:實際要求這個連通圖的結點3,5之間不能連通, 可在構造圖結構時就滿足改條件,然后再遍歷圖。
2-2-2. 不能有重復: 考慮到有兩個2,明顯會存在重復結果,可以把結果集放在TreeSet中過濾重復結果
2-2-3. 4不能在第三位: 仍舊在結果集中去除滿足此條件的結果。
采用二維數組定義圖結構,最后的代碼是:
- import java.util.Iterator;
- import java.util.TreeSet;
- public class TestQuestion {
- private String[] b = new String[]{"1", "2", "2", "3", "4", "5"};
- private int n = b.length;
- private boolean[] visited = new boolean[n];
- private int[][] a = new int[n][n];
- private String result = "";
- private TreeSet set = new TreeSet();
- public static void main(String[] args) {
- new TestQuestion().start();
- }
- private void start() {
- // Initial the map a[][]
- for (int i = 0; i < n; i++) {
- for (int j = 0; j < n; j++) {
- if (i == j) {
- a[i][j] = 0;
- } else {
- a[i][j] = 1;
- }
- }
- }
- // 3 and 5 can not be the neighbor.
- a[3][5] = 0;
- a[5][3] = 0;
- // Begin to depth search.
- for (int i = 0; i < n; i++) {
- this.depthFirstSearch(i);
- }
- // Print result treeset.
- Iterator it = set.iterator();
- while (it.hasNext()) {
- String string = (String) it.next();
- // "4" can not be the third position.
- if (string.indexOf("4") != 2) {
- System.out.println(string);
- }
- }
- }
- private void depthFirstSearch(int startIndex) {
- visited[startIndex] = true;
- result = result + b[startIndex];
- if (result.length() == n) {
- // Filt the duplicate value.
- set.add(result);
- }
- for(int j = 0; j < n; j++) {
- if (a[startIndex][j] == 1 && visited[j] == false) {
- depthFirstSearch(j);
- } else {
- continue;
- }
- }
- // restore the result value and visited value after listing a node.
- result = result.substring(0, result.length() -1);
- visited[startIndex] = false;
- }
- }
3. 遞歸思想。遞歸要抓住的關鍵是:(1)遞歸出口;(2)地推逐步向出口逼近。
3-1. 漢諾塔
這是遞歸的超經典的例子,幾乎每本程序設計書上談到遞歸都會介紹。具體情景不再贅述。以我上述的方法觀之:
3-1-1. 遞歸的出口在于disk數為一的時候
3-1-2. 向出口逼近:如果不是一,是n ,則我們先挪動上面n-1塊disk,等上面挪完,即遞歸返回的時候,我們挪動最底下的disk.
僅僅如此,一個貌似十分復雜的問題就解決了,因為挪動那n-1塊disk的時候,會繼續向上減少,直到disk的數量為一為止。下面給出代碼:
- import javax.swing.JOptionPane;
- public class Hanoi{
- private static final String DISK_B = "diskB";
- private static final String DISK_C = "diskC";
- private static final String DISK_A = "diskA";
- static String from=DISK_A;
- static String to=DISK_C;
- static String mid=DISK_B;
- public static void main(String[] args) {
- String input=JOptionPane.showInputDialog("please input the number of the disks you want me move.");
- int num=Integer.parseInt(input);
- move(num,from,mid,to);
- }
- private static void move(int num, String from2, String mid2, String to2) {
- if(num==1){
- System.out.println("move disk 1 from "+from2+" to "+to2);
- }
- else {
- move(num-1,from2,to2,mid2);
- System.out.println("move disk "+num+" from "+from2+" to "+to2);
- move(num-1,mid2,from2,to2);
- }
- }
- }
abc
acb
bac
bca
cab
cba
分析:
3-2-1. 算法的出口在于:low=high也就是現在給出的排列元素只有一個時;
3-2-2. 算法的逼近過程:先確定排列的第一位元素,也就是循環中i所代表的元素,然后low+1開始減少排列元素,如此下去,直到low=high。(暫未調通)
- public static void permute(String str) {
- char[] strArray = str.toCharArray();
- permute(strArray, 0, strArray.length - 1);
- }
- public static void permute(char[] list, int low, int high) {
- int i;
- if (low == high) {
- String cout = "";
- for (i = 0; i <= high; i++)
- cout += list[i];
- System.out.println(cout);
- } else {
- for (i = low; i <= high; i++) {
- char temp = list[low];
- list[low] = list[i];
- list[i] = temp;
- permute(list, low + 1, high);
- temp = list[low];
- list[low] = list[i];
- list[i] = temp;
- }
- }
- }
3-3. 這是一個組合的例子,與上述的例子相似,只是它所做的工作是,輸出所給字符串中制定數目的元素的組合種類。
分析:
3-3-1. 程序出口在于n=1,此時只要輸出目標數組的所有元素即可
3-3-2. 逼近過程,當n>1 的時候,我們先取第一個元素放入目標數組中,然后n-1,如此下去,最后出來。
- import javax.swing.JOptionPane;
- public class Combination {
- /**
- * @param args
- */
- public static void main(String[] args) {
- String input = JOptionPane.showInputDialog("please input your String: ");
- String numString = JOptionPane.showInputDialog("please input the number of your Combination: ");
- int num = Integer.parseInt(numString);
- Combine(input, num);
- }
- private static void Combine(String input, int num) {
- char[] a = input.toCharArray();
- String b = "";
- Combine(a, num, b, 0, a.length);
- }
- private static void Combine(char[] a, int num, String b, int low, int high) {
- if (num == 0) {
- System.out.println(b);
- } else {
- for (int i = low; i < a.length; i++) {
- b += a[i];
- Combine(a, num - 1, b, i+1, a.length);
- b=b.substring(0, b.length()-1);
- }
- }
- }
- }
posted on 2011-07-20 16:12 七孑 閱讀(14103) 評論(1) 編輯 收藏 所屬分類: 經典算法典藏