面试题 08.01. 三步问题
三步问题。有个小孩正在上楼梯,楼梯有n阶台阶,小孩一次可以上1阶、2阶或3阶。实现一种方法,计算小孩有多少种上楼梯的方式。结果可能很大,你需要对结果模1000000007。
示例1:
输入:n = 3
输出:4
说明: 有四种走法
示例2:
输入:n = 5
输出:13
提示:
n范围在[1, 1000000]之间
public int waysToStep(int n) {
int MOD = 1000000007;
long[] ans = new long[n + 3];
ans[2] = 1;
for (int i = 3; i < n + 3; i++) {
ans[i] = (ans[i - 3] + ans[i - 2] + ans[i - 1]) % MOD;
}
return (int) ans[n + 2];
}
面试题 08.02. 迷路的机器人
设想有个机器人坐在一个网格的左上角,网格 r 行 c 列。机器人只能向下或向右移动,但不能走到一些被禁止的网格(有障碍物)。设计一种算法,寻找机器人从左上角移动到右下角的路径。
网格中的障碍物和空位置分别用 1 和 0 来表示。
返回一条可行的路径,路径由经过的网格的行号和列号组成。左上角为 0 行 0 列。
示例 1:
输入:
[
[0,0,0],
[0,1,0],
[0,0,0]
]
输出: [[0,0],[0,1],[0,2],[1,2],[2,2]]
解释:
输入中标粗的位置即为输出表示的路径,即
0行0列(左上角) -> 0行1列 -> 0行2列 -> 1行2列 -> 2行2列(右下角)
说明:r 和 c 的值均不超过 100。
public List> pathWithObstacles(int[][] obstacleGrid) {
List> ans = new ArrayList<>();
int rNum = obstacleGrid.length;
int cNum = obstacleGrid[0].length;
boolean[][] check = new boolean[rNum][cNum];
if (obstacleGrid[rNum - 1][cNum - 1] == 1) {
return ans;
}
check[rNum - 1][cNum - 1] = true;
for (int i = rNum - 2; i >= 0; i--) {
if (check[i + 1][cNum - 1] && obstacleGrid[i][cNum - 1] == 0) {
check[i][cNum - 1] = true;
}
}
for (int i = cNum - 2; i >= 0; i--) {
if (check[rNum - 1][i + 1] && obstacleGrid[rNum - 1][i] == 0) {
check[rNum - 1][i] = true;
}
}
for (int i = rNum - 2; i >= 0; i--) {
for (int j = cNum - 2; j >= 0; j--) {
if (obstacleGrid[i][j] == 1) {
continue;
}
if (check[i][j + 1] || check[i + 1][j]) {
check[i][j] = true;
}
}
}
if (!check[0][0]) {
return ans;
}
int x = 0;
int y = 0;
while (x < rNum && y < cNum) {
ans.add(getList(x, y));
if (x < rNum - 1 && check[x + 1][y]) {
x++;
continue;
}
if (y < cNum - 1 && check[x][y + 1]) {
y++;
continue;
}
if (x == rNum - 1 && y == cNum - 1) {
return ans;
}
}
return ans;
}
private List getList(int x, int y) {
List list = new ArrayList<>(2);
list.add(x);
list.add(y);
return list;
}
面试题 08.03. 魔术索引
魔术索引。 在数组A[0…n-1]中,有所谓的魔术索引,满足条件A[i] = i。给定一个有序整数数组,编写一种方法找出魔术索引,若有的话,在数组A中找出一个魔术索引,如果没有,则返回-1。若有多个魔术索引,返回索引值最小的一个。
示例1:
输入:nums = [0, 2, 3, 4, 5]
输出:0
说明: 0下标的元素为0
示例2:
输入:nums = [1, 1, 1]
输出:1
提示:
nums长度在[1, 1000000]之间
public int findMagicIndex(int[] nums) {
int len = nums.length;
int sta = 0;
for (int i = 0; i < len; i++) {
if (nums[i] < i) {
continue;
}
if (nums[i] == i) {
return i;
}
sta = nums[i];
}
for (int i = sta; i < len; i = nums[i]) {
if (nums[i] == i) {
return i;
}
}
return -1;
}
面试题 08.04. 幂集
幂集。编写一种方法,返回某集合的所有子集。集合中不包含重复的元素。
说明:解集不能包含重复的子集。
示例:
输入: nums = [1,2,3]
输出:
[
[3],
[1],
[2],
[1,2,3],
[1,3],
[2,3],
[1,2],
[]
]
public List> subsets(int[] nums) {
List> ans = new ArrayList<>();
List res = new ArrayList<>();
addValue(ans, res, nums, nums.length, 0);
return ans;
}
private void addValue(List> ans, List res, int[] nums, int length, int k) {
if (length == k) {
ans.add(res);
return;
}
List res2 = new ArrayList<>(res);
addValue(ans, res, nums, length, k + 1);
res2.add(nums[k]);
addValue(ans, res2, nums, length, k + 1);
}著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
面试题 08.05. 递归乘法
递归乘法。 写一个递归函数,不使用 * 运算符, 实现两个正整数的相乘。可以使用加号、减号、位移,但要吝啬一些。
示例1:
输入:A = 1, B = 10
输出:10
示例2:
输入:A = 3, B = 4
输出:12
提示:
保证乘法范围不会溢出
public int multiply(int A, int B) {
if (A > B) {
return getAns(B, A, 0);
}
return getAns(A, B, 0);
}
private int getAns(int a, int b, int ans) {
if (a == 0) {
return ans;
}
if ((a & 1) == 1) {
ans += b;
}
return getAns(a >> 1, b << 1, ans);
}
面试题 08.06. 汉诺塔问题
在经典汉诺塔问题中,有 3 根柱子及 N 个不同大小的穿孔圆盘,盘子可以滑入任意一根柱子。一开始,所有盘子自上而下按升序依次套在第一根柱子上(即每一个盘子只能放在更大的盘子上面)。移动圆盘时受到以下限制:
(1) 每次只能移动一个盘子;
(2) 盘子只能从柱子顶端滑出移到下一根柱子;
(3) 盘子只能叠在比它大的盘子上。
请编写程序,用栈将所有盘子从第一根柱子移到最后一根柱子。
你需要原地修改栈。
示例1:
输入:A = [2, 1, 0], B = [], C = []
输出:C = [2, 1, 0]
示例2:
输入:A = [1, 0], B = [], C = []
输出:C = [1, 0]
提示:
A中盘子的数目不大于14个。
public void hanota(List A, List B, List C) {
move(A, B, C, A.size());
}
private void move(List from, List other, List to, int len) {
if (len == 1) {
to.add(from.remove(from.size() - 1));
return;
}
move(from, to, other, len - 1);
to.add(from.remove(from.size() - 1));
move(other, from, to, len - 1);
}
面试题 08.07. 无重复字符串的排列组合
无重复字符串的排列组合。编写一种方法,计算某字符串的所有排列组合,字符串每个字符均不相同。
示例1:
输入:S = “qwe”
输出:[“qwe”, “qew”, “wqe”, “weq”, “ewq”, “eqw”]
示例2:
输入:S = “ab”
输出:[“ab”, “ba”]
提示:
字符都是英文字母。
字符串长度在[1, 9]之间。
我的原来的答案:
int k = 0;
public String[] permutation(String S) {
Set characters = new HashSet<>();
char[] chars = S.toCharArray();
for (int i = 0; i < chars.length; i++) {
characters.add(chars[i]);
}
String[] ans = new String[A(S.length())];
char[] anss = new char[S.length()];
addAns(ans, characters, anss, characters.size(), characters.size());
return ans;
}
private int A(int k) {
if (k == 2) {
return k;
}
return k * A(k - 1);
}
private void addAns(String[] ans, Set characters, char[] anss, int size, int len) {
if (size == 0) {
ans[k++] = new String(anss);
return;
}
List list = new ArrayList(characters);
for (Character c : list) {
anss[len - size] = c;
characters.remove(c);
addAns(ans, characters, anss, size - 1, len);
characters.add(c);
}
}
看了最佳答案后,新的答案:
int k = 0;
public String[] permutation(String S) {
char[] chars = S.toCharArray();
int len = S.length();
String[] ans = new String[A(S.length())];
doAns(ans, chars, 0, len);
return ans;
}
private void doAns(String[] ans, char[] chars, int s, int len) {
if (s == len - 1) {
ans[k++] = new String(chars);
return;
}
doAns(ans, chars, s + 1, len);
for (int i = s + 1; i < len; i++) {
change(chars, s, i);
doAns(ans, chars, s + 1, len);
change(chars, s, i);
}
}
private void change(char[] chars, int y, int x) {
char c = chars[y];
chars[y] = chars[x];
chars[x] = c;
}
private int A(int k) {
if (k == 2) {
return k;
}
return k * A(k - 1);
}
面试题 08.08. 有重复字符串的排列组合
有重复字符串的排列组合。编写一种方法,计算某字符串的所有排列组合。
示例1:
输入:S = “qqe”
输出:[“eqq”,“qeq”,“qqe”]
示例2:
输入:S = “ab”
输出:[“ab”, “ba”]
提示:
字符都是英文字母。
字符串长度在[1, 9]之间。
int k = 0;
public String[] permutation(String S) {
char[] chars = S.toCharArray();
Arrays.sort(chars);
int len = S.length();
String[] ans = new String[getLen(chars)];
doAns(ans, chars, 0, len);
return ans;
}
private int getLen(char[] chars) {
int[] nums = new int[128];
for (int i = 0; i < chars.length; i++) {
nums[chars[i]]++;
}
int ans = A(chars.length);
for (int i = 0; i < 128; i++) {
if (nums[i] > 1) {
ans /= A(nums[i]);
}
}
return ans;
}
private int A(int k) {
if (k == 1) {
return k;
}
return k * A(k - 1);
}
private void doAns(String[] ans, char[] chars, int s, int len) {
if (s == len - 1) {
ans[k++] = new String(chars);
return;
}
doAns(ans, chars, s + 1, len);
for (int i = s + 1; i < len; i++) {
if (chars[i] == chars[s] || chars[i] == chars[i - 1]) {
continue;
}
change(chars, s, i);
doAns(ans, chars, s + 1, len);
change(chars, s, i);
}
}
private void change(char[] chars, int y, int x) {
char c = chars[y];
chars[y] = chars[x];
chars[x] = c;
}
面试题 08.09. 括号
括号。设计一种算法,打印n对括号的所有合法的(例如,开闭一一对应)组合。
说明:解集不能包含重复的子集。
例如,给出 n = 3,生成结果为:
[
“((()))”,
“(()())”,
“(())()”,
“()(())”,
“()()()”
]
public List generateParenthesis(int n) {
char[] chars = new char[n * 2];
List ans = new ArrayList<>();
addStr(ans, chars, 0, 0, n);
return ans;
}
private void addStr(List ans, char[] chars, int left, int k, int n) {
if (k == 2 * n) {
ans.add(new String(chars));
return;
}
if (left < n) {
chars[k] = '(';
addStr(ans, chars, left + 1, k + 1, n);
}
if (k - left < left) {
chars[k] = ')';
addStr(ans, chars, left, k + 1, n);
}
}
面试题 08.10. 颜色填充
颜色填充。编写函数,实现许多图片编辑软件都支持的“颜色填充”功能。给定一个屏幕(以二维数组表示,元素为颜色值)、一个点和一个新的颜色值,将新颜色值填入这个点的周围区域,直到原来的颜色值全都改变。
示例1:
输入:
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] 内。
给出的初始点将满足 0 <= sr < image.length 和 0 <= sc < image[0].length。
image[i][j] 和 newColor 表示的颜色值在范围 [0, 65535]内。
public int[][] floodFill(int[][] image, int sr, int sc, int newColor) {
if (image[sr][sc] == newColor) {
return image;
}
color(image, sr, sc, image[sr][sc], newColor);
return image;
}
private void color(int[][] image, int sr, int sc, int oldColor, int newColor) {
if (sr < 0 || sr >= image.length || sc < 0 || sc >= image[0].length) {
return;
}
if (image[sr][sc] != oldColor) {
return;
}
image[sr][sc] = newColor;
color(image, sr - 1, sc, oldColor, newColor);
color(image, sr + 1, sc, oldColor, newColor);
color(image, sr, sc - 1, oldColor, newColor);
color(image, sr, sc + 1, oldColor, newColor);
}
面试题 08.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
public int waysToChange(int n) {
int MOD = 1000000007;
int[] ans5 = new int[n + 1];
for (int i = 0; i <= n; i++) {
ans5[i] = i / 5 + 1;
}
if (n < 10) {
return ans5[n];
}
int[] ans10 = new int[n + 1];
for (int i = 0; i < 10; i++) {
ans10[i] = ans5[i];
}
for (int i = 10; i <= n; i++) {
ans10[i] = (ans10[i - 10] + ans5[i]) % MOD;
}
if (n < 25) {
return ans10[n];
}
int[] ans25 = new int[n + 1];
for (int i = 0; i < 25; i++) {
ans25[i] = ans10[i];
}
for (int i = 25; i <= n; i++) {
ans25[i] = (ans25[i - 25] + ans10[i]) % MOD;
}
return ans25[n];
}
面试题 08.12. 八皇后
设计一种算法,打印 N 皇后在 N × N 棋盘上的各种摆法,其中每个皇后都不同行、不同列,也不在对角线上。这里的“对角线”指的是所有的对角线,不只是平分整个棋盘的那两条对角线。
注意:本题相对原题做了扩展
示例:
输入:4
输出:[[".Q…","…Q",“Q…”,"…Q."],["…Q.",“Q…”,"…Q",".Q…"]]
解释: 4 皇后问题存在如下两个不同的解法。
[
[".Q…", // 解法 1
“…Q”,
“Q…”,
“…Q.”],
["…Q.", // 解法 2
“Q…”,
“…Q”,
“.Q…”]
]
public List> solveNQueens(int n) {
List> ans = new ArrayList<>();
boolean[] cha = new boolean[n * 2];
boolean[] he = new boolean[n * 2];
boolean[] zon = new boolean[n];
char[][] charList = new char[n][n];
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
charList[i][j] = '.';
}
}
check(n, 0, cha, he, zon, ans, charList);
return ans;
}
private void check(int n, int k, boolean[] cha, boolean[] he, boolean[] zon, List> ans, char[][] charList) {
if (n == k) {
List r = new ArrayList<>();
for (int i = 0; i < n; i++) {
r.add(new String(charList[i]));
}
ans.add(r);
return;
}
char[] res = charList[k];
for (int i = 0; i < n; i++) {
if (zon[i] || he[i + k] || cha[k - i + n]) {
continue;
}
zon[i] = true;
he[i + k] = true;
cha[k - i + n] = true;
res[i] = 'Q';
check(n, k + 1, cha, he, zon, ans, charList);
res[i] = '.';
zon[i] = false;
he[i + k] = false;
cha[k - i + n] = false;
}
}
面试题 08.13. 堆箱子
堆箱子。给你一堆n个箱子,箱子宽 wi、高hi、深di。箱子不能翻转,将箱子堆起来时,下面箱子的宽度、高度和深度必须大于上面的箱子。实现一种方法,搭出最高的一堆箱子。箱堆的高度为每个箱子高度的总和。
输入使用数组[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
提示:
箱子的数目不大于3000个。
public int pileBox(int[][] box) {
if (box == null) {
return 0;
}
int len = box.length;
if (len == 0) {
return 0;
}
if (len == 1) {
return box[0][2];
}
//排序
Arrays.sort(box, new Comparator() {
@Override
public int compare(int[] o1, int[] o2) {
if (o1[2] != o2[2]) {
return o1[2] - o2[2];
}
if (o1[1] != o2[1]) {
return o1[1] - o2[1];
}
return o1[0] - o2[0];
}
});
//保存答案
int[] ans = new int[len + 1];
//保存截止最大值
int[] max = new int[len + 1];
for (int i = 0; i < len; i++) {
int value = box[i][2];
ll:
for (int j = i - 1; j >= 0; j--) {
//如果截止没有更大,就直接结束
if (value >= box[i][2] + max[j + 1]) {
break;
}
for (int k = 0; k < 3; k++) {
//不符合的直接跳过
if (box[i][k] <= box[j][k]) {
continue ll;
}
}
value = box[i][2] + ans[j];
}
ans[i] = value;
max[i + 1] = Math.max(value, max[i]);
}
return max[len];
}
面试题 08.14. 布尔运算
给定一个布尔表达式和一个期望的布尔结果 result,布尔表达式由 0 (false)、1 (true)、& (AND)、 | (OR) 和 ^ (XOR) 符号组成。实现一个函数,算出有几种可使该表达式得出 result 值的括号方法。
示例 1:
输入: s = “1^0|0|1”, result = 0
输出: 2
解释: 两种可能的括号方法是
1^(0|(0|1))
1^((0|0)|1)
示例 2:
输入: s = “0&0&0&1^1|0”, result = 1
输出: 10
提示:
运算符的数量不超过 19 个
//最优规划的做法
public int countEval(String s, int result) {
if (result < 0 || result > 1) {
return 0;
}
if (s == null || s.length() == 0) {
return 0;
}
char[] chars = s.toCharArray();
int len = chars.length;
int[][][] ans = new int[s.length()][s.length()][2];
for (int i = 0; i < len; i += 2) {
ans[i][i][chars[i] - '0'] = 1;
}
//表示计算的长度
for (int i = 2; i < len; i += 2) {
//j表示计算的起点
for (int j = 0; j < len - i; j += 2) {
//k表示在哪分割计算
for (int k = j + 1; k < j + i; k += 2) {
if (chars[k] == '&') {
ans[j][j + i][1] += ans[j][k - 1][1] * ans[k + 1][i + j][1];
ans[j][j + i][0] += ans[j][k - 1][0] * ans[k + 1][i + j][1] +
ans[j][k - 1][1] * ans[k + 1][i + j][0] +
ans[j][k - 1][0] * ans[k + 1][i + j][0];
} else if (chars[k] == '|') {
ans[j][j + i][1] += ans[j][k - 1][1] * ans[k + 1][i + j][1] +
ans[j][k - 1][0] * ans[k + 1][i + j][1] +
ans[j][k - 1][1] * ans[k + 1][i + j][0];
ans[j][j + i][0] += ans[j][k - 1][0] * ans[k + 1][i + j][0];
} else if (chars[k] == '^') {
ans[j][j + i][1] += ans[j][k - 1][1] * ans[k + 1][i + j][0] +
ans[j][k - 1][0] * ans[k + 1][i + j][1];
ans[j][j + i][0] += ans[j][k - 1][0] * ans[k + 1][i + j][0] +
ans[j][k - 1][1] * ans[k + 1][i + j][1];
}
}
}
}
return ans[0][len - 1][result];
}