日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

全排列两种实现方式(java)—poj2718

發布時間:2025/3/20 编程问答 13 豆豆
生活随笔 收集整理的這篇文章主要介紹了 全排列两种实现方式(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]和1 [4,3,2]的效果一樣,你需要關注的是1這個部分。1這個部分處理好遞歸自然會處理好子節點的關系。
  • 對于同一層級 比如1[],2[],3[],4[],例如1,2,3,4而言,每一層如下的步驟,可以保證同層數據可靠,并且底層按照如下思路也是正確的。
    • 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)//因為不能影響同層之間數據,要保證數據都是初始話
      具體代碼為:
    import java.util.Scanner;public class quanpailie2 {public static void main(String[] args) {// TODO Auto-generated method stubScanner 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]);}long startTime = System.currentTimeMillis();arrange(a, 0, a.length - 1);long endTime = System.currentTimeMillis();System.out.println("運行時間:" + (endTime - startTime) + "ms");}static void arrange(int a[], int start, int end) {if (start == end) { for (int i : a) {System.out.print(i);}System.out.println();return;}for (int i = start; i <= end; i++) {swap(a, i, start);arrange(a, start + 1, end);swap(a, i, start);}}static void swap(int arr[], int i, int j) {int te = arr[i];arr[i] = arr[j];arr[j] = te;} }

    輸入輸出結果為:

    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的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。