算法:【深度搜索(回溯)】
一、数字字符串转化成IP地址
现在有一个只包含数字的字符串,将该字符串转化成IP地址的形式,返回所有可能的情况。
例如:
给出的字符串为"25525522135",
返回["255.255.22.135", "255.255.221.35"]. (顺序没有关系)输入:
"25525522135"
返回值:
["255.255.22.135","255.255.221.35"]
import java.util.*; public class Solution { ArrayList<String> res = new ArrayList<String>(); public ArrayList<String> restoreIpAddresses (String s) { // 深搜 ArrayList<String> ip = new ArrayList<String>();//存放中间结果 dfs(s, ip, 0); return res; } public void dfs(String s, ArrayList<String> ip, int start){ if(ip.size() == 4 && start == s.length()){ // 找到一个合法解 res.add(ip.get(0) + '.' + ip.get(1) + '.' + ip.get(2) + '.' + ip.get(3)); return; } if(s.length() - start > 3*(4-ip.size())) return; if(s.length() - start < (4-ip.size())) return; int num = 0; for(int i=start; i<start + 3 && i<s.length(); i++){ num = num*10 + (s.charAt(i) - '0'); if(num < 0 || num > 255) continue; ip.add(s.substring(start, i+1)); dfs(s, ip, i+1); ip.remove(ip.size()-1); if(num == 0) //不允许前缀0 break; } } }
二、字符串的排列
输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则按字典序打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。
输入:
"ab"
返回值:
["ab","ba"]
import java.util.ArrayList; import java.util.*; public class Solution { ArrayList<String> res = new ArrayList<String>(); public ArrayList<String> Permutation(String str) { if(str.length() == 0) return res; char[] ch = str.toCharArray(); Arrays.sort(ch); process(ch, new boolean[ch.length], new StringBuffer()); return res; } public void process(char[] ch, boolean[] maked, StringBuffer str){ if(str.length() == ch.length){ res.add(str.toString()); return; } for(int i=0; i<ch.length; i++){ if(maked[i]) continue; if(i != 0 && ch[i] == ch[i-1] && !maked[i-1]){ continue; } maked[i] = true; str.append(ch[i]); process(ch, maked, str); maked[i] = false; str.deleteCharAt(str.length() -1); } } }
class Solution { public: //分字符串长度奇偶性讨论,模拟两个滑动窗口,一开始的最大长度是字符串的一半,从左往右滑动比较 //右端窗口滑到字符串末尾后,两个窗口长度同时减一,并从字符串左端从头开始扫描 //只要扫描过程中第一次匹配到两个窗口内的字符串相同,那么答案就是2*窗口长度 int solve(string a) { if(a.size()%2==0){//字符串长度是偶数 int l1(0),r1(a.size()/2-1); int l2(a.size()/2),r2(a.size()-1); while(l1<=r1&&l2<=r2&&a.substr(l1,r1-l1+1)!=a.substr(l2,r2-l2+1)){ int wlen = r1-l1;//窗口缩小1格 l1=0,r1=l1+wlen-1,l2=r1+1,r2=l2+wlen-1; while(r2<a.size()&&a.substr(l1,r1-l1+1)!=a.substr(l2,r2-l2+1)){ l1++,r1++; l2++,r2++; } if(a.substr(l1,r1-l1+1)==a.substr(l2,r2-l2+1)) return 2*(r1-l1+1); } return 2*(r1-l1+1); } else{//字符串长度是奇数 int l1(0),r1(a.size()/2-1); int l2(a.size()/2),r2(a.size()-2); while(l1<=r1&&l2<=r2&&a.substr(l1,r1-l1+1)!=a.substr(l2,r2-l2+1)){ while(r2<a.size()&&a.substr(l1,r1-l1+1)!=a.substr(l2,r2-l2+1)){ l1++,r1++; l2++,r2++; } if(a.substr(l1,r1-l1+1)==a.substr(l2,r2-l2+1)) return 2*(r1-l1+1); int wlen = r1-l1;//窗口缩小1格 l1=0,r1=l1+wlen-1,l2=r1+1,r2=l2+wlen-1; } return 2*(r1-l1+1); } return 0; } };
三、1. 组合总和
给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的数字可以无限制重复被选取。输入:candidates = [2,3,6,7], target = 7,
所求解集为:
[[7],[2,2,3]]
class Solution { List<List<Integer>> res = new LinkedList<List<Integer>>(); public List<List<Integer>> combinationSum(int[] candidates, int target) { if(candidates == null || candidates.length == 0) return res; List<Integer> tmp = new LinkedList<Integer>(); Arrays.sort(candidates); backtracking(candidates, tmp, 0, target); return res; } // start 是控制的关键 public void backtracking(int[] candidates, List<Integer> tmp, int start, int target){ if(target == 0 && !res.contains(tmp)){ res.add(new LinkedList<Integer>(tmp)); return; } for(int i=start; i<candidates.length; i++){ // 减枝,数组已经从小到大排序好了,如果当前的数已经大于target,后面的数必定大于target。 if(candidates[i] > target) break; tmp.add(candidates[i]); backtracking(candidates, tmp, i, target-candidates[i]); tmp.remove(tmp.size()-1); } } }
三、2. 组合总和 II
给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的每个数字在每个组合中只能使用一次。
- 所有数字(包括目标数)都是正整数。
- 解集不能包含重复的组合。
输入: candidates = [10,1,2,7,6,1,5], target = 8,
所求解集为:
[
[1, 7],
[1, 2, 5],
[2, 6],
[1, 1, 6]
]
class Solution { // 回溯法 List<List<Integer>> res = new LinkedList<>(); public List<List<Integer>> combinationSum2(int[] candidates, int target) { if(candidates == null || candidates.length == 0) return res; Arrays.sort(candidates); List<Integer> tmp = new LinkedList<>(); backtracking(candidates, 0, tmp, target); return res; } public void backtracking(int[] candidates, int start, List<Integer> tmp, int target){ // 减枝,数组已经从小到大排序好了,如果当前的数已经大于target,后面的数必定大于target。 if(target == 0 && !res.contains(tmp)){ res.add(new LinkedList(tmp)); return; } for(int i=start; i<candidates.length; i++){ if(target - candidates[i] < 0) break; tmp.add(candidates[i]); backtracking(candidates, i+1, tmp, target-candidates[i]); tmp.remove(tmp.size()-1); } } }
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/combination-sum-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
四、岛屿的最大面积
给定一个包含了一些 0 和 1 的非空二维数组 grid 。
一个 岛屿 是由一些相邻的 1 (代表土地) 构成的组合,这里的「相邻」要求两个 1 必须在水平或者竖直方向上相邻。你可以假设 grid 的四个边缘都被 0(代表水)包围着。
找到给定的二维数组中最大的岛屿面积。(如果没有岛屿,则返回面积为 0 。)示例 1:
[[0,0,1,0,0,0,0,1,0,0,0,0,0],
[0,0,0,0,0,0,0,1,1,1,0,0,0],
[0,1,1,0,1,0,0,0,0,0,0,0,0],
[0,1,0,0,1,1,0,0,1,0,1,0,0],
[0,1,0,0,1,1,0,0,1,1,1,0,0],
[0,0,0,0,0,0,0,0,0,0,1,0,0],
[0,0,0,0,0,0,0,1,1,1,0,0,0],
[0,0,0,0,0,0,0,1,1,0,0,0,0]]
对于上面这个给定矩阵应返回 6。注意答案不应该是 11 ,因为岛屿只能包含水平或垂直的四个方向的 1 。
class Solution { public int maxAreaOfIsland(int[][] grid) { int res = 0; // 遍历整个数组 for(int i=0; i<grid.length; i++) for(int j=0; j<grid[0].length; j++){ res = Math.max(res, process(grid, i, j)); } return res; } public int process(int[][] grid, int curi, int curj){ if(curi<0 || curj<0 || curi>=grid.length || curj>=grid[0].length || grid[curi][curj] != 1) return 0; // 置为0,防止重复遍历 grid[curi][curj] = 0; // 上下左右变换 int[] di = {0,0,1,-1}; int[] dj = {1,-1,0,0}; int ans = 1; // 深度遍历 for(int i=0; i<4; i++){ ans += process(grid, curi+di[i], curj+dj[i]); } return ans; } }