每日演算法之字串的排列
阿新 • • 發佈:2022-12-09
JZ38 字串的排列
描述
輸入一個長度為 n 字串,打印出該字串中字元的所有排列,你可以以任意順序返回這個字串陣列。
例如輸入字串ABC,則輸出由字元A,B,C所能排列出來的所有字串ABC,ACB,BAC,BCA,CBA和CAB。
題目主要資訊
給定一個長度為n的字串,求其中所有字元的全排列
字串中可能有重複字元,列印順序任意
字串中只包含大小寫字母
思路
都是求元素的全排列,字串與陣列沒有區別,一個是數字全排列,一個是字元全排列。為了便於去掉重複情況,還是參照陣列全排列,優先考慮字典序排序,因為排序後重復的字元就會相鄰,後序遞迴找起來也很方便 使用臨時變數去組裝一個全排列情況:每當我們選取一個字元以後,就確定了其位置,相當於對字串中剩下的元素進行全排列新增在該元素的後面,給剩餘部分進行全排列就是一個子問題,因此可以使用遞迴。 終止條件:臨時字串中選取了n個元素,已經形成了一種排列情況,可以將其加入輸出陣列中。 返回值:每一層給上一層返回的就是本層級在臨時字串中新增的元素,遞迴到末尾的時候,就能新增全部元素 具體做法 先對字串按照字典序排序,獲得第一個排列情況 準備一個空串,暫存遞迴過程中組裝的排列情況。使用額外的vis陣列用於記錄哪些位置的字元被加入了 每次遞迴從頭遍歷字串,獲取字元加入:首先根據vis陣列,已經加入的元素不能再加入了;同時,如果當前的元素str[i]與同一層的前一個元素str[i-1]相同,且str[i-1]已經用,也不需要將其加入 進入下一等遞迴前將vis陣列當前位置標記為使用過 回溯時,需要修改vis陣列當前位置標記,同時去掉剛剛加入的字串元素 臨時字串長度達到原串長度就是一種排列情況
程式碼
package mid.JZ38字串的排列; import java.util.ArrayList; import java.util.Arrays; public class Solution { private StringBuilder builder = new StringBuilder(); private ArrayList<String> res = new ArrayList<>(); public ArrayList<String> Permutation(String str) { if (str == null || str.length() == 0) { return res; } char[] chars = str.toCharArray(); Arrays.sort(chars); String sortedStr = new String(chars); //當vis[i]=0,表示index為0的字元沒有被使用 //當vis[i]=1,表示index為1的字元被使用了 int[] vis = new int[chars.length]; dfs(sortedStr, vis, 0); return res; } private void dfs(String str, int[] vis, int depth) { if (builder.length() == str.length()) { res.add(builder.toString()); return; } for (int i = 0; i < str.length(); i++) { if (vis[i] == 1) { continue; } //當前的元素str[i]與同一層的前一個元素str[i-1]相同且str[i-1]已經用過了 if (i != 0 && str.charAt(i) == str.charAt(i - 1) && vis[i - 1] == 1) { continue; } builder.append(str.charAt(i)); vis[i] = 1; dfs(str, vis, depth + 1); builder.deleteCharAt(builder.length() - 1); vis[i] = 0; } } public static void main(String[] args) { System.out.println(new Solution().Permutation("ab")); } }