Java下實現無重字符串的全排列(遞歸和回溯方法)

給定一個無重複字符的字符串數組,實現它的全排列java

1、採用遞歸實現全排列

思想是從第一個字符開始排,一直排到只有一個字符那麼就說明一個排列完成了。不然就交換當前第k個字符和第i個字符(這裏的第i個字符指的是k後面全部字符的其中一個,例如abcd,當a固定,對bcd排列,那就要將b和c、b和d依次交換),再對k+1到m進行排列。當返回成功後就再將第k個字符和第i個字符再交換回來。數組

遞歸的終止條件就是k=m函數

k 爲起始下標,m爲結束下標,下面的函數用於返回從k到m的全排列測試

list[]用於存儲待排列的字符串spa

public static void perm(char list[], int k, int m) {
        if(k == m) {
            System.out.println(list);
        }
        else {
            for(int i = k; i <= m; i++) {
                //交換第k個字符和第i個字符,由於java是按值傳遞,因此我就沒用函數交換
                char temp;
                temp = list[k];
                list[k] = list [i];
                list[i] = temp;
//再對k後面的字符串進行全排列
                perm(list, k+1, m);
            //排列完後再換回來繼續下一層循環
                temp = list[k];
                list[k] = list [i];
                list[i] = temp;
            }
        }
    }


2、採用回溯實現全排列

思想是深度優先搜索,好比要找12345的全排列的一種排列12345code

第一層,有五種選擇,一、二、三、四、5,五個元素都標記沒被使用過遞歸

第二層,1下面有四種二、三、四、5, 1被標記使用過字符串

第三層,2下面有三種三、四、5, 一、2被標記使用過input

第四層,3下面有兩種四、5, 一、二、3被標記使用過string

第五層,4下面就只能排5了,一、二、三、4被標記使用過

這時就找到了一種排列12345,而後將5標記爲沒被使用過,返回到上一層4,將4也標記爲沒使用過,但5能夠選,而後再選4,這樣就實現了回溯,就能夠獲得全部解了。

具體代碼以下:

參數n表示第幾層,list是一個char型的數組,存放了待全排列的字符串

used[]是一個bool型的數組,具體下標對應的真假值判斷那個下標的字符有沒有被使用過,vector[]是一個整型數組,用於存放一個排列的解,amount用於計數,result函數用於將解向量輸出成排列。

public static void perm(char list[], int k, int m) {
        if(k == m) {
            System.out.println(list);
        }
        else {
            for(int i = k; i <= m; i++) {
                //交換
                char temp;
                temp = list[k];
                list[k] = list [i];
                list[i] = temp;
                //返回k以後的全排列
                perm(list, k+1, m);
                //交換回來
                temp = list[k];
                list[k] = list [i];
                list[i] = temp;
            }
        }
    }


完整的能夠經過輸入字符串(要保證無重複字符)來返回全部排列的測試代碼以下:

import java.nio.file.attribute.AclEntryPermission;
import java.util.Arrays;
import java.util.Scanner;
 
public class Generator {
    static boolean used[];//存放字符是否被使用過
    static char list[];//存放字符串
    static int vector[];//存放當前排列的解向量
    static int amount = 0;//用於計數
    public static void main(String[] args) {
        // 用戶輸入字符串
        String str;
        Scanner input = new Scanner(System.in);
        str = input.next();
        list = str.toCharArray();
        used = new boolean[str.length()];
        vector = new int[str.length()];
        
        //perm(list, 0, str.length()-1); //用遞歸實現全排列
        Aperm(0);//用回溯實現全排列,從第0層開始
        
        
    }
    
    public static void perm(char list[], int k, int m) {
        if(k == m) {
            System.out.println(list);
        }
        else {
            for(int i = k; i <= m; i++) {
                //交換
                char temp;
                temp = list[k];
                list[k] = list [i];
                list[i] = temp;
                //返回k以後的全排列
                perm(list, k+1, m);
                //交換回來
                temp = list[k];
                list[k] = list [i];
                list[i] = temp;
            }
        }
    }
    
    public static void Aperm(int n) {
        //第0層有i.length種選擇
        for(int i = 0; i < list.length; i++) {
            if(!used[i]) {
                vector[n] = i;//記錄當前解
                used[i] = true;//當前元素被使用
                if(n!= list.length - 1)
                    Aperm(n+1);//若不是最後一層則返回下一層的
                else//不然就是最後一層,那麼就找到了一種排列,輸出
                    System.out.println(amount++ +": "+ result(vector));
                //很重要,返回到上一層就得取消標記
                used[i] = false;
            }
        }
    }
    
    public static String result(int vector[]) {
        String string = "";
        for(int i = 0; i < vector.length; i++) {
            string += list[vector[i]];
        }
        return string;
    }
 
}
相關文章
相關標籤/搜索