1. 程式人生 > 實用技巧 >劍指27.字串的排列

劍指27.字串的排列

題目描述

輸入一個字串,按字典序打印出該字串中字元的所有排列。例如輸入字串abc,則按字典序打印出由字元a,b,c所能排列出來的所有字串abc,acb,bac,bca,cab和cba。

輸入描述:

輸入一個字串,長度不超過9(可能有字元重複),字元只包括大小寫字母。

思路

求整個字串的排列,可以看成兩步。

第一步:求所有可能出現在第一個位置的字元,即把第一個字元先與自身交換,再和後面所有的字元交換。每交換完一次相當於確定了一個字元。

第二步:固定第一個字元,求後面所有字元的排列。 例如接著確定下一個字元即第二個字元,讓它再與自身交換然後與它之後的字元逐一交換……

典型的遞迴

需要注意的地方:“輸出按字典序排”—因此需要有排序操作;“可能由字元重複”—因此需要去重

解法1

import java.util.*;
public class Solution {
    /*
     *  通過遞迴去查詢每一個位置的字元可能出現的情況。
     *  比如要找index位置的字元,那麼交換index以及index位置之後的那些字元即可。
     */
    public ArrayList<String> Permutation(String str) {
        ArrayList
<String> res = new ArrayList<>(); if (str.length() == 0 || str == null) return res; char[] strArray = str.toCharArray(); // 把字串String轉化為char型別的字元陣列 solve(strArray,0,res); res = new ArrayList<String>(new HashSet<String>(res)); // 去重操作 Collections.sort(res); //
字典排序 -> 等價於 res.sort(null); null意思是升序 return res; } private void solve(char[] strArray, int index, ArrayList<String> res) { int length = strArray.length; if (index == length - 1){ // 把當前確定的結果(char陣列的形式)轉化為字串儲存下來 res.add(String.valueOf(strArray)); // String.valueOf(strArray) 把字元陣列轉化為字串 }else{ // 確定index位置的字元 // i要從index位置開始,原因是當前index位置的字元可以是原始字串所對應的字元 for (int i = index; i < length; i++) { swap(strArray,index,i); // 交換index位置和index位置之後的字元 // 當前index位置的字元已經通過交換找到了,接著遞迴找下一個位置的字元 solve(strArray, index + 1, res); // 消除當前層遞迴時交換字元的影響,不消除會造成原index位置的字元發生變化 // 即字串輸出後要將字元交換回來,變回原始的字串,以便進行下一輪交換。 swap(strArray,index,i); } } } private void swap(char[] strArray, int a, int b){ char temp = strArray[a]; strArray[a] = strArray[b]; strArray[b] = temp; } }

解法2(在解法1的基礎上,使用TreeSet)

import java.util.*;
public class Solution {
    public ArrayList<String> Permutation(String str) {
        ArrayList<String> res = new ArrayList<>();
        if (str.length() == 0 || str == null)
            return res;
        TreeSet<String> treelist = new TreeSet<>(); //TreeSet是一個有序的集合,它的作用是提供有序的Set集合。
        Solve(str.toCharArray(),0,treelist);
        for (String s : treelist)
            res.add(s);
        return res;
    }
    private void Solve(char[] strArray, int index, TreeSet<String> treelist) {
        int length = strArray.length;
        if (index == length - 1){
            treelist.add(String.valueOf(strArray));
        }else{
            for (int i = index; i < length; i++) {
                swap(strArray,index,i);
                Solve(strArray,index + 1,treelist);
                swap(strArray,index,i);
            }
        }
    }
    private void swap(char[] strArray, int a, int b) {
        char temp = strArray[a];
        strArray[a] = strArray[b];
        strArray[b] = temp;
    }
}

拓展(字串的全組合)

首先對比一下全排列和全組合,給定一個字串"abc",

  • 全組合形式:a,b,c,ab,ac,bc,abc
  • 全排列形式:abc,acb,bac,bca,cab,cba

交換字串中的兩個字元時,雖然能得到兩個不同的排列,但卻是同一個組合。比如ab和ba是不同的排列,但只算一個組合。

全組合思路:

Java實現對 數字 進行全組合

import java.util.ArrayList;
public class test {
    public static void main(String[] args) {
        int[] A = {1,2,3,4};
        for (int i = 1; i <= A.length ; i++) {
            ArrayList<Integer> res = new ArrayList<>();
            getCombination(A, i, 0, res);
        }
    }
    private static void getCombination(int[] A, int m, int start, ArrayList<Integer> res) {
        if (m == 0){
            // m個元素已經找齊,則列印
            for (Integer i : res){
                System.out.print(i + " ");
            }
            System.out.println();
            return;
        }
        if (start < A.length){
            res.add(A[start]);
            //使用A[start],從剩下的(n-start)-1個元素中找m - 1個元素求它的組合
            getCombination(A,m - 1, start + 1, res);
            //不使用A[start],從剩下(n-start)-1個元素中找m個元字元,求它的組合
            res.remove(res.size() - 1);//刪掉本次新增的元素
            getCombination(A,m, start + 1, res);
        }
    }
}
View Code

Java實現對 字元 進行全組合

import java.util.ArrayList;
public class test {
    public static void main(String [] args) {
        String str = "abc";
        ArrayList<String> res = new ArrayList<>();
        for (int i = 1; i <= str.length(); i++) {
            multiCombination(str.toCharArray(), i,0, res);
        }
    }
    private static void multiCombination(char[] strArray, int m, int start,ArrayList<String> res) {
        if (m == 0) {
            System.out.println(res);
            return;
        }
        if (start < strArray.length) {
            res.add(String.valueOf(strArray[start]));
            multiCombination(strArray, m - 1,start + 1, res); // 從剩餘的n-1箇中選擇m-1個
            res.remove(res.size() - 1);
            multiCombination(strArray, m,start + 1, res);  // 從剩餘的n-1箇中選擇m個
        }
    }
}
View Code

舉一反三

如果題目是按照一定要求擺放若干個數字,則可以先求出這些數字的所有排列,然後一一判斷每個排列是不是滿足題意。

總結

去重操作:可以用HashMap()去重,也可以使用 list.contains() 方法判斷是否有重複字串,如果沒有重複,再進行 list.add操作。

排序操作:Collections.sort(list)可以將list中的字串進行排序。

字串和字元陣列的相互轉化:str.toCharArray() String.valueOf(strArray)

字元在遞迴過程中進行了交換後,要記得交換回來。

參考:

【Java】 劍指offer(38) 字串的排列

[劍指offer]求字元陣列的所有組合