文章目录
一、10 斐波那契数列 I
1.算法描述
写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项。斐波那契数列的定义如下:
F(0) = 0, F(1) = 1
F(N) = F(N - 1) + F(N - 2),其中 N > 1.
斐波那契数列由 0 和 1 开始,之后的斐波那契数就是由之前的两数相加而得出。
答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。
2.算法题解
- 当 n = 0 时 F(0) = 0,当 n = 1 时 F(1) = 1
- 当 n > 1 时 F(n) = F(n - 1) + F(n - 2), 其中 n > 1
- 本地中需要注意大数取模的问题
class Solution {
public int fib(int n) {
if (n == 0 || n == 1) {
return n;
}
int len = n + 1;
List<Integer> list = new ArrayList<>(len);
list.add(0);
list.add(1);
for (int i = 2; i < len; i++) {
Integer res = list.get(i - 1) + list.get(i - 2);
list.add(res % 1000000007);
}
return list.get(n);
}
}
二、10 青蛙跳台阶问题 ||
1.算法描述
一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。
答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。
2.算法题解
设跳上 n 级台阶有 f(n) 种跳法。在所有跳法中,青蛙的最后一步只有两种情况: 跳上 1 级或 2 级台阶。
- 当为 1 级台阶:剩 n−1 个台阶,此情况共有 f(n−1) 种跳法;
- 当为 2 级台阶:剩 n−2 个台阶,此情况共有 f(n-2) 种跳法。
f(n) 为以上两种情况之和,即 f(n)=f(n-1)+f(n-2),以上递推性质为斐波那契数列。本题可转化为求斐波那契数列第 n 项的值。
本题的初始值和斐波那契不同
- 青蛙跳台阶问题:f(0)=1,f(1)=1,f(2)=2;
- 斐波那契数列问题:f(0)=0,f(1)=1,f(2)=1。
class Solution {
public int numWays(int n) {
if (n <= 1) {
return 1;
}
int[] nums = new int[n + 1];
nums[1] = 1;
nums[2] = 2;
for (int i = 3; i < nums.length; i++) {
nums[i] = nums[i - 1] + nums[i - 2];
nums[i] %= 1000000007;
}
return nums[n];
}
}
三、11 旋转数组的最小数字
1.算法描述
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一个旋转,该数组的最小值为1。
2.算法题解
旋转点的元素一定小于左排序数组的所有元素,一定大于右排序数组的所有元素。
- 声明 left、right 双指针分别指向 numbers 数组左右两端。
- 设 mid = left + (right - left) / 2 为每次二分的中点,因此恒有 left < mid < right,可分为以下三种情况:
- 当 numbers[mid] > numbers[right] 时:mid 一定在左排序数组中,即旋转点 x 一定在 [mid + 1, right] 闭区间内。
- 当 numbers[mid] < numbers[right] 时:mid 一定在右排序数组中,即旋转点 x 一定在 [left, mid] 闭区间内。
- 当 numbers[mid] = numbers[right] 时:无法判断 mid 在哪个排序数组中,即无法判断旋转点 x 在 [left, mid] 还是 [mid + 1, right] 区间中。解决方案: 执行 right = right - 1 缩小判断范围。
- 当 left = right 时跳出二分循环,并返回 旋转点的值 numbers[left]即可。
class Solution {
public int minArray(int[] numbers) {
int left = 0, right = numbers.length - 1;
while (left < right) {
int mid = left + (right - left) / 2;
if (numbers[mid] < numbers[right]) {
right = mid;
} else if (numbers[mid] > numbers[right]) {
left = mid + 1;
} else {
right -= 1;
}
}
return numbers[left];
}
}
四、12 矩阵中的路径
1.算法描述
请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一格开始,每一步可以在矩阵中向左、右、上、下移动一格。如果一条路径经过了矩阵的某一格,那么该路径不能再次进入该格子。例如,在下面的3×4的矩阵中包含一条字符串“bfce”的路径
[[“a”,“b”,“c”,“e”],
[“s”,“f”,“c”,“s”],
[“a”,“d”,“e”,“e”]]
但矩阵中不包含字符串“abfb”的路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入这个格子。
2.算法题解
五、13 机器人的运动范围
1.算法描述
请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100"、“5e2”、"-123"、“3.1416”、"-1E-16"、“0123"都表示数值,但"12e”、“1a3.14”、“1.2.3”、"±5"及"12e+5.4"都不是。
2.算法题解
六、14 剪绳子 I
1.算法描述
给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]…k[m-1] 。请问 k[0]k[1]…*k[m-1] 可能的最大乘积是多少?
例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。
2.算法题解
根据(题怪推到过程)数学推导可知:一个数被分的份数越多乘积越大,但份数会有一个极值,当每份的数值等于 3 的时候,乘积达到最大。
切分规则:
- 最优:把绳子尽可能切为多个长度为 3 的片段,留下的最后一段绳子的长度可能为 0、1、2 三种情况。
- 次优:若最后一段绳子长度为 2 则保留,不再拆为。
- 最差:若最后一段绳子长度为 1 则应把一份 3 + 1 替换为 2 + 2,因为 2 × 2 > 3 × 1。
算法流程:
- 当 n ≤ 3 时,按照规则应不切分,但由于题目要求必须剪成 m > 1 段,因此必须剪出一段长度为 1 的绳子,即返回 n - 1 。
- 当 n > 3 时,求 n 除以 3 的 整数部分 a 和余数部分 b (即 n = 3a + b),并分为以下三种情况:
- 当 b = 0 时直接返回 3^a
- 当 b = 1 时要将一个 1 + 3 转换为 2 + 2,因此返回 3^(a-1) × 4
- 当 b = 2 时返回 3^a × 2
class Solution {
public int cuttingRope(int n) {
if (n <= 3) {
return n - 1;
}
int a = n / 3, b = n % 3;
if (b == 0) {
return (int) Math.pow(3, a);
}
if (b == 1) {
return (int) Math.pow(3, a - 1) * 4;
}
return (int) Math.pow(3, a) * 2;
}
}
七、14 剪绳子 II
1.算法描述
给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]…k[m - 1] 。请问 k[0]k[1]…*k[m - 1] 可能的最大乘积是多少?
例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。
答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。
2.算法题解
本题基于剪绳子 I,主要考察数据取模。首先求 n 除以 3 的 整数部分 a 和余数部分 b (即 n = 3a + b),然后计算 3^(a-1) 的值 ret,b 分为以下三种情况:
- 当 b = 0 时直接返回 ret × 3
- 当 b = 1 时要将一个 1 + 3 转换为 2 + 2,因此返回 ret × 4
- 当 b = 2 时返回 ret × 6
class Solution {
public int cuttingRope(int n) {
if (n <= 3) {
return n - 1;
}
int a = n / 3, b = n % 3;
long ret = 1;
for (int i = 1; i < a; i++) {
ret = ret * 3 % 1000000007;
}
if (b == 0) {
return (int) (ret * 3 % 1000000007);
}
if (b == 1) {
return (int) (ret * 4 % 1000000007);
}
return (int) (ret * 6 % 1000000007);
}
}