有几个需要注意的要点:
// 需要new一个新的对象,不可以直接写成res.add(sub);res.add(new ArrayList<>(sub));// 如果是回溯算法,记得撤销动作,如果是基本类型的修改不用撤销sub.remove(sub.size()-1);
46. 全排列
class Solution {public List<List<Integer>> permute(int[] nums) {int n=nums.length;boolean[] used=new boolean[n];List<Integer> path=new ArrayList<>();List<List<Integer>> res=new ArrayList<>();backtrack(nums,path,used,res);return res;}private void backtrack(int[] nums,List<Integer> path,boolean[] used,List<List<Integer>> res){if(path.size()==nums.length){res.add(new ArrayList<>(path));return;}for(int i=0;i<nums.length;i++){if(!used[i]){path.add(nums[i]);used[i]=true;backtrack(nums,path,used,res);used[i]=false;path.remove(path.size()-1);}}}}
剑指 Offer 38. 字符串的排列
剑指 Offer 38. 字符串的排列
和上面的全排列非常相似,区别:这道题input中用来排列的元素可能重复
题目中给的abc是没有重复字母的,乍一看和上面一题一模一样,但是,第一次提交后发现,输入是”aab”时,重复的a会导致有重复的排列。需要去重。
去重的方法:
- 排序,把相同的字符都放在一起
在backtrack函数中,if(i>0&&array[i]==array[i-1]&&!used[i-1]) continue; 如果当前元素和前一个元素相同,且前一个元素没有被用到,那么跳过。(如果前一个a没有被用到现在的组合中,而使用了现在这个a,那就和使用了前一个a没用现在这个a的排列重复了)
class Solution {public String[] permutation(String s) {char[] array=s.toCharArray();List<String> resList=new ArrayList<>();StringBuilder sb=new StringBuilder();boolean[] used=new boolean[array.length];Arrays.sort(array);backtrack(sb,resList,array,used);String[] result = resList.toArray(new String[resList.size()]);return result;}private void backtrack(StringBuilder sb,List<String> resList,char[] array,boolean[] used){if(sb.length()==array.length){resList.add(sb.toString());return;}for(int i=0;i<array.length;i++){if(used[i]) continue;if(i>0&&array[i]==array[i-1]&&!used[i-1]) continue;sb.append(array[i]);used[i]=true;backtrack(sb,resList,array,used);used[i]=false;sb.deleteCharAt(sb.length()-1);}}}
131. 分割回文串
题目链接:131. 分割回文串

