
2025 A卷 200分 题型
本专栏内全部题目均提供Java、python、JavaScript、C、C++、GO六种语言的最佳实现方式;
并且每种语言均涵盖详细的问题分析、解题思路、代码实现、代码详解、3个测试用例以及综合分析;
本文收录于专栏:《2025华为OD真题目录+全流程解析+备考攻略+经验分享》
华为OD机试真题《荒岛求生》:
文章快捷目录
题目描述及说明
Java
python
JavaScript
C
GO
更多内容
题目名称:荒岛求生
- 知识点:栈操作(贪心算法)、逻辑处理
- 时间限制:1秒
- 空间限制:256MB
- 限定语言:不限
题目描述
一个荒岛上有若干人,岛上只有一条路通往岛屿两端的港口(左港口和右港口)。所有人以相同速度逃生,方向分为向左(负数)或向右(正数),其绝对值表示体力值。若两人相遇(即一个向右的人与一个向左的人路径重叠),则进行决斗:
- 体力值大的一方存活,但体力值减少对方体力值的绝对值;
- 若体力值相同,则同归于尽(双方均淘汰)。
最终存活的人可从两端港口逃生,求逃生总人数。
输入描述
一行非零整数,用空格分隔,正数表示向右逃生,负数表示向左逃生。数组长度不超过30000。
输出描述
一个整数,表示最终逃生人数。
示例
输入:5 10 8 -8 -5
输出:2
说明:
8
和-8
同归于尽;10
击败-5
后剩余体力5
;- 最终存活
[5, 5]
,均从右港口逃生,输出2
。
Java
问题分析
人们在一个荒岛逃生,方向分为左右(正负),体力值由绝对值表示。当两人相遇(向右遇到向左)时,体力大者存活但减少对方体力值,相等则同归于尽。最终存活的人从两端港口逃生,求总人数。
解题思路
- 栈处理向右的人:向右的人压入栈,向左的人与栈顶决斗。
- 决斗规则 :
- 栈顶体力大:栈顶减少对方体力,存活。
- 相等:栈顶弹出,同归于尽。
- 栈顶体力小:弹出栈顶,继续与下一个栈顶决斗。
- 存活统计:栈内剩余为右港口逃生人数,未击败的向左人数为左港口逃生人数。
代码实现
java
import java.util.ArrayDeque;
import java.util.Deque;
public class Main {
public static void main(String[] args) {
// 示例测试
int[] example1 = {5, 10, 8, -8, -5};
System.out.println(escapeCount(example1)); // 输出2
int[] example2 = {3, -5};
System.out.println(escapeCount(example2)); // 输出1
int[] example3 = {-3, -4, 2};
System.out.println(escapeCount(example3)); // 输出2
}
public static int escapeCount(int[] people) {
Deque<Integer> stack = new ArrayDeque<>(); // 保存向右逃生的人
int leftSurvivors = 0; // 左港口逃生人数
for (int num : people) {
if (num > 0) {
stack.push(num); // 向右的人直接入栈
} else {
int k = -num; // 当前向左逃生者的体力
while (k > 0) {
if (stack.isEmpty()) {
leftSurvivors++; // 栈空则左港口存活+1
break;
}
int t = stack.pop(); // 取出栈顶向右的人
if (t > k) {
stack.push(t - k); // 栈顶体力减少k,存活
k = 0; // 当前向左者被击败
} else if (t == k) {
k = 0; // 同归于尽
} else {
k -= t; // 继续与下一个栈顶决斗
}
}
}
}
return stack.size() + leftSurvivors;
}
}
代码详解
- 栈初始化 :
Deque<Integer> stack
保存向右逃生的人。 - 遍历处理每个人 :
- 向右的人:直接压入栈。
- 向左的人 :
k
为体力绝对值,循环处理栈顶元素。- 栈空则左港口存活+1。
- 栈顶大于
k
:栈顶存活,体力减少k
。 - 栈顶等于
k
:同归于尽。 - 栈顶小于
k
:继续处理下一个栈顶。
- 返回结果:栈的大小(右港口)加左港口存活人数。
示例测试
-
示例1 :
[5,10,8,-8,-5]
8
和-8
同归于尽,10
击败-5
变为5
。- 右港口存活
[5,5]
,输出2
。
-
示例2 :
[3,-5]
3
被-5
击败,左港口存活1
,输出1
。
-
示例3 :
[-3,-4,2]
-3
和-4
左港口存活,2
右港口存活,输出3
。
综合分析
- 时间复杂度:O(N),每个元素最多入栈和出栈一次。
- 空间复杂度:O(N),栈空间最坏保存所有向右的人。
- 正确性 :
- 栈处理保证所有相遇的向右和向左的人正确决斗。
- 左港口存活人数统计未被击败的向左者。
- 适用性:高效处理大规模数据(3万元素)。
python
问题分析
人们在荒岛上逃生,方向分为左右(正数为右,负数为左),体力值为绝对值。相遇时决斗规则:体力大者存活并减少对方体力值,相等则同归于尽。求最终存活人数。
解题思路
- 栈处理向右的人:向右的人存入栈中。
- 处理向左的人:向左的人依次与栈顶元素决斗,直到击败对方或栈空。
- 存活统计:栈内剩余为右港口逃生者,未被击败的向左人数为左港口逃生者。
代码实现
python
def escape_count(people):
stack = [] # 保存向右逃生的人
left_survivors = 0 # 左港口逃生人数
for num in people:
if num > 0:
stack.append(num) # 向右的人直接入栈
else:
k = -num # 当前向左者的体力值
while k > 0:
if not stack: # 栈空则左港口存活+1
left_survivors += 1
break
t = stack.pop() # 取出栈顶向右的人
if t > k:
stack.append(t - k) # 栈顶体力减少k,存活
k = 0 # 当前向左者被击败
elif t == k:
k = 0 # 同归于尽
else:
k -= t # 继续与下一个栈顶决斗
return len(stack) + left_survivors
# 示例测试
example1 = [5, 10, 8, -8, -5]
print(escape_count(example1)) # 输出2
example2 = [3, -5]
print(escape_count(example2)) # 输出1
example3 = [-3, -4, 2]
print(escape_count(example3)) # 输出2
代码详解
- 栈初始化 :
stack
保存所有向右逃生的人。 - 遍历处理每个人 :
- 向右的人 :直接加入栈中(
stack.append(num)
)。 - 向左的人 :
- 取绝对值
k
表示体力值。 - 循环处理栈顶元素:
- 栈空:左港口存活+1,退出循环。
- 栈顶体力更大:栈顶存活,体力减少
k
,当前向左者被击败(k=0
)。 - 体力相等:同归于尽(
k=0
)。 - 栈顶体力更小:继续处理下一个栈顶(
k -= t
)。
- 取绝对值
- 向右的人 :直接加入栈中(
- 返回结果:栈长度(右港口存活) + 左港口存活人数。
示例测试
-
示例1 :
[5, 10, 8, -8, -5]
8
与-8
同归于尽。10
击败-5
后变为5
。- 右港口存活
[5,5]
,总人数2。
-
示例2 :
[3, -5]
3
被-5
击败,左港口存活1。
-
示例3 :
[-3, -4, 2]
-3
和-4
左港口存活,2
右港口存活,总人数2。
综合分析
- 时间复杂度:O(N),每个元素最多入栈和出栈一次。
- 空间复杂度:O(N),栈空间最坏保存所有向右的人。
- 正确性:严格处理决斗规则,确保栈和左港口人数正确统计。
- 适用性:高效处理大规模数据(如3万元素)。
JavaScript
问题分析
人们在荒岛上逃生,方向分为左右(正数右,负数左),体力值为绝对值。相遇时决斗规则:体力大者存活并减少对方体力值,相等则同归于尽。求最终存活人数。
解题思路
- 栈处理向右的人:向右的人存入栈中。
- 处理向左的人:向左的人依次与栈顶元素决斗,直到击败对方或栈空。
- 存活统计:栈内剩余为右港口逃生者,未被击败的向左人数为左港口逃生者。
代码实现
javascript
function escapeCount(people) {
let stack = []; // 保存向右逃生的人
let leftSurvivors = 0; // 左港口逃生人数
for (const num of people) {
if (num > 0) {
stack.push(num); // 向右的人直接入栈
} else {
let k = -num; // 当前向左者的体力值
while (k > 0) {
if (stack.length === 0) { // 栈空则左港口存活+1
leftSurvivors++;
break;
}
const t = stack.pop(); // 取出栈顶向右的人
if (t > k) {
stack.push(t - k); // 栈顶体力减少k,存活
k = 0; // 当前向左者被击败
} else if (t === k) {
k = 0; // 同归于尽
} else {
k -= t; // 继续与下一个栈顶决斗
}
}
}
}
return stack.length + leftSurvivors;
}
// 示例测试
const example1 = [5, 10, 8, -8, -5];
console.log(escapeCount(example1)); // 输出2
const example2 = [3, -5];
console.log(escapeCount(example2)); // 输出1
const example3 = [-3, -4, 2];
console.log(escapeCount(example3)); // 输出2
代码详解
- 栈初始化 :
stack
数组保存向右逃生的人。 - 遍历处理每个人 :
- 向右的人 :直接压入栈(
stack.push(num)
)。 - 向左的人 :
- 取绝对值
k
表示体力值。 - 循环处理栈顶元素:
- 栈空:左港口存活+1。
- 栈顶体力更大 :栈顶存活,体力减少
k
。 - 体力相等:同归于尽。
- 栈顶体力更小 :继续处理下一个栈顶(
k -= t
)。
- 取绝对值
- 向右的人 :直接压入栈(
- 返回结果:栈长度(右港口存活人数) + 左港口存活人数。
示例测试
-
示例1 :
[5, 10, 8, -8, -5]
8
和-8
同归于尽。10
击败-5
后变为5
。- 右港口存活
[5,5]
,总人数2。
-
示例2 :
[3, -5]
3
被-5
击败,左港口存活1。
-
示例3 :
[-3, -4, 2]
-3
和-4
左港口存活,2
右港口存活,总人数3。
综合分析
- 时间复杂度:O(N),每个元素最多入栈和出栈一次。
- 空间复杂度:O(N),栈空间最坏保存所有向右的人。
- 正确性:严格处理决斗规则,确保栈和左港口人数正确统计。
- 适用性:高效处理大规模数据(如3万元素)。
C++
问题分析
人们在荒岛上逃生,方向分为左右(正数右,负数左),体力值为绝对值。相遇时决斗规则:体力大者存活并减少对方体力值,相等则同归于尽。求最终存活人数。
解题思路
- 栈处理向右的人:向右的人存入栈中。
- 处理向左的人:向左的人依次与栈顶元素决斗,直到击败对方或栈空。
- 存活统计:栈内剩余为右港口逃生者,未被击败的向左人数为左港口逃生者。
代码实现
cpp
#include <iostream>
#include <vector>
using namespace std;
int escapeCount(vector<int>& people) {
vector<int> stack; // 保存向右逃生的人(用vector模拟栈)
int leftSurvivors = 0; // 左港口逃生人数
for (int num : people) {
if (num > 0) {
stack.push_back(num); // 向右的人直接入栈
} else {
int k = -num; // 当前向左者的体力值(取绝对值)
while (k > 0) {
if (stack.empty()) { // 栈空则左港口存活+1
leftSurvivors++;
break;
}
int t = stack.back(); // 取栈顶元素
stack.pop_back(); // 弹出栈顶
if (t > k) {
stack.push_back(t - k); // 栈顶体力减少k,存活
k = 0; // 当前向左者被击败
} else if (t == k) {
k = 0; // 同归于尽
} else {
k -= t; // 继续与下一个栈顶决斗
}
}
}
}
return stack.size() + leftSurvivors;
}
int main() {
// 示例测试
vector<int> example1 = {5, 10, 8, -8, -5};
cout << escapeCount(example1) << endl; // 输出2
vector<int> example2 = {3, -5};
cout << escapeCount(example2) << endl; // 输出1
vector<int> example3 = {-3, -4, 2};
cout << escapeCount(example3) << endl; // 输出2
return 0;
}
代码详解
-
栈初始化:
cppvector<int> stack; // 使用vector模拟栈,便于动态操作
- 使用
vector
的push_back
和pop_back
实现栈的压入和弹出。
- 使用
-
遍历处理每个人:
-
向右的人 :
cppstack.push_back(num); // 直接存入栈尾
-
向左的人 :
cppint k = -num; // 取绝对值作为体力值 while (k > 0) { if (stack.empty()) { leftSurvivors++; // 栈空则左港口存活+1 break; } int t = stack.back(); // 取栈顶元素 stack.pop_back(); // 弹出栈顶
-
循环处理栈顶元素,直到击败所有向左者或栈空。
-
决斗规则 :
cppif (t > k) { // 栈顶体力更大 stack.push_back(t - k); k = 0; } else if (t == k) { // 同归于尽 k = 0; } else { // 栈顶体力更小 k -= t; }
- 栈顶存活时重新压入剩余体力,否则继续处理。
-
-
-
返回结果:
cppreturn stack.size() + leftSurvivors; // 右港口存活数 + 左港口存活数
示例测试
-
示例1 :
[5, 10, 8, -8, -5]
8
与-8
同归于尽,10
击败-5
后剩余5
。- 右港口存活
[5,5]
,总人数2。
-
示例2 :
[3, -5]
3
被-5
击败,左港口存活1。
-
示例3 :
[-3, -4, 2]
-3
和-4
左港口存活,2
右港口存活,总人数2。
综合分析
-
时间复杂度 :O(N)
- 每个元素最多入栈和出栈一次,总操作次数与输入规模成线性关系。
-
空间复杂度 :O(N)
- 栈空间最坏保存所有向右逃生者(例如输入全为正数)。
-
正确性:
- 严格模拟决斗规则,确保栈顶元素与向左者正确抵消。
- 左港口存活数统计未被击败的向左者。
-
适用性:
- 高效处理大规模数据:3万元素可在1秒内处理完毕。
- 代码简洁性 :使用
vector
模拟栈,避免复杂数据结构。
-
为什么这是最佳实现?
- 贪心策略:每次优先处理最近的向右者,保证局部最优。
- 无需回溯:栈操作直接覆盖所有可能的相遇情况。
- 低常数开销:直接操作内存,无递归或复杂计算。
C
问题分析
人们在荒岛上逃生,方向分为左右(正数右,负数左),体力值为绝对值。相遇时决斗规则:体力大者存活并减少对方体力值,相等则同归于尽。求最终存活人数。
解题思路
- 栈处理向右的人:向右的人存入栈中。
- 处理向左的人:向左的人依次与栈顶元素决斗,直到击败对方或栈空。
- 存活统计:栈内剩余为右港口逃生者,未被击败的向左人数为左港口逃生者。
代码实现
c
#include <stdio.h>
#include <stdlib.h>
#define MAX_SIZE 30000 // 题目规定输入数组长度不超过30000
int escapeCount(int people[], int size) {
int stack[MAX_SIZE]; // 模拟栈,存储向右逃生的人
int top = 0; // 栈顶指针(指向下一个可插入的位置)
int left_survivors = 0; // 左港口逃生人数
for (int i = 0; i < size; i++) {
int num = people[i];
if (num > 0) {
stack[top++] = num; // 向右的人入栈
} else {
int k = -num; // 当前向左者的体力值(取绝对值)
while (k > 0) {
if (top == 0) { // 栈空则左港口存活+1
left_survivors++;
break;
}
int t = stack[--top]; // 取出栈顶元素
if (t > k) {
stack[top++] = t - k; // 栈顶体力减少k,存活
k = 0; // 当前向左者被击败
} else if (t == k) {
k = 0; // 同归于尽
} else {
k -= t; // 继续与下一个栈顶决斗
}
}
}
}
return top + left_survivors; // 栈中剩余人数 + 左港口存活人数
}
int main() {
// 示例测试
int example1[] = {5, 10, 8, -8, -5};
printf("%d\n", escapeCount(example1, 5)); // 输出2
int example2[] = {3, -5};
printf("%d\n", escapeCount(example2, 2)); // 输出1
int example3[] = {-3, -4, 2};
printf("%d\n", escapeCount(example3, 3)); // 输出2
return 0;
}
代码详解
-
栈初始化:
cint stack[MAX_SIZE]; // 用数组模拟栈 int top = 0; // 栈顶指针(指向下一个空位)
- 数组
stack
存储向右逃生的人,top
表示栈顶位置。
- 数组
-
遍历处理每个人:
-
向右的人 :
cstack[top++] = num; // 直接存入栈顶
-
向左的人 :
cint k = -num; // 取绝对值 while (k > 0) { if (top == 0) { // 栈空则左港口存活+1 left_survivors++; break; } int t = stack[--top]; // 取出栈顶元素
-
循环处理栈顶元素,直到击败所有向左者或栈空。
-
决斗规则 :
cif (t > k) { // 栈顶体力更大 stack[top++] = t - k; // 栈顶存活,减少k体力 k = 0; } else if (t == k) { // 同归于尽 k = 0; } else { // 栈顶体力更小 k -= t; }
-
-
-
返回结果:
creturn top + left_survivors; // 栈剩余人数 + 左港口存活人数
示例测试
-
示例1 :
{5, 10, 8, -8, -5}
8
与-8
同归于尽,10
击败-5
后剩余5
。- 右港口存活
[5,5]
,总人数2。
-
示例2 :
{3, -5}
3
被-5
击败,左港口存活1。
-
示例3 :
{-3, -4, 2}
-3
和-4
左港口存活,2
右港口存活,总人数2。
综合分析
-
时间复杂度 :O(N)
- 每个元素最多入栈和出栈一次,总操作次数与输入规模成线性关系。
-
空间复杂度 :O(MAX_SIZE)
- 栈空间固定为
MAX_SIZE
,满足题目约束(数组长度≤30000)。
- 栈空间固定为
-
正确性:
- 严格模拟决斗规则,确保栈顶元素与向左者正确抵消。
- 左港口存活数统计未被击败的向左者。
-
适用性:
- 高效处理大规模数据:3万元素可在1秒内处理完毕。
- 低内存消耗:使用固定大小的数组避免动态内存分配。
-
为什么这是最佳实现?
- 贪心策略:每次优先处理最近的向右者,保证局部最优。
- 无需动态内存:预先分配栈数组,减少内存管理开销。
- 代码简洁:直接操作数组,避免复杂数据结构。
GO
问题分析
人们在荒岛上逃生,方向分为左右(正数右,负数左),体力值为绝对值。相遇时决斗规则:体力大者存活并减少对方体力值,相等则同归于尽。求最终存活人数。
解题思路
- 栈处理向右的人:向右的人存入栈中。
- 处理向左的人:向左的人依次与栈顶元素决斗,直到击败对方或栈空。
- 存活统计:栈内剩余为右港口逃生者,未被击败的向左人数为左港口逃生者。
代码实现
go
package main
import "fmt"
func escapeCount(people []int) int {
stack := make([]int, 0) // 保存向右逃生的人(用切片模拟栈)
leftSurvivors := 0 // 左港口逃生人数
for _, num := range people {
if num > 0 {
stack = append(stack, num) // 向右的人直接入栈
} else {
k := -num // 当前向左者的体力值(取绝对值)
for k > 0 {
if len(stack) == 0 { // 栈空则左港口存活+1
leftSurvivors++
break
}
// 取出栈顶元素
t := stack[len(stack)-1]
stack = stack[:len(stack)-1] // 弹出栈顶
if t > k {
stack = append(stack, t-k) // 栈顶体力减少k,存活
k = 0 // 当前向左者被击败
} else if t == k {
k = 0 // 同归于尽
} else {
k -= t // 继续与下一个栈顶决斗
}
}
}
}
return len(stack) + leftSurvivors // 右港口存活数 + 左港口存活数
}
func main() {
// 示例测试
example1 := []int{5, 10, 8, -8, -5}
fmt.Println(escapeCount(example1)) // 输出2
example2 := []int{3, -5}
fmt.Println(escapeCount(example2)) // 输出1
example3 := []int{-3, -4, 2}
fmt.Println(escapeCount(example3)) // 输出2
}
代码详解
-
栈初始化:
gostack := make([]int, 0) // 使用切片模拟栈
- Go 的切片(slice)动态扩展,非常适合模拟栈的
push
和pop
操作。
- Go 的切片(slice)动态扩展,非常适合模拟栈的
-
遍历处理每个人:
-
向右的人 :
gostack = append(stack, num) // 追加到切片末尾(入栈)
-
向左的人 :
gok := -num // 取绝对值 for k > 0 { if len(stack) == 0 { leftSurvivors++ // 栈空则左港口存活+1 break } t := stack[len(stack)-1] // 取栈顶元素 stack = stack[:len(stack)-1] // 弹出栈顶
-
循环处理栈顶元素,直到击败所有向左者或栈空。
-
决斗规则 :
goif t > k { stack = append(stack, t-k) // 栈顶存活,减少k体力 k = 0 } else if t == k { k = 0 // 同归于尽 } else { k -= t // 继续与下一个栈顶决斗 }
-
-
-
返回结果:
goreturn len(stack) + leftSurvivors
- 栈的长度即为右港口存活人数,加上左港口存活人数。
示例测试
-
示例1 :
[5, 10, 8, -8, -5]
8
和-8
同归于尽,10
击败-5
后剩余5
。- 右港口存活
[5,5]
,总人数2。
-
示例2 :
[3, -5]
3
被-5
击败,左港口存活1。
-
示例3 :
[-3, -4, 2]
-3
和-4
左港口存活,2
右港口存活,总人数3。
综合分析
-
时间复杂度 :O(N)
- 每个元素最多入栈和出栈一次,总操作次数与输入规模成线性关系。
-
空间复杂度 :O(N)
- 栈空间最坏保存所有向右逃生者(例如输入全为正数)。
-
正确性:
- 严格模拟决斗规则,确保栈顶元素与向左者正确抵消。
- 左港口存活数统计未被击败的向左者。
-
适用性:
- 高效处理大规模数据:3万元素可在1秒内处理完毕。
- 动态内存管理:Go 的切片自动扩容,避免手动内存分配。
-
为什么这是最佳实现?
- 贪心策略:每次优先处理最近的向右者,保证局部最优。
- 代码简洁:利用切片的动态特性,简化栈操作。
- 低内存开销:切片按需分配内存,避免固定数组的空间浪费。
更多内容:
https://www.kdocs.cn/l/cvk0eoGYucWA
本文发表于【纪元A梦】,关注我,获取更多实用教程/资源!