华为OD机试真题---找终点
华为OD机试真题“找终点”是一道考察算法思维和编程能力的题目。题目大意是给定一个正整数数组,要求从数组的第一个元素开始,通过每一步跳跃(跳跃步数由当前所在元素的值决定)到达数组的最后一个元素,并计算最少的步骤数。如果无法到达数组的最后一个元素,则返回-1。
题目描述
- 输入:一个正整数数组,数组长度小于100,数组中的元素代表从当前位置可以跳跃的步数。
- 输出:最少的步骤数,如果无法到达终点则返回-1。
解题思路
-
初始化:
- 创建一个数组来存储正整数。
- 初始化一个变量来记录最小步数,初始值可以设置为一个较大的数(如数组长度加1,以便后续判断是否可达)。
-
遍历第一步的步长:
- 由于第一步的步长必须在1到数组长度的一半之间(包括1但不包括数组长度的一半),因此需要对这个范围内的步长进行遍历。
-
尝试到达终点:
- 对于每个可能的第一步步长,从数组的第一个元素开始,按照当前元素的值进行跳跃,直到无法继续跳跃或到达终点。
- 在跳跃过程中,记录步数,并在每次跳跃后更新当前位置。
-
判断并更新最小步数:
- 如果在某次跳跃中成功到达终点,则比较当前步数与之前记录的最小步数,并更新最小步数(如果更小的话)。
-
输出结果:
- 遍历完所有可能的第一步步长后,检查最小步数是否仍然保持为初始值(即未找到可达路径),如果是,则输出-1;否则,输出最小步数。
示例
输入:7 5 9 4 2 6 8 3 5 4 3 9
输出:2
(第一步跳5步到第二个元素9,第二步从9开始跳9步到达终点)
注意事项
- 题目要求的是最少的步骤数,因此需要遍历所有可能的第一步步长来找到最优解。
- 如果数组中的某个元素值为0,且该元素不是最后一个元素,则可能无法从该元素继续跳跃,需要特别注意这种情况。
- 在编程实现时,要注意数组越界的问题,确保在跳跃过程中不会访问到数组范围之外的元素。
编程实现(示例代码,以Java为例)
import java.util.Arrays;
import java.util.List;
public class FindFinish {
public static void main(String[] args) {
List<Integer> nums = Arrays.asList(7, 5, 9, 4, 2, 6, 8, 3, 5, 4, 3, 9);
int result = findFinish(nums);
System.out.println("最小步数:"+result);
}
public static int findFinish(List<Integer> nums) {
int n = nums.size();
if (n == 0) return -1;
int minSteps = n + 1; // 初始化为一个较大的数
for (int firstStep = 1; firstStep < n / 2; firstStep++) {
int steps = 1; // 记录步数,第一步已经迈出
int currentIndex = firstStep;
while (currentIndex < n) {
currentIndex += nums.get(currentIndex);
steps++;
if (currentIndex == n - 1) {
minSteps = Math.min(minSteps, steps);
break;
}
}
}
return minSteps == n + 1 ? -1 : minSteps;
}
}
注意:上述代码虽然能实现题目要求,输出最小步数。但仔细分析就会发现代码其实是有问题的。
潜在问题及修改建议:
1、数组越界问题:在循环中 currentIndex += nums.get(currentIndex) 可能导致数组越界。如果nums.get(currentIndex) 的值非常大,currentIndex 可能会超过 n - 1,从而导致 List 越界访问。
- 修改建议:在每次增加 currentIndex 前,先检查是否越界。
2、逻辑 Bug:当 firstStep 设置为 n / 2 时,可能会错过某些可能的路径。虽然题目没有明确说明,但从逻辑上讲,firstStep 应该遍历到 n-1 才能覆盖所有可能的情况。
- 修改建议:将 for 循环条件改为 firstStep <= n - 1。
优化方向及修改建议:
1、初始化 minSteps 的值:初始值 n + 1 可以简化为 Integer.MAX_VALUE,这样更直观且不容易出错。
- 修改建议:使用 Integer.MAX_VALUE 初始化 minSteps。
2、提高代码可读性:通过添加注释来提高代码的可读性和维护性。
- 修改建议:在关键逻辑处添加注释。
3、减少不必要的循环次数:在 while 循环内部,如果 currentIndex 超过 n - 1,则可以直接跳出循环,避免无意义的计算。
- 修改建议:在 while 循环内部添加判断条件。
以下是修改后的代码:
public static int findFinish(List<Integer> nums) {
int n = nums.size();
if (n == 0) return -1;
int minSteps = Integer.MAX_VALUE; // 初始化为一个极大的数
// 遍历所有可能的第一步
for (int firstStep = 1; firstStep <= n - 1; firstStep++) {
int steps = 1; // 记录步数,第一步已经迈出
int currentIndex = firstStep;
// 当前索引不超过数组长度时继续计算
while (currentIndex < n) {
// 防止数组越界
if (currentIndex + nums.get(currentIndex) >= n) {
break;
}
currentIndex += nums.get(currentIndex);
steps++;
if (currentIndex == n - 1) {
minSteps = Math.min(minSteps, steps);
break;
}
}
}
return minSteps == Integer.MAX_VALUE ? -1 : minSteps;
}