一:回溯算法(加剪枝、去重
1、 #17 电话号码的字母组合
class Solution {
public static List<String> letterCombinations(String digits) {
if (digits.length()==0){
return new ArrayList<>();
}
String[] strings = new String[]{"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
List<String> res = new ArrayList<>();
int index = 0;
return letterCombinationsHelp(strings,0,digits,res,"");
}
public static List<String> letterCombinationsHelp(String[] strings,int index ,String digits,List<String> list,String temp) {
if (index<digits.length()-1){
int digit = digits.charAt(index)-'0';//当前数字字符对应的整型数字
for (int j=0;j<strings[digit].length();j++){
list = letterCombinationsHelp(strings,index+1,digits,list,temp+strings[digit].charAt(j));
}
}else{
int digit = digits.charAt(index)-'0';//当前数字字符对应的整型数字
for (int j=0;j<strings[digit].length();j++){
list.add(temp+strings[digit].charAt(j));
}
}
return list;
}
}
2、 #22 括号生成
class Solution {
public static List<String> generateParenthesis(int n) {
if (n==0) return new ArrayList<>();
int left=0,right=0;//左括号好有括号初始个数都是0
List<String> list = new ArrayList<>();
generateParenthesisHelp(list,n,left,right,"");
return list;
}
public static void generateParenthesisHelp(List<String> list,int n,int left,int right,String temp) {
if (left==n&&right==n){
list.add(temp);
return;
}
if (left < n) { // 如果左括号还剩余的话,可以拼接左括号
generateParenthesisHelp(list,n,left+1,right,temp+"(");
}
if (left>right) { // 如果右括号剩余多于左括号剩余的话,可以拼接右括号
generateParenthesisHelp(list,n,left,right+1,temp+")");
}
}
}
3、 #39. 组合总和
class Solution {
List<List<Integer>> res = new ArrayList<>();
public List<List<Integer>> combinationSum(int[] candidates, int target) {
Arrays.sort(candidates);//升序排列
combinationSumHelp(new ArrayList<>(),candidates,target,0);
return res;
}
public void combinationSumHelp(List<Integer> list,int[] candidates, int target,int index){
if (target<0) return;
if (target==0) {
res.add(new ArrayList<>(list));
return;
}
//target>0
for (int i=index;i<candidates.length;i++){
if (target-candidates[i]<0) break;//如果当前数字不满足,后面的都不满足,直接break
list.add(candidates[i]);
target-=candidates[i];
combinationSumHelp(list,candidates,target,i);
target+=list.remove(list.size()-1);
}
}
}
4、 #组合总和 II
class Solution {
public static List<List<Integer>> combinationSum2(int[] candidates, int target) {
List<List<Integer>> result = new ArrayList<>();
Arrays.sort(candidates);
combinationSum2Help(result,new ArrayList<Integer>(),candidates,target,0);
return result;
}
public static void combinationSum2Help(List<List<Integer>> result,List<Integer> list,int[] candidates, int target,int index) {
if (target<0){
return;
}
if (target==0&&!result.contains(list)){
result.add(new ArrayList<>(list));
return;
}
for (int i=index;i<candidates.length;i++){
if (target-candidates[i]<0){
break;
}
list.add(candidates[i]);
target-=candidates[i];
combinationSum2Help(result,list,candidates,target,i+1);
target+=list.remove(list.size()-1);
}
}
}
5、 #46 全排列
class Solution {
public List<List<Integer>> permute(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
permuteHelp(nums,res,new ArrayList<>());
return res;
}
public void permuteHelp(int[] nums,List<List<Integer>> res,List<Integer> list) {
if (list.size()==nums.length){
res.add(new ArrayList<>(list));
return;
}
for (int i =0 ;i<nums.length;i++){
if (!list.contains(nums[i])){
list.add(nums[i]);
permuteHelp(nums,res,list);
list.remove(list.size()-1);
}
}
}
}
6、 #47全排列 II
class Solution {
public List<List<Integer>> permuteUnique(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
Arrays.sort(nums);
int[] visited=new int[nums.length];
permuteUniqueHelp(res,new ArrayList<>(),nums,visited);
return res;
}
public void permuteUniqueHelp(List<List<Integer>> res,List<Integer> list,int[] nums,int[] visited){
if (list.size()==nums.length){
res.add(new ArrayList<>(list));
}
for (int i =0;i<nums.length;i++){
if (visited[i]==0){
if (i>0&&nums[i]==nums[i-1]&&visited[i-1]==0){
//重点就是剪枝:如果这个数和之前的数一样,并且之前的数还未使用过(说明已经回溯过)
continue;
}
visited[i]=1;
list.add(nums[i]);
permuteUniqueHelp(res,list,nums,visited);
list.remove(list.size()-1);
visited[i]=0;
}
}
}
}
7、 #51. N皇后
class Solution {
public List<List<String>> solveNQueens(int n) {
/*
想法:从第一行开始递归,每一行都有n个选择,当满足n皇后的条件时放置Q,否则剪枝;
因为有多个选择,所以可以撤销之前的选择,;
*/
//初始化 n 行 n 列的...
StringBuffer stringBuffer = new StringBuffer();
List<String> list = new ArrayList<>();
List<List<String>> res = new ArrayList<>();
for (int i =0;i<n;i++){
stringBuffer.append(".");
}
for (int i =0;i<n;i++){
list.add(stringBuffer.toString());
}
solveNQueensHelp(res,list,n,0);
return res;
}
public void solveNQueensHelp(List<List<String>> res,List<String> list,int n,int row) {
if (row==n){
res.add(new ArrayList<>(list));
return;
}
for (int j =0;j<n;j++){
if (!IsSolveNQueens(list,n,row,j)){
continue;
}
String temp = list.get(row);
temp=temp.substring(0,j)+'Q'+temp.substring(j+1,n);
list.set(row,temp);
solveNQueensHelp(res,list,n,row+1);
String tp = list.get(row);
tp=tp.substring(0,j)+'.'+tp.substring(j+1,n);
list.set(row,tp);
}
}
public boolean IsSolveNQueens(List<String> list,int n,int row,int col) {//当前位置是否可以放置Q
//因为当前行的的后面几行 还没有放置元素,所以直接判断当前列、左上对角线、右上对角线是否有重复元素即可
for (int i=0;i<n;i++){
if (list.get(i).charAt(col)=='Q'){
return false;
}
}
for (int i = row-1,j=col+1;i>=0&&j<n;i--,j++ ){//右上
if (list.get(i).charAt(j) == 'Q') {
return false;
}
}
for (int i=row-1,j=col-1;i>=0&&j>=0;i--,j--){//左上
if (list.get(i).charAt(j) == 'Q') {
return false;
}
}
return true;
}
}
8、 #60. 第k个排列
public static String getPermutation(int n, int k) {
/*
因为是按照顺序排列,1 开头的排列完之后,开始 2 ,然后 3 ,4开头的排列
分别以 1,2,3,4开头的排列有 (n-1)!个
比如 n=4,k=18 ,list(1,2,3,4)
index=ceil(k/3!)=3,找到第一个开头的数字是list中第几个 ,此时 是第3个,也就是 第一个开头的数字是 3
3之前开头的数字也就是1,2开头的排列有(4-1)!*2种=12
list中还剩下 1,2,4,所以找第 18 个排列变成了在剩余list中找第 k= 18-12=6个排列
1,2,4开头的数字分别有 2!个
index = ceil(k/2!) = 3 是list中的第三个数字 4;
以此类推 k = k- (3-1)*2!=2;
list(1,2)
index = ceil(k/1!) = 2; list中的第2个数字 2;
把最后一个数字 1 放在字符串结尾
最终结果 3421
*/
List<Integer> list = new ArrayList<>();
for (int i=1;i<=n;i++){
list.add(i);
}
String res="";
while (list.size()>1){
int index = (int) Math.ceil(1.0*k/fb(n-1));//找第k个是以那个数字开头的
res+=list.remove(index-1);
//从list中剩下的几个数字里面找新的index
k = k-(index-1)*fb(n-1);//去掉index之前的排列数,找新的第k个排列
n=n-1;
}
res+=list.get(0);
return res;
}
public static int fb(int n){
if (n==1)
return 1;
return n*fb(n-1);
}
9、 #77.组合
public static List<List<Integer>> combine(int n, int k) {
List<List<Integer>> res = new ArrayList<>();
int[] nums = new int[n];
for (int i = 0; i < n; i++) {
nums[i]=i+1;
}
combineHelp(res,new ArrayList<>(),nums,n,k,0);
return res;
}
public static void combineHelp(List<List<Integer>> res,List<Integer> list,int[] nums,int n, int k,int index) {
if (list.size()==k) {
res.add(new ArrayList<>(list));
return;
}
for (int i = index;i<=n-(k-list.size());i++) {
/* 1,2,3,4,5 。n=5,k=3时,序号0,1,2,3,4,添加3个数时,i的最大值是2即最后一个组合是(2,3,4),此时max(i)=n-k=2,
* 当list添加1,list(1),添加2个数时,从序号1,2,3,4里面找两个,最后一个组合是(3,4),此时max(i)=n-k+3;
* 当list(1,2)时,从3,4里面找1个数,maix(i)=n-k+2=4;
* 可以看成是i<=n-(k-list.size())
* i<=n-(k-list.size())减枝
*/
list.add(nums[i]);
combineHelp(res, list, nums, n, k, i+1);
list.remove(list.size()-1);
}
}
10、#78. 子集
public List<List<Integer>> subsets(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
subsetsHelp(res,new ArrayList<>(),nums,0);
return res;
}
public void subsetsHelp(List<List<Integer>> res,List<Integer> list,int[] nums,int index){
res.add(new ArrayList<>(list));
for (int i=index;i<nums.length;i++){
list.add(nums[i]);
subsetsHelp(res,list,nums,i+1);
list.remove(list.size()-1);
}
}
11、#90. 子集 II
public List<List<Integer>> subsetsWithDup(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
Arrays.sort(nums);
subsetsWithDupHelp(res,new ArrayList<>(),nums,0);
return res;
}
public void subsetsWithDupHelp(List<List<Integer>> res,List<Integer> list,int[] nums,int index){
res.add(new ArrayList<>(list));
for (int i=index;i<nums.length;i++){
if (i>index&&nums[i]==nums[i-1]){
continue;
}
list.add(nums[i]);
subsetsWithDupHelp(res,list,nums,i+1);
list.remove(list.size()-1);
}
}