全排列两种实现方式(java)—poj2718
前言
以前遇到的全排列,清一色的dfs回溯,自己知道時間復雜度挺高的,最近遇到poj2718認真總結了下全排列。
全排列:給定幾個數,要求找出所有的排列方式。
法一:dfs回溯法:
- 思路:回溯法的核心思路就是模擬過程,其實它相對簡單因為你往往不需要考慮它的下一步是什么,你只需關注如果操作這些數。你往往可能不在意數的規則規律但是也能搞出來。
- 舉個例子。有1,2,3,4,5五個數需要全排列。我用回溯法的話我可以用附加的數組,或者list,boolean數組等添加和刪除模擬的數據。
- 比如第一次你可以循環將第一個賦值(1-5),在賦值每個數的時候標記那些用過,那些還能用的數據,執行dfs一直到底層。然后dfs執行完要將數據復原。比如標記的數據進行取消標記等等。
詳細代碼為
package 全排列;import java.util.Scanner;public class quanpailie1 {public static void main(String[] args) {Scanner sc = new Scanner(System.in);String s[] = sc.nextLine().split(" ");int a[] = new int[s.length];for (int i = 0; i < s.length; i++) {a[i] = Integer.parseInt(s[i]);}int b[] = new int[a.length];boolean b1[] = new boolean[a.length];// 判斷是否被用long startTime = System.currentTimeMillis();dfs(b1, a, b, 0);long endTime = System.currentTimeMillis();System.out.println("運行時間:" + (endTime - startTime) + "ms");}private static void dfs(boolean[] b1, int[] a, int b[], int index) {// TODO Auto-generated method stubint len = a.length;if (index == a.length)// 停止{if (b[0] == 0) {} else {for (int j : b) {System.out.print(j + " ");}System.out.println();}} elsefor (int i = 0; i < len; i++) {if (!b1[i]) {b[index] = a[i];b1[i] = true;// 下層不能在用dfs(b1, a, b, index + 1);b1[i] = false;// 還原}}} }輸出打印結果為:
1 2 3 4
1 2 4 3
1 3 2 4
1 3 4 2
1 4 2 3
1 4 3 2
2 1 3 4
2 1 4 3
2 3 1 4
2 3 4 1
2 4 1 3
2 4 3 1
3 1 2 4
3 1 4 2
3 2 1 4
3 2 4 1
3 4 1 2
3 4 2 1
4 1 2 3
4 1 3 2
4 2 1 3
4 2 3 1
4 3 1 2
4 3 2 1
運行時間:2ms
法二:遞歸法
上述方法雖然能夠實現全排列,但是方法的復雜度還是很高。指數級別增長。因為要遍歷很多沒用的情況。所以當數據較大并不能高速處理。所以換一種思路處理。
設[a,b,c,d]為abcd的全排列
那么,該全排列就是
[1,2,3,4](四個數的全排列)
-
1 [2,3,4](1開頭[2,3,4]的全排列)
-
1 2 [3,4] (1,2開頭的[3,4]全排列)
-
1 2 3 [4] =1 2 3 4(1 2 3 開頭的[4]全排列)
-
1 2 4 [3]=1 2 3 4
-
1 3 [2,4]
-
1 3 2 [4]=1 3 2 4
-
1 3 4 [2]=1 3 4 2
-
1 4 [3,2]
-
1 4 3 [2]=1 4 3 2
-
1 4 2 [3]=1 4 2 3
-
2 [1,3,4]
-
2 1 [3,4]
-
2 1 3 [4]=2 1 3 4
-
2 1 4 [3]=2 1 4 3
-
2 3 [1,4]
-
2 3 1 [4]=2 3 1 4
-
2 3 4 [1]=2 3 4 1
-
2 4 [3,1]
-
2 4 3 [1]=2 4 3 1
-
2 4 1 [3]=2 4 1 3
-
3 [2,1,4]=(略)
-
3 2 [1,4]
-
3 1 [2,4]
-
3 4 [3,2]
-
4 [2,3,1](略)
-
4 2 [3,1]
-
4 3 [2,1]
-
4 1 [3,2]
對于全排列遞歸的模式為:(和dfs很像)
- isstop?: 判斷遞歸終止
- stop
- do not stop:
- before recursive()
- recursive()
- after recursive()
根據上面的數據找點規律吧:
- 1,2,3,4—>swap(0,0)—>1 [2 3 4] (子遞歸不用管)—>swap(0,0)—>1,2,3,4
- 1,2,3,4—>swap(0,1)—>2 [1 3 4] (子遞歸不用管)—>swap(0,1)—>1,2,3,4
- 1,2,3,4—>swap(0,2)—>3 [2 1 4] (子遞歸不用管)—>swap(0,1)—>1,2,3,4
- 1,2,3,4—>swap(0,3)—>4 [2 3 1] (子遞歸不用管)—>swap(0,1)—>1,2,3,4
- 停止
- 不停止:
-for(i from start to end) - swap(start,i)//i是從該層后面所有可能的全部要選一次排列到該層
- recursive(start 1)//該層確定,進入下一層子遞歸
- swap(start,i)//因為不能影響同層之間數據,要保證數據都是初始話
具體代碼為:
輸入輸出結果為:
1 2 3 4
1234
1243
1324
1342
1432
1423
2134
2143
2314
2341
2431
2413
3214
3241
3124
3142
3412
3421
4231
4213
4321
4312
4132
4123
運行時間:1ms
你可以發現兩者采用的規則不同,輸出的數據到后面是不一樣的。但是你可能還沒體驗到大數據對程序運行的影響。我把輸出的結果注釋掉。用0 1 2 3 4 5 6 7 8 9進行全排序:
對于全排列,建議能采用遞歸還是遞歸。因為遞歸沒有額外數組開銷。并且計算的每一次都有用。而回溯會有很多無用計算。數只越大越明顯。
poj2718
題意就是給幾個不重復的數字,讓你找出其中所有排列方式中組成的兩個數的差值最小。除了大小為0否則0不做開頭。
思路:全排列所有情況。最小的一定是該全排列從中間分成2半的數組差。要注意的就是0的處理,不日3個長度的0開頭/其他長度的0開頭等等。還有的人采用貪心剪枝。個人感覺數據并沒有那么有規律貪心不一定好處理,但是你可以適當剪枝減少時間也是可以的。比如根據兩個數據的首位 相應剪枝。但這題全排列就可以過。
還有一點就是:數據加減乘除轉換,能用int就別用String,string轉起來很慢會超時。
附上ac代碼,代碼可能并不漂亮,前面的介紹也可能有疏漏,還請大佬指出!
package 搜索;import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.PrintWriter;public class poj2718 {static int min = Integer.MAX_VALUE;static int mid = 0;public static void main(String[] args) throws IOException {// TODO Auto-generated method stubBufferedReader in = new BufferedReader(new InputStreamReader(System.in));PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));int t = Integer.parseInt(in.readLine());for (int q = 0; q < t; q++) {String s[] = in.readLine().split(" ");int a[] = new int[s.length];for (int i = 0; i < s.length; i++) {a[i] = Integer.parseInt(s[i]);}min = Integer.MAX_VALUE;mid = (a.length) / 2;arrange(a, 0, a.length - 1);out.println(min);out.flush();}}static void arrange(int a[], int start, int end) {if (start == end) { // for(int i:a) // { // System.out.print(i); // } // System.out.println();if ((a[0] == 0 && mid == 1) || (a[mid] == 0 && a.length - mid == 0) || (a[0] != 0 && a[mid] != 0)) {int va1 = 0;int va2 = 0;for (int i = 0; i < mid; i++) {va1 = va1 * 10 + a[i];}for (int i = mid; i < a.length; i++) {va2 = va2 * 10 + a[i];}min = min < Math.abs(va1 - va2) ? min : Math.abs(va1 - va2);}return;}for (int i = start; i <= end; i++) {swap(a, start, i);arrange(a, start + 1, end);swap(a, start, i);}}static void swap(int arr[], int i, int j) {int te = arr[i];arr[i] = arr[j];arr[j] = te;} }dfs回溯法github鏈接
遞歸法全排列github鏈接
poj2718代碼github鏈接
如有錯誤還請大佬指正。
- 如果對后端、爬蟲、數據結構算法等感性趣歡迎關注我的個人公眾號交流:bigsai
總結
以上是生活随笔為你收集整理的全排列两种实现方式(java)—poj2718的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 作业收缴系统设计手册(自写开源小系统)
- 下一篇: 素数筛模板