class Solution {public List<List<String>> partition(String s) {List<List<String>> res=new ArrayList<>();ArrayList<String> sub=new ArrayList<>();dfs(s,0,sub,res);return res;}private void dfs(String s,int start,ArrayList<String> sub,List<List<String>> res){if(start==s.length()){res.add(new ArrayList<>(sub));return;}for(int i=start;i<s.length();i++){String s1=s.substring(start,i+1);if(!isPalindrome(s1)) continue;sub.add(s1);dfs(s,i+1,sub,res);sub.remove(sub.size()-1);}}private boolean isPalindrome(String s){if(s==null||s.length()<2) return true;int l=0,r=s.length()-1;while(l<r){if(s.charAt(l++)!=s.charAt(r--)){return false;}}return true;}}
用dp来记录,进一步提高效率(提高了一点点点)
class Solution {public List<List<String>> partition(String s) {List<List<String>> res=new ArrayList<>();ArrayList<String> sub=new ArrayList<>();int len=s.length();boolean[][] dp=new boolean[len][len];for(int i=0;i<len;i++){isPalindrome(s,i,i,dp);isPalindrome(s,i,i+1,dp);}dfs(s,0,sub,res,dp);return res;}private void dfs(String s,int start,ArrayList<String> sub,List<List<String>> res,boolean[][] dp){if(start==s.length()){res.add(new ArrayList<>(sub));return;}for(int i=start;i<s.length();i++){if(!dp[start][i]) continue;sub.add(s.substring(start,i+1));dfs(s,i+1,sub,res,dp);sub.remove(sub.size()-1);}}private void isPalindrome(String s,int i,int j,boolean[][] dp){int len=s.length();while(i>=0&&j<len&&s.charAt(i)==s.charAt(j)){dp[i][j]=true;i--;j++;}}}
有一道困难题,看起来和这个很类似,但其实不能用相同方法来解决,会超时。
132. 分割回文串 II 困难
132. 分割回文串 II
使用动态规划,dp[i]表示s.substring(0,i)需要最少切割几次class Solution {public int minCut(String s) {if(s == null || s.length() <= 1)return 0;int len = s.length();int dp[] = new int[len];Arrays.fill(dp, len-1);for(int i = 0; i < len; i++){findMinCut(s , i , i , dp); // 奇数回文串以1个字符为中心findMinCut(s, i , i+1 , dp); // 偶数回文串以2个字符为中心}return dp[len-1];}private void findMinCut(String s, int i, int j, int[] dp){int len = s.length();while(i >= 0 && j < len && s.charAt(i) == s.charAt(j)){//以当前字符为中心的最大回文串左侧为i,右侧为j,那s[i]~s[j]长度是不需要切割的//只需要在s[i-1]处切一刀即可,即dp[i-1]+1。所以更新dp[j] = min(dp[j] , dp[i-1]+1)dp[j] = Math.min(dp[j] , (i==0?-1:dp[i-1])+1);i--;j++;}}}
93. 复原 IP 地址
题目:93. 复原 IP 地址
这个答案的效率不太好,但是是我自己想出来的,所以记录一下思路
有几个要点:
(1)当前的数字最短能有多短
比如:输入的数据为“25525511135”,一共是11位,在不知道具体数字的情况下,我们也能知道,最短可以是2位,如果4个数字中有一个是1位,那么就会出现数字多余的情况。
(2)当前的数字最长能有多长
比如:输入的数据为“1111”,一共是11位,在不知道具体数字的情况下,我们也能知道,最长只可以是1位,如果4个数字中有一个是2位,那么就会出现数字不够用的情况。
(3)转换成数字之前的格式校验
当时没注意到这一点,导致漏了很多0
比如”01”这个字符串是不符合要求的,虽然转换成int之后是1。转换之前需要进行校验。
(4)把结果加入resList的方法
一开始path我是用的List
if(nums.size()==4){String str=nums.get(0)+"."+nums.get(1)+"."+nums.get(2)+"."+nums.get(3);resList.add(str);return;}
可以看到优化了很多很多
class Solution {public List<String> restoreIpAddresses(String s) {int len=s.length();if(len>12||len<4) return new ArrayList<>();List<String> resList=new ArrayList<>();List<String> path=new ArrayList<>();dfs(0,path,resList,s);return resList;}private void dfs(int start,List<String> path,List<String> resList,String s){if(path.size()==4){resList.add(String.join(".", path)); //优化了很多!!!return;}int len=minLen(start,path,s);int limit=maxLen(start, path, s);for(;len<=limit&&(start+len)<=s.length();len++){String str=s.substring(start,start+len);if(str.length()>1&&str.charAt(0)=='0') continue;int num=Integer.parseInt(str);if(num>=0&&num<=255){path.add(str);dfs(start+len,path,resList,s);path.remove(path.size()-1);}}}private int minLen(int start,List<String> path,String s){int used=path.size();int remainNumber=s.length()-start;int remainPos=4-used;if((remainNumber/3)==(remainPos-1)){return remainNumber%3==0?1:remainNumber%3;}else if((remainNumber/3)==remainPos){return 3;}else{return 1;}}private int maxLen(int start,List<String> path,String s){int used=path.size();int remainNumber=s.length()-start;int remainPos=4-used;int len=remainNumber-remainPos+1;return Math.min(3,len);}}
大佬的思路:
代码更简洁,但是效率没提升
class Solution {public List<String> restoreIpAddresses(String s) {int len=s.length();if(len>12||len<4) return new ArrayList<>();List<String> resList=new ArrayList<>();List<String> path=new ArrayList<>();dfs(0,path,resList,s);return resList;}private void dfs(int start,List<String> path,List<String> resList,String s){if(path.size()==4){resList.add(String.join(".", path));return;}for(int len=1;len<=3&&(start+len)<=s.length();len++){String str=s.substring(start,start+len);if(str.length()>1&&str.charAt(0)=='0') continue;//不够了或有多余的//int remainNumber=s.length()-start-len;//int remainPos=4-path.size()-1;if((s.length()-start-len)<(3-path.size())||(s.length()-start-len)>(3-path.size())*3) continue;int num=Integer.parseInt(str);if(num>=0&&num<=255){path.add(str);dfs(start+len,path,resList,s);path.remove(path.size()-1);}}}}
剑指 Offer 12. 矩阵中的路径
链接:剑指 Offer 12. 矩阵中的路径
写的有点丑陋
class Solution {int m,n;char[] c;boolean[][] visited;char[][] board;int[][] directions=new int[][]{{0,1},{0,-1},{1,0},{-1,0}};public boolean exist(char[][] board, String word) {this.board=board;m=board.length;n=board[0].length;c=word.toCharArray();visited=new boolean[m][n];for(int i=0;i<m;i++){for(int j=0;j<n;j++){if(board[i][j]==c[0]){visited[i][j]=true;if(backtrack(i,j,1)) return true;visited[i][j]=false;;}}}return false;}private boolean backtrack(int i,int j,int next){if(next==c.length) return true;for(int[] dir:directions){int x=i+dir[0];int y=j+dir[1];if(x>=0&&y>=0&&x<m&&y<n&&c[next]==board[x][y]&&!visited[x][y]){visited[x][y]=true;if(backtrack(x, y, next+1)) return true;visited[x][y]=false;}}return false;}}

