全排列:給定幾個數,要求找出全部的排列方式。之前遇到的全排列,清一色的dfs回溯,本身知道時間複雜度挺高的,最近遇到poj2718認真總結了下全排列。
詳細代碼爲java
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 stub
int len=a.length;
if(index==a.length)//中止
{
if(b[0]==0) {}
else {
for(int j:b)
{
System.out.print(j+" ");
}
System.out.println();
}
}
else
for(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;//還原
}
}
}
}
複製代碼
輸出打印結果爲:git
輸入: 1 2 3 4 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
運行時間:2msgithub
上述方法雖然可以實現全排列,可是方法的複雜度仍是很高。指數級別增加。由於要遍歷不少沒用的狀況。因此當數據較大並不能高速處理。因此換一種思路處理。 設[a,b,c,d]爲abcd的全排列 那麼,該全排列就是 [1,2,3,4](四個數的全排列)= 數組
1 [2,3,4](1開頭[2,3,4]的全排列)=函數
2 [1,3,4]=大數據
3 [2,1,4]=(略)spa
4 [2,3,1]=(略)code
對於全排列遞歸的模式爲:(和dfs很像)cdn
根據上面的數據找點規律吧:blog
上面是遞歸沒毛病。整個全排列就是子排列遞歸到最後遍歷的全部狀況
千萬別被用回溯的得到全排列的數據影響。博主以前卡了好久一直想着從回溯到獲得的數據中找到遞歸的關係,結果寫着寫着就寫崩了。
遞歸的數據有規律。它只關注位置而不關注數據的大小排列。意思是說你不須要糾結每一種排列的初始態是啥。你只要關注他有那些數就行,舉個例子,出臺爲1 [2,3,4]和1 [4,3,2]的效果同樣,你須要關注的是1這個部分。1這個部分處理好遞歸天然會處理好子節點的關係。
對於同一層級 好比1[],2[],3[],4[],例如1,2,3,4而言,每一層以下的步驟,能夠保證同層數據可靠,而且底層按照以下思路也是正確的。
因此整個全排列函數大體爲:
import java.util.Scanner;
public class quanpailie2 {
public static void main(String[] args) {
// TODO Auto-generated method stub
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]);
}
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進行全排序:
對於全排列,建議能採用遞歸仍是遞歸。由於遞歸沒有額外數組開銷。而且計算的每一次都有用。而回溯會有不少無用計算。數只越大越明顯。
題意就是給幾個不重複的數字,讓你找出其中全部排列方式中組成的兩個數的差值最小。除了大小爲0不然0不作開頭。
思路:全排列全部狀況。最小的必定是該全排列從中間分紅2半的數組差。要注意的就是0的處理,不日3個長度的0開頭/其餘長度的0開頭等等。還有的人採用貪心剪枝。我的感受數據並無那麼有規律貪心不必定好處理,可是你能夠適當剪枝減小時間也是能夠的。好比根據兩個數據的首位 相應剪枝。但這題全排列就能夠過。
還有一點就是:數據加減乘除轉換,能用int就別用String,string轉起來很慢會超時。
附上ac代碼,代碼可能並不漂亮,前面的介紹也可能有疏漏,還請大佬指出!
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 stub
BufferedReader 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;
}
}
複製代碼
若有錯誤還請大佬指正。