最近发现自己的回溯算法,图论相关完全是一坨狗屎,遂记录一下
给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。
示例 1:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 class Solution { List<List<Integer>> res = new LinkedList <>(); List<Integer> path = new LinkedList <>(); public List<List<Integer>> permute (int [] nums) { boolean [] used = new boolean [nums.length]; dfs(nums, 0 , used); return res; } private void dfs (int [] nums, int i, boolean [] used) { int n = nums.length; if (i == n) { res.add(new LinkedList <>(path)); return ; } for (int j = 0 ; j < n; ++ j) { if (!used[j]) { used[j] = true ; path.add(nums[j]); dfs(nums, i + 1 , used); path.removeLast(); used[j] = false ; } } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class Solution { List<List<Integer>> res = new LinkedList <>(); List<Integer> path = new LinkedList <>(); public List<List<Integer>> permute (int [] nums) { dfs(nums, 0 ); return res; } private void dfs (int [] nums, int i) { int n = nums.length; if (i == n) { res.add(new LinkedList <>(path)); return ; } for (int j = 0 ; j < n; ++ j) { if (!path.contains(nums[j])) { path.add(nums[j]); dfs(nums, i + 1 ); path.removeLast(); } } } }
给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
示例 1:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class Solution { List<List<Integer>> res = new LinkedList <>(); List<Integer> path = new LinkedList <>(); public List<List<Integer>> subsets (int [] nums) { dfs(nums, 0 ); return res; } private void dfs (int [] nums, int i) { int n = nums.length; if (i == n) { res.add(new LinkedList <>(path)); return ; } dfs(nums, i + 1 ); path.add(nums[i]); dfs(nums, i + 1 ); path.removeLast(); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class Solution { List<List<Integer>> res = new LinkedList <>(); List<Integer> path = new LinkedList <>(); public List<List<Integer>> subsets (int [] nums) { dfs(nums, 0 ); return res; } private void dfs (int [] nums, int i) { int n = nums.length; res.add(new LinkedList <>(path)); if (i == n) { return ; } for (int j = i; j < n; ++ j) { path.add(nums[j]); dfs(nums, j + 1 ); path.removeLast(); } } }
给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
示例 1:
1 2 输入:digits = "23" 输出:["ad" ,"ae" ,"af" ,"bd" ,"be" ,"bf" ,"cd" ,"ce" ,"cf" ]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class Solution { List<String> res = new ArrayList <>(); String [] index = new String [] {"0" , "0" , "abc" , "def" , "ghi" , "jkl" , "mno" , "pqrs" , "tuv" , "wxyz" }; StringBuilder sb = new StringBuilder (); public List<String> letterCombinations (String digits) { int n = digits.length(); if (n == 0 ) return res; dfs(digits, 0 ); return res; } private void dfs (String digits, int i) { int n = digits.length(); if (i == n) { res.add(sb.toString()); return ; } String s = index[digits.charAt(i) - '0' ]; for (char c : s.toCharArray()) { sb.append(c); dfs(digits, i + 1 ); sb.deleteCharAt(i); } } }
给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是 回文串 。返回 s 所有可能的分割方案。
示例 1:
1 2 输入:s = "aab" 输出:[["a" ,"a" ,"b" ],["aa" ,"b" ]]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 class Solution { List<List<String>> res = new LinkedList <>(); LinkedList<String> path = new LinkedList <>(); public List<List<String>> partition (String s) { dfs(s, 0 ); return res; } private void dfs (String s, int i) { int n = s.length(); if (i == n) { res.add(new LinkedList <>(path)); return ; } for (int j = i; j < n; ++ j) { String t = s.substring(i, j + 1 ); if (isH(t)) { path.add(t); dfs(s, j + 1 ); path.removeLast(); } } } private boolean isH (String s) { int l = 0 , r = s.length() - 1 ; while (l < r) { if (s.charAt(l) != s.charAt(r)) { return false ; } l ++; r --; } return true ; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 class Solution { List<List<String>> res = new LinkedList <>(); LinkedList<String> path = new LinkedList <>(); public List<List<String>> partition (String s) { dfs(s, 0 , 0 ); return res; } private void dfs (String s, int i, int start) { int n = s.length(); if (i == n) { res.add(new LinkedList <>(path)); return ; } if (i < n - 1 ) { dfs(s, i + 1 , start); } String t = s.substring(start, i + 1 ); if (isH(t)) { path.add(t); dfs(s, i + 1 , i + 1 ); path.removeLast(); } } private boolean isH (String s) { int l = 0 , r = s.length() - 1 ; while (l < r) { if (s.charAt(l) != s.charAt(r)) { return false ; } l ++; r --; } return true ; } }
有效 IP 地址 正好由四个整数(每个整数位于 0 到 255 之间组成,且不能含有前导 0),整数之间用 '.' 分隔。
例如:"0.1.2.201" 和 "192.168.1.1" 是 有效 IP 地址,但是 "0.011.255.245"、"192.168.1.312" 和 "192.168@1.1" 是 无效 IP 地址。
给定一个只包含数字的字符串 s ,用以表示一个 IP 地址,返回所有可能的有效 IP 地址 ,这些地址可以通过在 s 中插入 '.' 来形成。你 不能 重新排序或删除 s 中的任何数字。你可以按 任何 顺序返回答案。
示例 1:
1 2 输入:s = "25525511135" 输出:["255.255.11.135" ,"255.255.111.35" ]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 class Solution { List<String> res = new ArrayList <>(); List<String> path = new ArrayList <>(); public List<String> restoreIpAddresses (String s) { dfs(0 , s, 0 ); return res; } private void dfs (int i, String s, int cnt) { int n = s.length(); if (i > n) return ; if (i == n && cnt == 4 ) { StringBuilder t = new StringBuilder (); for (String p : path) { t.append(p); t.append("." ); } t.deleteCharAt(t.length() - 1 ); res.add(t.toString()); } for (int j = 1 ; j <= 3 ; ++ j) { if (i + j <= n) { String sub = s.substring(i, i + j); if (validate(sub)) { path.add(sub); dfs(i + j, s, cnt + 1 ); path.remove(path.size() - 1 ); } } } } private boolean validate (String s) { if (s.length() > 1 && s.startsWith("0" )) return false ; if (Integer.parseInt(s) > 255 ) return false ; return true ; } }
给你二叉树的根节点 root 和一个整数目标和 targetSum ,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。
叶子节点 是指没有子节点的节点。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 class Solution { List<List<Integer>> res = new LinkedList <>(); List<Integer> path = new LinkedList <>(); public List<List<Integer>> pathSum (TreeNode root, int targetSum) { dfs(root, targetSum); return res; } private void dfs (TreeNode root, int tar) { if (root == null ) return ; path.add(root.val); tar -= root.val; if (root.left == null && root.right == null && tar == 0 ) { res.add(new LinkedList <>(path)); } dfs(root.left, tar); dfs(root.right, tar); path.removeLast(); } }
给定一个字符串 s ,通过将字符串 s 中的每个字母转变大小写,我们可以获得一个新的字符串。
返回 所有可能得到的字符串集合 。以 任意顺序 返回输出。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class Solution { List<String> ans = new ArrayList <>(); StringBuilder sb = new StringBuilder (); public List<String> letterCasePermutation (String s) { dfs(s, 0 ); return ans; } private void dfs (String s, int i) { if (i == s.length()) { ans.add(sb.toString()); return ; } char c = s.charAt(i); sb.append(c); dfs(s, i + 1 ); sb.deleteCharAt(sb.length() - 1 ); if (Character.isLetter(c)) { c ^= 32 ; sb.append(c); dfs(s, i + 1 ); sb.deleteCharAt(sb.length() - 1 ); } } }
原题去原链接看吧
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 class Solution { List<List<String>> ans; boolean [] used; boolean [] g1; boolean [] g2; int [] col; public List<List<String>> solveNQueens (int n) { ans = new LinkedList <>(); used = new boolean [n]; g1 = new boolean [2 * n - 1 ]; g2 = new boolean [2 * n - 1 ]; col = new int [n]; dfs(0 , n); return ans; } private void dfs (int r, int n) { if (r == n) { List<String> path = new LinkedList <>(); for (int i : col) { char [] c = new char [n]; Arrays.fill(c, '.' ); c[i] = 'Q' ; path.add(new String (c)); } ans.add(path); return ; } for (int j = 0 ; j < n; ++ j) { int t = r - j + n - 1 ; if (!used[j] && !g1[r + j] && !g2[t]) { col[r] = j; used[j] = g1[r + j] = g2[t] = true ; dfs(r + 1 , n); used[j] = g1[r + j] = g2[t] = false ; } } } }
给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。
你可以按 任何顺序 返回答案。
示例 1:
1 2 3 4 5 6 7 8 9 10 输入:n = 4, k = 2 输出:
正着遍历
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class Solution { List<List<Integer>> res = new LinkedList <>(); LinkedList<Integer> path = new LinkedList <Integer>(); int n; public List<List<Integer>> combine (int n, int k) { this .n = n; dfs(1 , k); return res; } public void dfs (int i, int k) { int d = k - path.size(); if (d == 0 ) { res.add(new LinkedList <>(path)); return ; } for (int j = i; j <= n; j ++) { path.add(j); dfs(j + 1 , k); path.removeLast(); } } }
倒着
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Solution { List<List<Integer>> res = new LinkedList <>(); LinkedList<Integer> path = new LinkedList <Integer>(); public List<List<Integer>> combine (int n, int k) { dfs(n, k); return res; } public void dfs (int i, int k) { int d = k - path.size(); if (d == 0 ) { res.add(new LinkedList <>(path)); return ; } for (int j = i; j >= d; j --) { path.add(j); dfs(j - 1 , k); path.removeLast(); } } }
选或者不选
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class Solution { List<List<Integer>> res = new LinkedList <>(); LinkedList<Integer> path = new LinkedList <Integer>(); public List<List<Integer>> combine (int n, int k) { dfs(n, k); return res; } public void dfs (int i, int k) { int d = k - path.size(); if (d == 0 ) { res.add(new LinkedList <>(path)); return ; } if (i > d) { dfs(i - 1 , k); } path.add(i); dfs(i - 1 , k); path.removeLast(); } }
给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。
candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 class Solution { List<List<Integer>> ans = new LinkedList <>(); List<Integer> path = new LinkedList <>(); int target; public List<List<Integer>> combinationSum (int [] candidates, int target) { this .target = target; dfs(0 , candidates, 0 ); return ans; } private void dfs (int i, int [] candidates, int sum) { int n = candidates.length; if (sum == target) { ans.add(new LinkedList <>(path)); return ; } for (int j = i; j < n; ++ j) { if (sum + candidates[j] <= target) { sum += candidates[j]; path.add(candidates[j]); dfs(j, candidates, sum); sum -= candidates[j]; path.removeLast(); } } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 class Solution { List<List<Integer>> ans = new LinkedList <>(); List<Integer> path = new LinkedList <>(); int target; public List<List<Integer>> combinationSum (int [] candidates, int target) { this .target = target; dfs(0 , candidates, 0 ); return ans; } private void dfs (int i, int [] candidates, int sum) { int n = candidates.length; if (i == n) return ; if (sum == target) { ans.add(new LinkedList <>(path)); return ; } dfs(i + 1 , candidates, sum); if (sum + candidates[i] <= target) { sum += candidates[i]; path.add(candidates[i]); dfs(i, candidates, sum); sum -= candidates[i]; path.removeLast(); } } }
给定一个候选人编号的集合 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。candidates 中的每个数字在每个组合中只能使用 一次 。
注意: 解集不能包含重复的组合。
示例 1:
1 2 3 4 5 6 7 8 输入: candidates = , target = 8, 输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class Solution { List<List<Integer>> res = new LinkedList <>(); LinkedList<Integer> path = new LinkedList <>(); public List<List<Integer>> combinationSum2 (int [] candidates, int target) { Arrays.sort(candidates); dfs(candidates, target, 0 ); return res; } private void dfs (int [] candidates, int t, int i) { if (t == 0 ) { res.add(new LinkedList <>(path)); return ; } for (int j = i; j < candidates.length; ++ j) { if (j > i && candidates[j] == candidates[j - 1 ]) continue ; if (t >= candidates[j]) { path.add(candidates[j]); dfs(candidates, t - candidates[j], j + 1 ); path.removeLast(); } } } }
找出所有相加之和为 n 的 k 个数的组合,且满足下列条件:
返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次,组合可以以任何顺序返回。
枚举选哪个
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class Solution { List<List<Integer>> res = new LinkedList <>(); LinkedList<Integer> path = new LinkedList <>(); public List<List<Integer>> combinationSum3 (int k, int n) { dfs(9 , k, n); return res; } public void dfs (int i, int k, int t) { int d = k - path.size(); if (t < 0 || t > (2 * i - d + 1 ) * d / 2 ) { return ; } if (d == 0 ) { res.add(new LinkedList <>(path)); return ; } for (int j = i; j >= d; j --) { path.add(j); dfs(j - 1 , k, t - j); path.removeLast(); } } }
选或不选
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class Solution { List<List<Integer>> res = new LinkedList <>(); LinkedList<Integer> path = new LinkedList <>(); public List<List<Integer>> combinationSum3 (int k, int n) { dfs(9 , k, n); return res; } public void dfs (int i, int k, int t) { int d = k - path.size(); if (t < 0 || t > (2 * i - d + 1 ) * d / 2 ) { return ; } if (d == 0 ) { res.add(new LinkedList <>(path)); return ; } if (i > d) { dfs(i - 1 , k, t); } path.add(i); dfs(i - 1 , k, t - i); path.removeLast(); } }
数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
示例 1:
1 2 输入:n = 3 输出:["((()))" ,"(()())" ,"(())()" ,"()(())" ,"()()()" ]
枚举第i个位置选哪个
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class Solution { List<String> ans = new LinkedList <>(); StringBuilder sb = new StringBuilder (); int n; public List<String> generateParenthesis (int n) { this .n = n; dfs(0 , 0 ); return ans; } private void dfs (int i, int left) { if (i == n * 2 ) { ans.add(sb.toString()); return ; } if (left < n) { sb.append('(' ); dfs(i + 1 , left + 1 ); sb.deleteCharAt(sb.length() - 1 ); } if (i - left < left) { sb.append(')' ); dfs(i + 1 , left); sb.deleteCharAt(sb.length() - 1 ); } } }
判断图中是否有环 给你一个课程总数n,同时给你一个二维数组[[a,b], [c,d]]表示想上课程a就必须先上课程b,想上课程c就必须先上课程d,问你能不能上完所有的课
比如[[0,1], [2,0], [1,2]],表示想上课程1就必须先上课程0,想上课程0就必须先上课程2,想上课程2就必须先上课程0,这就出现了循环依赖,也就是图中有环,死锁,返回false
DFS,为图建立邻接表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 public class Solution { public boolean canFinish (int numCourses, int [][] prerequisites) { List<List<Integer>> graph = new ArrayList <>(); for (int i = 0 ; i < numCourses; i++) { graph.add(new ArrayList <>()); } for (int [] pre : prerequisites) { graph.get(pre[0 ]).add(pre[1 ]); } int [] status = new int [numCourses]; for (int i = 0 ; i < numCourses; i++) { if (hasCycle(graph, status, i)) { return false ; } } return true ; } private boolean hasCycle (List<List<Integer>> graph, int [] status, int course) { if (status[course] == 1 ) { return true ; } if (status[course] == 2 ) { return false ; } status[course] = 1 ; for (int nextCourse : graph.get(course)) { if (hasCycle(graph, status, nextCourse)) { return true ; } } status[course] = 2 ; return false ; } }
拓扑排序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 public class Solution { public static void main (String[] args) { int [][] prerequisites = new int [][]{{0 , 1 }, {1 , 2 }, {2 , 0 }}; System.out.println(canFinish(3 , prerequisites)); } public static boolean canFinish (int numCourses, int [][] prerequisites) { List<List<Integer>> adjList = new ArrayList <>(); for (int i = 0 ; i < numCourses; i++) { adjList.add(new ArrayList <>()); } int [] inDegree = new int [numCourses]; for (int [] prereq : prerequisites) { int course = prereq[0 ]; int prereqCourse = prereq[1 ]; adjList.get(prereqCourse).add(course); inDegree[course]++; } Queue<Integer> queue = new LinkedList <>(); for (int i = 0 ; i < numCourses; i++) { if (inDegree[i] == 0 ) { queue.offer(i); } } int processedCourses = 0 ; while (!queue.isEmpty()) { int currCourse = queue.poll(); processedCourses++; for (int nextCourse : adjList.get(currCourse)) { inDegree[nextCourse]--; if (inDegree[nextCourse] == 0 ) { queue.offer(nextCourse); } } } return processedCourses == numCourses; } }