17. 電話号碼的字母組合
46. 全排列
47. 全排列 II
39. 組合總和
40. 組合總和 II
491. 遞增子序列
77. 組合
78. 子集
90. 子集 II
60. 排列序列
784. 字母大小寫全排列
22. 括号生成
51. N 皇後
491. 遞增子序列
93. 複原 IP 位址
回溯算法入門級詳解 + 練習(持續更新)
文章目錄
-
- 17. 電話号碼的字母組合
- 46. 全排列
- 47. 全排列 II
- 39. 組合總和
- 40. 組合總和 II
- 77. 組合
- 78. 子集
- 90. 子集 II
- 60. 排列序列
- 784. 字母大小寫全排列
- 22. 括号生成
- 51. N 皇後
- 491. 遞增子序列
- 93. 複原 IP 位址
17. 電話号碼的字母組合
給定一個僅包含數字 2-9 的字元串,傳回所有它能表示的字母組合。答案可以按 任意順序 傳回。
給出數字到字母的映射如下(與電話按鍵相同)。注意 1 不對應任何字母。
class Solution {
public List<String> letterCombinations(String digits) {
List<String> list=new ArrayList<>();
StringBuffer strbf=new StringBuffer();
if(digits==null||digits.length()<=0)
return list;
Map<Character,String> map=new HashMap<>();
map.put('2',"abc");map.put('3',"def");map.put('4',"ghi");
map.put('5',"jkl");map.put('6',"mno");map.put('7',"pqrs");
map.put('8',"tuv");map.put('9',"wxyz");
backTrack(list,digits,map,strbf,0);
return list;
}
private void backTrack(List<String> list,String digits,Map<Character,String> map,StringBuffer strbf,int index){
if(index==digits.length()){
list.add(strbf.toString());
return;
}
String str=map.get(digits.charAt(index));
for(int i=0;i<str.length();i++){
strbf.append(str.charAt(i));
backTrack(list,digits,map,strbf,index+1);
strbf.deleteCharAt(index);
}
}
}
46. 全排列
給定一個 沒有重複 數字的序列,傳回其所有可能的全排列。
class Solution {
public List<List<Integer>> permute(int[] nums) {
List<Integer> list = new ArrayList<>();
List<List<Integer>> res=new ArrayList<>();
int n=nums.length;
boolean[] isused=new boolean[n];
backTrack(nums,list,res,0,n,isused);
return res;
}
private void backTrack(int[] nums,List<Integer>list,List<List<Integer>> res,int index,int n,boolean[] isused){
if(index==n){
res.add(new ArrayList<>(list));
return;
}
for(int i=0;i<n;i++){
if(isused[i]==false){
isused[i]=true;
list.add(nums[i]);
backTrack(nums,list,res,index+1,n,isused);
isused[i]=false;
list.remove(list.size()-1);
}
}
}
}
47. 全排列 II
給定一個可包含重複數字的序列 nums ,按任意順序 傳回所有不重複的全排列。
差解:用了一個Set判斷
class Solution {
public List<List<Integer>> permuteUnique(int[] nums) {
List<Integer> list = new ArrayList<>();
List<List<Integer>> res=new ArrayList<>();
Set<List<Integer>> set=new HashSet<>();
int n=nums.length;
boolean[] isused=new boolean[n];
backTrack(nums,list,set,0,n,isused);
return new ArrayList(set);
}
private void backTrack(int[] nums,List<Integer>list,Set<List<Integer>> res,int index,int n,boolean[] isused){
if(index==n){
res.add(new ArrayList<>(list));
return;
}
for(int i=0;i<n;i++){
if(isused[i]==false){
isused[i]=true;
list.add(nums[i]);
backTrack(nums,list,res,index+1,n,isused);
isused[i]=false;
list.remove(list.size()-1);
}
}
}
}
優解:
class Solution {
public List<List<Integer>> permuteUnique(int[] nums) {
List<Integer> list = new ArrayList<>();
List<List<Integer>> res=new ArrayList<>();
Arrays.sort(nums);
int n=nums.length;
boolean[] isused=new boolean[n];//這個是說目前位置的元素是否被使用過
backTrack(nums,list,res,0,n,isused);
return res;
}
private void backTrack(int[] nums,List<Integer>list,List<List<Integer>> res,int index,int n,boolean[] isused){
if(index==n){
res.add(new ArrayList<>(list));
return;
}
for(int i=0;i<n;i++){
if(isused[i]||(i>0&&nums[i]==nums[i-1]&&!isused[i-1]))//可能會疑惑這裡為什麼要加isused[i-1}
continue;
isused[i]=true;
list.add(nums[i]);
backTrack(nums,list,res,index+1,n,isused);
isused[i]=false;
list.remove(list.size()-1);
}
}
}
當看了下面的組合總和II後可能會疑惑為什麼要加這一句話。
這是因為我們這裡的插入空隙的選擇是從0開始的,而下面的題是從index開始的。
即差別以下兩種狀況:
—— —— ——
1.1(第一個一) 1.2 2
1.2(第二個一) 1.1 2
當從下标0開始選擇,選擇1.1,然後進入下一個位置(第二個位置)進行選擇,而如果不寫上isused[i-1]這句話的話,則會無法選擇1.2
而(i>0&&nums[i]==nums[i-1)這句話的作用,就是為了讓第一個位置無法選擇1.2.
意思是,前一句是為了讓第一個位置無法選擇1.2,後一句是為了讓第二個位置可以選擇1.2.可以與組合綜合II對比一下。
39. 組合總和
給定一個無重複元素的數組 candidates 和一個目标數 target ,找出 candidates 中所有可以使數字和為 target 的組合。
candidates 中的數字可以無限制重複被選取。
說明:
所有數字(包括 target)都是正整數。
解集不能包含重複的組合。
class Solution {
public List<List<Integer>> combinationSum(int[] candidates, int target) {
List<Integer> list=new ArrayList<>();
List<List<Integer>> res=new ArrayList<>();
backTack(candidates,target,0,list,res,0);
return res;
}
private void backTack(int[] candidates,int target,int sum,List<Integer> list,List<List<Integer>> res,int index){
if(sum==target){
res.add(new ArrayList<>(list));
return;
}
if(sum>target)
return;
for(int i=index;i<candidates.length;i++){
list.add(candidates[i]);
backTack(candidates,target,sum+candidates[i],list,res,i);
list.remove(list.size()-1);
}
}
}
40. 組合總和 II
給定一個數組 candidates 和一個目标數 target ,找出 candidates 中所有可以使數字和為 target 的組合。
candidates 中的每個數字在每個組合中隻能使用一次。
說明:
所有數字(包括目标數)都是正整數。
解集不能包含重複的組合。
出現重複解:
class Solution {
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
List<Integer> list=new ArrayList<>();
List<List<Integer>> res=new ArrayList<>();
backTack(candidates,target,list,res,0,0);
return res;
}
private void backTack(int[] candidates,int target,List<Integer>list,List<List<Integer>> res,int sum,int index){
if(sum==target){
res.add(new ArrayList<>(list));
return;
}
if(sum>target||index==candidates.length){
return;
}
for(int i=index;i<candidates.length;i++){
list.add(candidates[i]);
backTack(candidates,target,list,res,sum+candidates[i],i+1);
list.remove(list.size()-1);
}
}
}
無重複解:
class Solution {
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
List<Integer> list=new ArrayList<>();
List<List<Integer>> res=new ArrayList<>();
Arrays.sort(candidates);//排序
backTack(candidates,target,list,res,0,0);
return res;
}
private void backTack(int[] candidates,int target,List<Integer>list,List<List<Integer>> res,int sum,int index){
if(sum==target){
res.add(new ArrayList<>(list));
return;
}
if(sum>target||index==candidates.length){
return;
}
for(int i=index;i<candidates.length;i++){
if(i>index&&candidates[i]==candidates[i-1])//判斷
continue;
list.add(candidates[i]);
backTack(candidates,target,list,res,sum+candidates[i],i+1);//當index=i+1時,是不允許目前元素再次使用的,而當index=i時,是允許目前元素再次使用的
list.remove(list.size()-1);
}
}
}
if(i>index&&candidates[i]==candidates[i-1])//判斷
continue;
可以将這一句了解為:
插入目前位置的時候,隻能插一次
因為回溯算法和空白位置插入是差不多的。
如: —— —— —— (1,2,2)在這三個位置上進行計算求和
第一個位置可以是1,第二個位置可以是2.1(第一個二),也可以是2.2(第二個二)
在這種情況下就會産生:
1,2.1,2.2
1,2.2,2.1
這樣的重複,但,加入我們在第二個位置加上限定:不允許插入和前一個元素相同的元素,那麼,2.2就無法插入到第二個位置了。
就隻會有1,2.1,2.2這一種選擇。
77. 組合
給定兩個整數 n 和 k,傳回 1 … n 中所有可能的 k 個數的組合。
class Solution {
public List<List<Integer>> combine(int n, int k) {
List<Integer> list=new ArrayList<>();
List<List<Integer>> res=new ArrayList<>();
backTack(n,k,list,res,1,0);
return res;
}
private void backTack(int n,int k,List<Integer> list, List<List<Integer>> res,int begin,int len){
if(len==k){
res.add(new ArrayList<>(list));
return;
}
for(int i=begin;i<=n;i++){
list.add(i);
backTack(n,k,list,res,i+1,len+1);
list.remove(list.size()-1);
}
}
}
78. 子集
給你一個整數數組 nums ,數組中的元素 互不相同 。傳回該數組所有可能的子集(幂集)。
解集 不能 包含重複的子集。你可以按 任意順序 傳回解集。
class Solution {
public List<List<Integer>> subsets(int[] nums) {
List<Integer> list=new ArrayList<>();
List<List<Integer>> res=new ArrayList<>();
backTack(nums,0,0,list,res);
return res;
}
private void backTack(int[] nums,int index,int len,List<Integer> list,List<List<Integer>> res){
if(len>nums.length)
return;
res.add(new ArrayList<>(list));
for(int i=index;i<nums.length;i++){
list.add(nums[i]);
backTack(nums,i+1,len+1,list,res);
list.remove(list.size()-1);
}
}
}
90. 子集 II
給你一個整數數組 nums ,其中可能包含重複元素,請你傳回該數組所有可能的子集(幂集)。
解集 不能 包含重複的子集。傳回的解集中,子集可以按 任意順序 排列。
class Solution {
public List<List<Integer>> subsetsWithDup(int[] nums) {
List<Integer> list=new ArrayList<>();
List<List<Integer>> res=new ArrayList<>();
boolean[] isused=new boolean[nums.length];
Arrays.sort(nums);
backTack(nums,list,res,0,0,isused);
return res;
}
private void backTack(int[] nums,List<Integer> list, List<List<Integer>> res,int index,int len,boolean[] isused){
if(len>nums.length)
return;
res.add(new ArrayList<>(list));
for(int i=index;i<nums.length;i++){
if(i>0&&nums[i]==nums[i-1]&&!isused[i-1])
continue;
isused[i]=true;
list.add(nums[i]);
backTack(nums,list,res,i+1,len+1,isused);
list.remove(list.size()-1);
isused[i]=false;
}
}
}
60. 排列序列
給出集合 [1,2,3,…,n],其所有元素共有 n! 種排列。
按大小順序列出所有排列情況,并一一标記,當 n = 3 時, 所有排列如下:
“123”
“132”
“213”
“231”
“312”
“321”
給定 n 和 k,傳回第 k 個排列。
class Solution {
String str;
int sum=0;
public String getPermutation(int n, int k) {
boolean[] isused=new boolean[n];
backTrack(n,k,0,isused,new StringBuffer());
return str;
}
private void backTrack(int n,int k,int index,boolean[] isused,StringBuffer sb){
if(sum==k)
return;
if(index==n){
sum++;
if(sum==k){
str=sb.toString();
return;
}
}
for(int i=0;i<n;i++){
if(isused[i])
continue;
isused[i]=true;
sb.append(Integer.toString(i+1));
backTrack(n,k,index+1,isused,sb);
sb.deleteCharAt(sb.length()-1);
isused[i]=false;
}
}
}
784. 字母大小寫全排列
給定一個字元串S,通過将字元串S中的每個字母轉變大小寫,我們可以獲得一個新的字元串。傳回所有可能得到的字元串集合。
示例:
輸入:S = “a1b2”
輸出:[“a1b2”, “a1B2”, “A1b2”, “A1B2”]
輸入:S = “3z4”
輸出:[“3z4”, “3Z4”]
輸入:S = “12345”
輸出:[“12345”]
class Solution {
public List<String> letterCasePermutation(String S) {
List<String> list=new ArrayList<>();
backTrack(S,0,new StringBuffer(),list);
return list;
}
private void backTrack(String s,int index,StringBuffer sb,List<String> list){
if(index==s.length()){
list.add(sb.toString());
return;
}
if(s.charAt(index)>='0'&&s.charAt(index)<='9'){
sb.append(s.charAt(index));
backTrack(s,index+1,sb,list);
sb.deleteCharAt(sb.length()-1);
return;
}
char c=helper(s.charAt(index));
sb.append(c);
backTrack(s,index+1,sb,list);
sb.deleteCharAt(sb.length()-1);
sb.append(s.charAt(index));
backTrack(s,index+1,sb,list);
sb.deleteCharAt(sb.length()-1);
}
private char helper(char c){
if(c>='A'&&c<='Z')
return Character.toLowerCase(c);
else
return Character.toUpperCase(c);
}
}
22. 括号生成
數字 n 代表生成括号的對數,請你設計一個函數,用于能夠生成所有可能的并且 有效的 括号組合。
示例 1:
輸入:n = 3
輸出:["((()))","(()())","(())()","()(())","()()()"]
示例 2:
輸入:n = 1
輸出:["()"]
class Solution {
public List<String> generateParenthesis(int n) {
List<String> list=new ArrayList<>();
backTrack(n,new StringBuffer(),list,0,0);
return list;
}
private void backTrack(int n,StringBuffer sb,List<String> list,int left,int right){
if(left>n||right>n||left<right)
return;
if(left==n&&right==n){
list.add(sb.toString());
return;
}
sb.append("(");
backTrack(n,sb,list,left+1,right);
sb.deleteCharAt(sb.length()-1);
sb.append(")");
backTrack(n,sb,list,left,right+1);
sb.deleteCharAt(sb.length()-1);
}
}
51. N 皇後
n 皇後問題 研究的是如何将 n 個皇後放置在 n×n 的棋盤上,并且使皇後彼此之間不能互相攻擊。
給你一個整數 n ,傳回所有不同的 n 皇後問題 的解決方案。
每一種解法包含一個不同的 n 皇後問題 的棋子放置方案,該方案中 ‘Q’ 和 ‘.’ 分别代表了皇後和空位。
491. 遞增子序列
給定一個整型數組, 你的任務是找到所有該數組的遞增子序列,遞增子序列的長度至少是 2 。
示例:
輸入:[4, 6, 7, 7]
輸出:[[4, 6], [4, 7], [4, 6, 7], [4, 6, 7, 7], [6, 7], [6, 7, 7], [7,7], [4,7,7]]
93. 複原 IP 位址
給定一個隻包含數字的字元串,用以表示一個 IP 位址,傳回所有可能從 s 獲得的 有效 IP 位址 。你可以按任何順序傳回答案。
有效 IP 位址 正好由四個整數(每個整數位于 0 到 255 之間組成,且不能含有前導 0),整數之間用 ‘.’ 分隔。
例如:“0.1.2.201” 和 “192.168.1.1” 是 有效 IP 位址,但是 “0.011.255.245”、“192.168.1.312” 和 “[email protected]” 是 無效 IP 位址。