題目連接:https://leetcode.com/problems/permutations/description/html
題目大意:給出一串數組進行全排列(數組中數字惟一)。java
法一:模板全排列代碼標記數組版。代碼以下(耗時5ms):數組
1 public List<List<Integer>> permute(int[] nums) { 2 List<List<Integer>> list = new ArrayList<List<Integer>>(); 3 int[] vis = new int[nums.length]; 4 Arrays.fill(vis, 0);//初始化全爲0 5 List<Integer> tmp = new ArrayList<Integer>(); 6 dfs(list, nums, vis, tmp); 7 return list; 8 } 9 public static void dfs(List<List<Integer>> list , int[] nums, int[] vis, List<Integer> tmp) { 10 // System.out.println(tmp); 11 if(tmp.size() == nums.length) { 12 list.add(new ArrayList<Integer>(tmp));//這裏必定要用new以後再加入到list中,不然list集合會是空值 13 return; 14 } 15 for(int i =0 ; i < nums.length; i++) { 16 if(vis[i] == 0) { 17 vis[i] = 1; 18 tmp.add(nums[i]); 19 dfs(list, nums, vis, tmp); 20 tmp.remove(tmp.size() - 1);//刪除最後一個元素 21 vis[i] = 0; 22 } 23 } 24 }
另外一種實現方式:模板java版,利用list屬性來進行if條件判斷,省去vis標記數組。代碼以下(耗時26ms):ide
1 public List<List<Integer>> permute(int[] nums) { 2 List<List<Integer>> list = new ArrayList<List<Integer>>(); 3 List<Integer> tmp = new ArrayList<Integer>(); 4 dfs(list, nums, tmp); 5 return list; 6 } 7 public static void dfs(List<List<Integer>> list , int[] nums, List<Integer> tmp) { 8 // System.out.println(tmp); 9 if(tmp.size() == nums.length) { 10 if(!list.contains(tmp)) 11 list.add(new ArrayList<Integer>(tmp));//這裏必定要用new以後再加入到list中,不然list集合會是空值 12 return; 13 } 14 for(int i =0 ; i < nums.length; i++) { 15 if(!tmp.contains(nums[i])) { 16 tmp.add(nums[i]); 17 dfs(list, nums, tmp); 18 tmp.remove(tmp.size() - 1);//刪除最後一個元素 19 } 20 } 21 }
當序列是{1,2,3}時運行結果以下:函數
[] [1] [1, 2]--->由於這裏有if條件判斷是否已經選1,因此這裏不會再將第二個1加入到序列當中,而是跳過if條件直接執行i++操做,在第77題組合數中,這裏的i不是從0開始,而是每次dfs時,傳入一個起始下標,這樣就能夠不用if條件判斷起始下標前面的數值是否已經存在,由於確定不會重複選擇。在排列中,不能用傳入起始下標的辦法,由於有可能當前要選的數的下標是在起始下標以前的,若是用起始下標的話,前面的數就無法選到。 [1, 2, 3]--->兩次跳過if條件,執行i++ [1, 3]--->return執行remove,這裏remove的是2,不是3,由於return回來的時候第三個數起始尚未加進去 [1, 3, 2]--->兩次跳過if條件,執行i++ [2]--->return執行remove,這裏remove的是1,緣由同上 [2, 1] [2, 1, 3] [2, 3] [2, 3, 1] [3] [3, 1] [3, 1, 2] [3, 2] [3, 2, 1] [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]
法二(借鑑):非遞歸,假設咱們有了當前前 i 個元素的組合,當第 i+1個元素加入時,咱們須要作的是將這個元素加入以前的每個結果,而且放在每一個結果的每一個位置,由於以前的結果沒有重複,因此加入新元素的結果也不會有重複,好比當前i個元素組合是1,當加入第2個元素的時候,能夠選擇加在1以前,也能夠選擇加在1以後,這樣就能夠獲得兩個不一樣序列,依次類推。代碼以下(耗時7ms):post
1 public List<List<Integer>> permute(int[] nums) { 2 LinkedList<List<Integer>> res = new LinkedList<List<Integer>>();//這裏利用LinkedList,而不用ArrayList,由於這樣能夠利用LinkedList.add(i,n)在第i位加入數值 3 res.add(new ArrayList<Integer>());//加一個空的list進去,以保證下面size在第一次的時候就爲1,從而保證後面的計算 4 for(int n : nums) {//遍歷nums數組值 5 for(int size = res.size(); size > 0; size--) {//查看結果集res中已經包含幾個序列 6 List<Integer> tmp = res.pollFirst();//取出結果集res中的每一個序列 7 for(int i = 0; i <= tmp.size(); i++) {//對於取出的序列,在每個位置都嘗試加入當前nums數組值,這裏的i表示當前取出序列的第i位,好比取出的序列是1,接下來就要在1以前及1以後加入2 8 List<Integer> resIn = new ArrayList<Integer>(tmp);//每個位置都新初始化剛取出的序列,這樣保證能在原始取出序列的基礎上增添數值 9 resIn.add(i, n);//在第i位加入數值n 10 res.add(resIn);//這裏由於每從結果集中取出一個序列就是清空一個序列,因此這裏add的時候就是一個全新的序列 11 } 12 } 13 } 14 return res; 15 }
當數組是{1,2,3}時,運行結果以下:spa
[1] 第一次取到{1} [2, 1] [1, 2] 第二次取到{2,1} [3, 2, 1] [2, 3, 1] [2, 1, 3] 第三次取到{1,2} [3, 1, 2] [1, 3, 2] [1, 2, 3] [[3, 2, 1], [2, 3, 1], [2, 1, 3], [3, 1, 2], [1, 3, 2], [1, 2, 3]]
法三:由31題http://www.cnblogs.com/cing/p/8001308.html,求解下一個排列所引起的這題的題解,也就是C++裏STL所封裝的nextPermutation函數用java實現後,其餘的代碼跟C++調用相似。注意在求解排列以前,數組要先升序排序,由於nextPermutation的false結果就是在數組是降序時結束。不然會致使求出的全排列不徹底。代碼以下(耗時6ms):code
1 public List<List<Integer>> permute(int[] nums) { 2 List<List<Integer>> res = new ArrayList<List<Integer>>(); 3 Arrays.sort(nums); 4 do { 5 ArrayList<Integer> listIn = new ArrayList<Integer>(); 6 for(int i = 0; i < nums.length; i++) { 7 listIn.add(nums[i]); 8 } 9 //每求出一個全排列就加入res結果集中 10 //由下面的嘗試得知,必需要new一個新的,緣由不是新開闢了一個地址空間,而是爲了讓原地址空間不被垃圾回收器回收,而致使原數據沒法讀取的狀況 11 //listIn和a的地址空間相同 12 /* System.out.println(listIn.hashCode()); 13 List<Integer> a = new ArrayList<Integer>(listIn); 14 System.out.println(a.hashCode()); 15 res.add(a);*/ 16 res.add(new ArrayList<Integer>(listIn)); 17 } while(nextPermutation(nums) == true); 18 return res; 19 } 20 public static boolean nextPermutation(int[] nums) { 21 int length = nums.length; 22 int pre = 0, post = 0;//pre標記前面的第一個小數,即nums[pre]<nums[pre+1],post標記後面的第一個大數,即nums[post]>nums[pre] 23 boolean mark = false;//標記是不是降序序列 24 //從後往前找,找到第一個前面的數小於後面的數的下標 25 for(int i = length - 1; i > 0; i--) { 26 if(nums[i - 1] < nums[i]) { 27 mark = true; 28 pre = i - 1; 29 break; 30 } 31 } 32 //從後往前找,找到第一個比前面標記的數大的數的下標 33 for(int i = length - 1; i > 0; i--) { 34 if(nums[i] > nums[pre]) { 35 post = i; 36 break; 37 } 38 } 39 int mid = (length - pre - 1) / 2; 40 //若是直接是降序,直接反轉便可 41 if(mark == false) { 42 return false; 43 } 44 int tmp = nums[pre]; 45 nums[pre] = nums[post]; 46 nums[post] = tmp; 47 //反轉後面的降序序列爲升序序列 48 for(int i = pre + 1; i <= (pre + mid); i++) { 49 int t = nums[i]; 50 nums[i] = nums[--length]; 51 nums[length] = t; 52 } 53 return true; 54 }