输入:S = “qwe”
输出:[“qwe”, “qew”, “wqe”, “weq”, “ewq”, “eqw”]
示例2:
输入:S = “ab”
输出:[“ab”, “ba”]
提示:
字符都是英文字母。
字符串长度在[1, 9]之间。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/permutation-i-lcci
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
回溯模板题
class Solution {
private boolean[] used;
/**
如果字符串有重复字母,最简单的去重是使用Set,也可以排序后下标剪枝(相对比较难)
重复字母的全排列可去做《全排列》系列题目。这道题好像和全排列I差不多。
接着要做的就是回溯模板啦,选了的跳过,每次选或不选即可爆搜到所有解。
*/
private List res;
private int len;
public String[] permutation(String S) {
len = S.length();
used = new boolean[len];
res = new ArrayList<>();
dfs(S, new StringBuilder(), 0);
return res.toArray(new String[0]);
}
private void dfs(String s, StringBuilder sb, int cnt) {
// end —— 当sb长度与s长度一致时结束,存储答案
if (cnt == len) {
res.add(sb.toString());
return;
}
// 回溯模板
for (int i = 0; i < len; i++) {
if (!used[i]) {
used[i] = true;
sb.append(s.charAt(i));
dfs(s, sb, cnt + 1);
used[i] = false;
sb.deleteCharAt(cnt);
}
}
}
}
Q8.8 有重复字符串的排列组合
有重复字符串的排列组合。编写一种方法,计算某字符串的所有排列组合。
示例1:
输入:S = “qqe”
输出:[“eqq”,“qeq”,“qqe”]
示例2:
输入:S = “ab”
输出:[“ab”, “ba”]
提示:
字符都是英文字母。
字符串长度在[1, 9]之间。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/permutation-ii-lcci
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
这个就是基本的dfs, 不过要注意去重,如果单纯用set则复杂度过高,这里可以直接写规则来过滤,下面简单说一下两种过滤方式,假设数据是aab(这里注意要给字符数组先排序再dfs)
1.arr[i] == arr[i - 1] && book[i - 1]: 该种情况是优先取右,举个简单例子,第一个我们从左到右是a,a,b,这种情况是不可取的,因为当到第二个a时候,第一个a已经用过了,正相反,当我们从第二个a开始的时候,取第一个字符也就是arr[0]=a还没用过,符合条件,故两个a,a,b只会存下来1个。
2.arr[i] == arr[i - 1] && !book[i - 1]: 这个跟第一种过滤方式刚好相反,不过多解释。
还有一种方法可以免去去重步骤,就是可以把字符装桶,然后对桶dfs,只要这个字符还没用完就继续递归下去,有兴趣的可以实现一下。
public String[] permutation(String S) {
List list = new ArrayList<>();
char[] arr = S.toCharArray();
Arrays.sort(arr);
boolean[] book = new boolean[arr.length];
dfs(list, new StringBuilder(), book, arr);
String[] res = new String[list.size()];
for (int i = 0; i < res.length; i++)
res[i] = list.get(i);
return res;
}
public void dfs(List res, StringBuilder sb, boolean[] book, char[] arr) {
if (sb.length() == arr.length) {
res.add(sb.toString());
return;
}
for (int i = 0; i < arr.length; i++) {
if (!book[i]) {
if (i > 0 && arr[i] == arr[i - 1] && !book[i - 1])
continue;
else {
sb.append(arr[i]);
book[i] = true;
dfs(res, sb, book, arr);
book[i] = false;
sb.deleteCharAt(sb.length() - 1);
}
}
}
}
Q8.9 括号
括号。设计一种算法,打印n对括号的所有合法的(例如,开闭一一对应)组合。
说明:解集不能包含重复的子集。
例如,给出 n = 3,生成结果为:
[
“((()))”,
“(()())”,
“(())()”,
“()(())”,
“()()()”
]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/bracket-lcci
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution {
List result = new ArrayList();
public List generateParenthesis(int n) {
getAllResult(0, 0, n, new StringBuilder());
return result;
}
public void getAllResult(int pre, int last, int n, StringBuilder sb){
if(pre == n && last == n){
result.add(sb.toString());
return;
}
if(pre < n){
sb.append(’(’);
getAllResult(pre + 1, last, n, sb);
sb.delete(sb.length() - 1, sb.length());
}
if(last < pre){
sb.append(’)’);
getAllResult(pre, last + 1,
n, sb);
sb.delete(sb.length() - 1, sb.length());
}
}
}
Q8.10 颜色填充
编写函数,实现许多图片编辑软件都支持的「颜色填充」功能。
待填充的图像用二维数组 image 表示,元素为初始颜色值。初始坐标点的横坐标为 sr 纵坐标为 sc。需要填充的新颜色为 newColor 。
「周围区域」是指颜色相同且在上、下、左、右四个方向上存在相连情况的若干元素。
请用新颜色填充初始坐标点的周围区域,并返回填充后的图像。
示例:
输入:
image = [[1,1,1],[1,1,0],[1,0,1]]
sr = 1, sc = 1, newColor = 2
输出:[[2,2,2],[2,2,0],[2,0,1]]
解释:
初始坐标点位于图像的正中间,坐标 (sr,sc)=(1,1) 。
初始坐标点周围区域上所有符合条件的像素点的颜色都被更改成 2 。
注意,右下角的像素没有更改为 2 ,因为它不属于初始坐标点的周围区域。
提示:
image 和 image[0] 的长度均在范围 [1, 50] 内。
初始坐标点 (sr,sc) 满足 0 <= sr < image.length 和 0 <= sc < image[0].length 。
image[i][j] 和 newColor 表示的颜色值在范围 [0, 65535] 内。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/color-fill-lcci
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
r代表row,c代表col,理解为x行x列
class Solution {
public int[][] floodFill(int[][] image, int sr, int sc, int newColor) {
helper(image, sr, sc, image[sr][sc], newColor);
return image;
}
private void helper(int[][] image, int i, int j, int oldColor, int newColor){
if(i < 0 || i >= image.length || j < 0 || j >= image[0].length || image[i][j] != oldColor || image[i][j] == newColor) return;
image[i][j] = newColor;
helper(image, i+1, j, oldColor, newColor);
helper(image, i-1, j, oldColor, newColor);
helper(image, i, j+1, oldColor, newColor);
helper(image, i, j-1, oldColor, newColor);
}
}
Q8.11 硬币
硬币。给定数量不限的硬币,币值为25分、10分、5分和1分,编写代码计算n分有几种表示法。(结果可能会很大,你需要将结果模上1000000007)
示例1:
输入: n = 5
输出:2
解释: 有两种方式可以凑成总金额:
5=5
5=1+1+1+1+1
示例2:
输入: n = 10
输出:4
解释: 有四种方式可以凑成总金额:
10=10
10=5+5
10=5+1+1+1+1+1
10=1+1+1+1+1+1+1+1+1+1
说明:
注意:
你可以假设:
0 <= n (总金额) <= 1000000
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/coin-lcci
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
动态规划,每次小循环只用一种硬币。
若在一次for循环中处理四种情况(一个for里带四个硬币的处理情况),每次计算新一项时,由于每次取的硬币是任意的,会出现对于不同的硬币取法,情况重复的现象。 例如:n=15时,res[15] = 1(全1) + res[15 - 5] + res[15 - 10] = 7,但10 + 5和5 + 10是重复的。
每次小循环只用一种硬币可以避免重复,因为每次小循环中选用的硬币是固定的,在没有到对应硬币的循环前,表内记录对应的解必然不包含该硬币。 例如:n=15时,四次:res[15]=0 -> res[15] = 0 -> res[15] = 2 -> res[15] = 6
实际上coins数组升序也不会影响结果。
class Solution {
private final int mod = 1000000007;
private final int[] coins = {25,10,5,1};
public int waysToChange(int n) {
int[] res = new int[n + 1];
res[0] = 1;
for(int coin : coins){
for(int i = coin;i <= n;i++){
res[i] = (res[i] + res[i - coin]) % mod;
}
}
return res[n];
}
}
Q8.12 八皇后
设计一种算法,打印 N 皇后在 N × N 棋盘上的各种摆法,其中每个皇后都不同行、不同列,也不在对角线上。这里的“对角线”指的是所有的对角线,不只是平分整个棋盘的那两条对角线。
注意:本题相对原题做了扩展
示例:
输入:4
输出:[[".Q…","…Q",“Q…”,"…Q."],["…Q.",“Q…”,"…Q",".Q…"]]
解释: 4 皇后问题存在如下两个不同的解法。
[
[".Q…", // 解法 1
“…Q”,
“Q…”,
“…Q.”],
["…Q.", // 解法 2
“Q…”,
“…Q”,
“.Q…”]
]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/eight-queens-lcci
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
N皇后本质上就是一个枚举问题,和全排列、组合什么的一个道理。枚举问题都可以考虑使用DFS来解决
class Solution {
private List<List> res = new ArrayList<>();
public List<List> solveNQueens(int n) {
char[][] grid = new char[n][n];
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
grid[i][j] = ‘.’;
}
}
boolean[] col = new boolean[n];
boolean[] dg = new boolean[n + n];
boolean[] udg = new boolean[n + n];
dfs(0, n, grid, col, dg, udg);
return res;
}
private void dfs(int h, int n, char[][] grid, boolean[] col, boolean[] dg, boolean[] udg) {
if (h == n) {
List list = new ArrayList<>();
for (int i = 0; i < grid.length; i++) {
list.add(new String(grid[i]));
}
res.add(list);
return;
}
for (int j = 0; j < n; j++) {
if (!col[j] && !dg[n - h + j] && !udg[h + j]) {
grid[h][j] = ‘Q’;
col[j] = dg[n - h + j] = udg[h + j] = true;
dfs(h + 1, n, grid, col, dg, udg);
grid[h][j] = ‘.’;
col[j] = dg[n - h + j] = udg[h + j] = false;
}
}
}
}
Q8.13 堆箱子
堆箱子。给你一堆n个箱子,箱子宽 wi、深 di、高 hi。箱子不能翻转,将箱子堆起来时,下面箱子的宽度、高度和深度必须大于上面的箱子。实现一种方法,搭出最高的一堆箱子。箱堆的高度为每个箱子高度的总和。
输入使用数组[wi, di, hi]表示每个箱子。
示例1:
输入:box = [[1, 1, 1], [2, 2, 2], [3, 3, 3]]
输出:6
示例2:
输入:box = [[1, 1, 1], [2, 3, 4], [2, 6, 7], [3, 4, 5]]
输出:10
提示:
) {
list.add(new String(grid[i]));
}
res.add(list);
return;
}
for (int j = 0; j < n; j++) {
if (!col[j] && !dg[n - h + j] && !udg[h + j]) {
grid[h][j] = ‘Q’;
col[j] = dg[n - h + j] = udg[h + j] = true;
dfs(h + 1, n, grid, col, dg, udg);
grid[h][j] = ‘.’;
col[j] = dg[n - h + j] = udg[h + j] = false;
}
}
}
}
Q8.13 堆箱子
堆箱子。给你一堆n个箱子,箱子宽 wi、深 di、高 hi。箱子不能翻转,将箱子堆起来时,下面箱子的宽度、高度和深度必须大于上面的箱子。实现一种方法,搭出最高的一堆箱子。箱堆的高度为每个箱子高度的总和。
输入使用数组[wi, di, hi]表示每个箱子。
示例1:
输入:box = [[1, 1, 1], [2, 2, 2], [3, 3, 3]]
输出:6
示例2:
输入:box = [[1, 1, 1], [2, 3, 4], [2, 6, 7], [3, 4, 5]]
输出:10
提示: