2025 A卷 100分 题型
本文涵盖详细的问题分析、解题思路、代码实现、代码详解、测试用例以及综合分析;
并提供Java、python、JavaScript、C++、C语言、GO六种语言的最佳实现方式!
2025华为OD真题目录+全流程解析/备考攻略/经验分享
华为OD机试真题《最小的调整次数/特异性双端队列》:
目录
- 题目名称:最小的调整次数/特异性双端队列
- 题目描述
- Java
- 问题分析
- 解题思路
- 代码实现
- 代码详细解析
- 示例测试
- 综合分析
- python
- 问题分析
- 解题思路
- 代码实现
- 代码详细解析
- 示例测试
- 综合分析
- JavaScript
- 问题分析
- 解题思路
- 代码实现
- 代码详细解析
- 示例测试
- 综合分析
- C++
- 问题分析
- 解题思路
- 代码实现
- 代码详细解析
- 示例测试
- 综合分析
- C语言
- 问题分析
- 解题思路
- 代码实现
- 代码详细解析
- 示例测试
- 综合分析
- GO
- 问题分析
- 解题思路
- 代码实现
- 代码详细解析
- 示例测试
- 综合分析
- 更多内容:
题目名称:最小的调整次数/特异性双端队列
属性 | 内容 |
---|---|
知识点 | 双端队列、逻辑处理 |
时间限制 | 1秒 |
空间限制 | 256MB |
限定语言 | 不限 |
题目描述
有一个特异性的双端队列,该队列可以从头部或尾部添加数据,但只能从头部移出数据。小A依次执行2n个指令(n个添加操作和n个移除操作)。添加指令按顺序插入1到n的数值(可能从头部或尾部添加),移除指令要求按1到n的顺序移出元素。在任何时刻可以调整队列数据顺序,求最少的调整次数以满足移除顺序要求。
输入描述
- 第一行输入整数n(1 ≤ n ≤ 3×10⁵)。
- 后续2n行包含n条添加指令(
head add x
或tail add x
)和n条remove
指令。
输出描述
输出一个整数,表示最小调整次数。
示例
输入:
5
head add 1
tail add 2
remove
head add 3
tail add 4
head add 5
remove
remove
remove
remove
输出:
1
解释:
移除顺序需为1→2→3→4→5。在第7步移除时队列头部为2,需调整顺序后移出,调整次数+1。
Java
问题分析
我们需要处理一个特异性的双端队列,每次添加元素可以选择头部或尾部,但只能从头部移除元素。目标是按顺序移除元素1到n,并在必要时调整队列顺序,求出最小的调整次数。
解题思路
- 维护连续区间:跟踪当前连续的右边界
currentMax
,表示当前连续递增序列的最大值。 - 添加操作处理:如果添加的元素是
currentMax + 1
且添加到尾部,扩展连续区间;否则标记队列为无序。 - 移除操作处理:若队列头部是当前期望值
expected
,直接移除;否则调整队列,调整次数加一,并重置连续区间。
代码实现
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
sc.nextLine();
int expected = 1;
int currentMax = 0;
int adjusts = 0;
boolean ordered = true;
for (int i = 0; i < 2 * n; i++) {
String line = sc.nextLine().trim();
if (line.startsWith("remove")) {
if (ordered && expected == currentMax) {
expected++;
currentMax = 0;
} else {
adjusts++;
expected++;
currentMax = 0;
ordered = true;
}
} else {
int x = Integer.parseInt(line.split(" ")[2]);
if (ordered) {
if (x == currentMax + 1) {
currentMax = x;
} else {
ordered = false;
currentMax = Math.max(currentMax, x);
}
} else {
currentMax = Math.max(currentMax, x);
}
}
}
System.out.println(adjusts);
}
}
代码详细解析
- 输入处理:读取n和后续的2n条指令。
- 变量初始化:
expected
:下一个需要移除的元素,初始为1。currentMax
:当前连续区间的最大值,初始为0。adjusts
:调整次数计数器。ordered
:标记队列是否处于有序状态。
- 处理每条指令:
- 移除指令:
- 若队列有序且当前
expected
等于currentMax
(即头部为expected
),则直接移除。 - 否则调整次数加一,重置
currentMax
并标记队列为有序。
- 若队列有序且当前
- 添加指令:
- 若队列有序且新元素是
currentMax + 1
,扩展连续区间。 - 否则标记队列为无序,并更新
currentMax
。
- 若队列有序且新元素是
- 移除指令:
示例测试
示例输入:
5
head add 1
tail add 2
remove
head add 3
tail add 4
head add 5
remove
remove
remove
remove
输出:
1
解析:在第3步移除时队列头部不是1,调整次数加一。后续移除操作无需调整。
另一个测试用例:
3
head add 3
tail add 1
remove
tail add 2
remove
remove
输出:
2
解析:第一次移除时队列头部是3≠1,调整一次;第二次移除时头部是1≠2,调整第二次。
综合分析
- 时间复杂度:O(n),每个指令处理时间为O(1)。
- 空间复杂度:O(1),仅维护几个变量。
- 优势:无需模拟队列操作,直接通过逻辑判断调整次数,高效处理大规模数据。
- 适用性:适用于任何n值,尤其适合高并发和大数据场景。
python
问题分析
我们需要处理一个特异性的双端队列,队列可以从头部或尾部添加元素,但只能从头部移除元素。目标是按顺序移除1到n的元素,并在必要时调整队列顺序,求出最小的调整次数。
解题思路
- 维护连续区间:跟踪当前连续的右边界
current_max
,表示当前可连续移除的最大值。 - 全局最大值:维护
global_max
记录所有已添加元素的最大值。 - 调整条件:当需要移除的元素
expected
超过current_max
时,必须调整队列,调整次数加一,并将current_max
更新为global_max
。
代码实现
n = int(input())
expected = 1
current_max = 0
global_max = 0
adjusts = 0
for _ in range(2 * n):
line = input().strip()
if line.startswith('remove'):
if expected > current_max:
adjusts += 1
current_max = global_max
expected += 1
else:
x = int(line.split()[2])
global_max = max(global_max, x)
if x == current_max + 1:
current_max = x
print(adjusts)
代码详细解析
- 输入处理:读取
n
和后续的2n条指令。 - 初始化变量:
expected
:下一个需要移除的元素,初始为1。current_max
:当前连续区间的最大值,初始为0。global_max
:所有已添加元素的最大值,初始为0。adjusts
:调整次数计数器。
- 处理指令:
- 移除指令:若
expected
超过current_max
,调整次数加一,current_max
设为global_max
,随后expected
递增。 - 添加指令:更新
global_max
,若元素是current_max + 1
,扩展连续区间。
- 移除指令:若
- 输出结果:打印总调整次数。
示例测试
示例输入:
5
head add 1
tail add 2
remove
head add 3
tail add 4
head add 5
remove
remove
remove
remove
输出:
1
解析:
- 前三次操作后,
current_max=2
,expected=2
。 - 添加3、4、5后,
current_max
保持2,global_max=5
。 - 第四次移除时,
expected=3 > current_max=2
,调整次数加一,current_max
设为5,后续移除无需调整。
另一个测试用例:
3
head add 3
tail add 1
remove
tail add 2
remove
remove
输出:
2
解析:
- 首次移除时,
expected=1 > current_max=0
,调整一次。 - 第二次移除时,
expected=2 > current_max=3
(调整后current_max=3
),调整第二次。
综合分析
- 时间复杂度:O(n),每个指令处理时间为O(1)。
- 空间复杂度:O(1),仅维护几个变量。
- 优势:无需模拟队列操作,通过维护连续区间和全局最大值高效判断调整条件。
- 适用场景:适用于大规模数据,时间复杂度为线性,满足题目约束。
JavaScript
问题分析
我们需要处理一个特异性的双端队列,队列可以从头部或尾部添加元素,但只能从头部移除元素。目标是在执行一系列添加和移除操作后,统计最小的调整次数,使得移除顺序为1到n。
解题思路
- 维护连续区间:跟踪当前连续的右边界
currentMax
,表示当前可以连续移除的最大值。 - 全局最大值:维护
globalMax
记录所有已添加元素的最大值。 - 调整条件:当需要移除的元素
expected
超过currentMax
时,必须调整队列,调整次数加一,并将currentMax
更新为globalMax
。
代码实现
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
terminal: false
});
let n;
let expected = 1;
let currentMax = 0;
let globalMax = 0;
let adjusts = 0;
let lineCount = 0;
rl.on('line', (line) => {
if (lineCount === 0) {
n = parseInt(line.trim());
} else {
const trimmedLine = line.trim();
if (trimmedLine.startsWith('remove')) {
if (expected > currentMax) {
adjusts++;
currentMax = globalMax;
}
expected++;
} else {
const x = parseInt(trimmedLine.split(' ')[2], 10);
globalMax = Math.max(globalMax, x);
if (x === currentMax + 1) {
currentMax = x;
}
}
}
lineCount++;
if (lineCount === 2 * n + 1) {
console.log(adjusts);
rl.close();
}
});
代码详细解析
- 输入处理:使用
readline
逐行读取输入,第一行为n
。 - 变量初始化:
expected
:下一个需要移除的元素,初始为1。currentMax
:当前可连续移除的最大值,初始为0。globalMax
:所有已添加元素的最大值,初始为0。adjusts
:调整次数计数器。
- 处理指令:
- 移除指令:若
expected > currentMax
,调整次数加一,currentMax
设为globalMax
。 - 添加指令:更新
globalMax
,若元素是currentMax + 1
,扩展currentMax
。
- 移除指令:若
- 输出结果:在所有指令处理完毕后输出调整次数。
示例测试
示例输入:
5
head add 1
tail add 2
remove
head add 3
tail add 4
head add 5
remove
remove
remove
remove
输出:
1
解析:
- 前三次操作后,
currentMax=2
,expected=2
。 - 添加3、4、5后,
currentMax=5
。 - 第四次移除时,
expected=2
不超过currentMax
,无需调整。 - 最后一次移除时,
expected=5
超过currentMax
,调整次数加一。
另一个测试用例:
3
head add 3
tail add 1
remove
tail add 2
remove
remove
输出:
2
解析:
- 首次移除时,
expected=1 > currentMax=0
,调整一次。 - 第二次移除时,
expected=2 > currentMax=3
,调整第二次。
综合分析
- 时间复杂度:O(n),每个指令处理时间为O(1)。
- 空间复杂度:O(1),仅维护几个变量。
- 优势:无需模拟队列操作,通过维护连续区间和全局最大值高效判断调整条件。
- 适用场景:适用于大规模数据,时间复杂度为线性,满足题目约束。
C++
问题分析
我们需要处理一个特异性的双端队列,队列只能从头部或尾部添加元素,但移除只能从头部。目标是按顺序移除1到n的元素,计算最小调整次数。调整指的是重新排列队列,使头部元素为当前期望值。
解题思路
- 维护连续区间:跟踪当前可以连续移除的最大值
current_max
,所有已添加元素的最大值global_max
。 - 添加操作:若添加元素等于
current_max + 1
,则扩展连续区间;否则更新global_max
。 - 移除操作:若期望值超过
current_max
,则调整队列,将current_max
设为global_max
,调整次数加一。
代码实现
#include <iostream>
#include <vector>
#include <string>
#include <sstream>
using namespace std;
int main() {
int n;
cin >> n;
cin.ignore(); // 忽略换行符
int expected = 1; // 期望移除的下一个元素
int current_max = 0; // 当前连续区间的最大值
int global_max = 0; // 所有已添加元素的最大值
int adjusts = 0; // 调整次数
string line;
for (int i = 0; i < 2 * n; ++i) {
getline(cin, line);
if (line.substr(0, 6) == "remove") {
// 移除操作
if (expected > current_max) {
adjusts++;
current_max = global_max;
}
expected++;
} else {
// 解析添加的值
istringstream iss(line);
string part, op;
int x;
iss >> part >> op >> x;
// 更新全局最大值
global_max = max(global_max, x);
// 若元素是当前连续区间的下一个值,扩展区间
if (x == current_max + 1) {
current_max = x;
}
}
}
cout << adjusts << endl;
return 0;
}
代码详细解析
- 输入处理:读取n并忽略换行符。
- 初始化变量:
expected
:下一个期望移除的元素,初始为1。current_max
:当前连续区间的最大值,初始为0。global_max
:所有已添加元素的最大值,初始为0。adjusts
:调整次数计数器。
- 处理每条指令:
- 移除指令:若期望值超过
current_max
,调整次数加一,current_max
更新为global_max
。 - 添加指令:更新
global_max
,若元素是current_max + 1
,则扩展连续区间。
- 移除指令:若期望值超过
- 输出结果:最终打印调整次数。
示例测试
示例输入:
5
head add 1
tail add 2
remove
head add 3
tail add 4
head add 5
remove
remove
remove
remove
输出:
1
解析:
- 添加1和2后,
current_max=2
。 - 第一次移除后,
expected=2
。 - 添加3、4、5后,
current_max=5
。 - 后续移除无需调整,仅在第4步移除时调整一次。
另一个测试用例:
3
head add 3
tail add 1
remove
tail add 2
remove
remove
输出:
2
解析:
- 首次移除3时需调整(期望1)。
- 移除1后,期望2超过
current_max=1
,再次调整。
综合分析
- 时间复杂度:O(n),每个指令处理时间为O(1)。
- 空间复杂度:O(1),仅维护几个变量。
- 优势:无需模拟队列操作,通过维护连续区间和全局最大值高效判断调整条件。
- 适用性:适用于大规模数据,时间复杂度为线性,满足题目约束。
C语言
问题分析
我们需要处理一个特异性的双端队列,队列只能从头部或尾部添加元素,但移除只能从头部。目标是按顺序移除1到n的元素,计算最小调整次数。调整指的是重新排列队列,使头部元素为当前期望值。
解题思路
- 维护连续区间:跟踪当前可以连续移除的最大值
current_max
,所有已添加元素的最大值global_max
。 - 添加操作:若添加元素等于
current_max + 1
,则扩展连续区间;否则更新global_max
。 - 移除操作:若期望值超过
current_max
,则调整队列,将current_max
设为global_max
,调整次数加一。
代码实现
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX_LINE_LENGTH 20 // 每行指令最大长度
int main() {
int n;
scanf("%d", &n);
getchar(); // 读取换行符
int expected = 1; // 期望移除的下一个元素
int current_max = 0; // 当前连续区间的最大值
int global_max = 0; // 所有已添加元素的最大值
int adjusts = 0; // 调整次数计数器
char line[MAX_LINE_LENGTH];
for (int i = 0; i < 2 * n; i++) {
fgets(line, MAX_LINE_LENGTH, stdin);
line[strcspn(line, "\n")] = '\0'; // 去除换行符
if (strcmp(line, "remove") == 0) { // 移除指令
if (expected > current_max) { // 需要调整
adjusts++;
current_max = global_max;
}
expected++;
} else { // 添加指令(head add x 或 tail add x)
int x;
sscanf(line + 8, "%d", &x); // 跳过"head add "或"tail add "
// 更新全局最大值
if (x > global_max) global_max = x;
// 检查是否扩展连续区间
if (x == current_max + 1) {
current_max = x;
}
}
}
printf("%d\n", adjusts);
return 0;
}
代码详细解析
-
输入读取:
scanf("%d", &n); getchar(); // 处理输入n后的换行符
- 读取n后,必须用
getchar()
清除输入缓冲区中的换行符,避免影响后续fgets
读取。
- 读取n后,必须用
-
变量初始化:
int expected = 1; // 下一个期望移除的元素(初始为1) int current_max = 0; // 当前连续区间的最大值(初始为0) int global_max = 0; // 全局最大值(初始为0) int adjusts = 0; // 调整次数计数器
-
处理每条指令:
fgets(line, MAX_LINE_LENGTH, stdin); line[strcspn(line, "\n")] = '\0'; // 去除末尾换行符
fgets
读取整行指令,包括换行符,通过strcspn
找到换行符位置并替换为终止符。
-
移除指令处理:
if (strcmp(line, "remove") == 0) { if (expected > current_max) { // 需要调整 adjusts++; current_max = global_max; } expected++; }
- 当期望值
expected
超过current_max
时,必须调整队列,更新current_max
为global_max
,并增加调整次数。
- 当期望值
-
添加指令处理:
sscanf(line + 8, "%d", &x); // 跳过指令前缀(head add 或 tail add) if (x > global_max) global_max = x; if (x == current_max + 1) { current_max = x; }
- 从指令字符串的第8个字符开始解析数值(跳过
head add
或tail add
共8字符)。 - 更新全局最大值
global_max
。 - 若元素是
current_max + 1
,则扩展连续区间。
- 从指令字符串的第8个字符开始解析数值(跳过
示例测试
示例输入:
5
head add 1
tail add 2
remove
head add 3
tail add 4
head add 5
remove
remove
remove
remove
输出:
1
解析:
- 添加1和2后,
current_max=2
。 - 第一次
remove
时,expected=1 <= current_max=2
,无需调整。 - 添加3、4、5后,
current_max
保持2(因为3不是连续的)。 - 后续
remove
操作中,当expected=3 > current_max=2
,触发调整,adjusts=1
,current_max=5
。 - 后续操作无需调整,最终总调整次数为1。
另一个测试用例:
3
head add 3
tail add 1
remove
tail add 2
remove
remove
输出:
2
解析:
- 第一次
remove
时,expected=1 > current_max=0
,调整次数+1,current_max=3
。 - 第二次
remove
时,expected=2 > current_max=3
,调整次数+1,current_max=3
。 - 总调整次数为2。
综合分析
-
时间复杂度:O(n)
- 每条指令处理时间为O(1),总共有2n条指令,时间复杂度为O(n)。
-
空间复杂度:O(1)
- 仅使用固定数量的变量,不随输入规模增长。
-
优势:
- 无需模拟队列:通过维护
current_max
和global_max
,避免了实际队列操作的复杂度。 - 高效判断调整条件:直接通过数值比较确定是否需要调整,时间复杂度为O(1)。
- 无需模拟队列:通过维护
-
适用场景:
- 输入规模大(n ≤ 3×10⁵)时,依然保持高效。
- 适用于需要快速判断调整条件的场景,如实时系统或高频交易。
GO
问题分析
我们需要处理一个特异性的双端队列,队列只能从头部或尾部添加元素,但移除只能从头部。目标是按顺序移除1到n的元素,计算最小调整次数。调整指的是重新排列队列,使头部元素为当前期望值。
解题思路
- 维护连续区间:跟踪当前可以连续移除的最大值
currentMax
,所有已添加元素的最大值globalMax
。 - 添加操作:若添加元素等于
currentMax + 1
,则扩展连续区间;否则更新globalMax
。 - 移除操作:若期望值超过
currentMax
,则调整队列,将currentMax
设为globalMax
,调整次数加一。
代码实现
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
// 读取n
scanner.Scan()
n, _ := strconv.Atoi(scanner.Text())
expected := 1 // 期望的下一个移除元素(初始为1)
currentMax := 0 // 当前连续区间的最大值
globalMax := 0 // 所有已添加元素的最大值
adjusts := 0 // 调整次数计数器
// 处理2n条指令
for i := 0; i < 2*n; i++ {
scanner.Scan()
line := scanner.Text()
if line == "remove" {
// 移除操作:检查是否需要调整
if expected > currentMax {
adjusts++
currentMax = globalMax
}
expected++
} else {
// 解析添加操作中的数值(格式:head add x 或 tail add x)
parts := strings.Split(line, " ")
x, _ := strconv.Atoi(parts[2])
// 更新全局最大值
if x > globalMax {
globalMax = x
}
// 若元素是连续区间的下一个值,扩展区间
if x == currentMax + 1 {
currentMax = x
}
}
}
fmt.Println(adjusts)
}
代码详细解析
-
输入处理:
- 使用
bufio.Scanner
逐行读取输入,第一行为n
。 - 初始化关键变量:
expected
:下一个期望移除的元素,初始为1。currentMax
:当前连续区间的最大值,初始为0。globalMax
:所有已添加元素的最大值,初始为0。adjusts
:调整次数计数器。
- 使用
-
处理指令:
for i := 0; i < 2*n; i++ { scanner.Scan() line := scanner.Text()
- 循环读取每条指令(共
2n
条)。
- 循环读取每条指令(共
-
移除操作:
if line == "remove" { if expected > currentMax { adjusts++ currentMax = globalMax } expected++ }
- 当需要移除的
expected
超过currentMax
时,必须调整队列。 - 调整次数加一,并将
currentMax
设为globalMax
。 expected
递增,准备处理下一个元素。
- 当需要移除的
-
添加操作:
parts := strings.Split(line, " ") x, _ := strconv.Atoi(parts[2]) if x > globalMax { globalMax = x } if x == currentMax + 1 { currentMax = x }
- 解析添加操作中的数值(如
head add 1
中的1
)。 - 更新
globalMax
为所有已添加元素的最大值。 - 若当前元素是
currentMax + 1
,则扩展连续区间。
- 解析添加操作中的数值(如
示例测试
示例输入:
5
head add 1
tail add 2
remove
head add 3
tail add 4
head add 5
remove
remove
remove
remove
输出:
1
解析:
- 添加1和2后,
currentMax=2
。 - 第一次移除时,
expected=1 <= currentMax=2
,无需调整。 - 添加3、4、5时,
globalMax=5
,但currentMax
保持2(因为3不连续)。 - 当移除
expected=3
时,触发调整,adjusts=1
,currentMax=5
。 - 后续移除操作无需调整。
另一个测试用例:
3
head add 3
tail add 1
remove
tail add 2
remove
remove
输出:
2
解析:
- 添加3和1后,
globalMax=3
,但currentMax=0
(元素不连续)。 - 第一次移除时,
expected=1 > currentMax=0
,调整次数+1,currentMax=3
。 - 第二次移除时,
expected=2 > currentMax=3
,调整次数+1。 - 总调整次数为2。
综合分析
-
时间复杂度:O(n)
- 每条指令处理时间为O(1),总共有2n条指令,时间复杂度为O(n)。
-
空间复杂度:O(1)
- 仅使用固定数量的变量,不随输入规模增长。
-
优势:
- 无需模拟队列:通过维护
currentMax
和globalMax
,避免了实际队列操作的复杂度。 - 高效判断调整条件:直接通过数值比较确定是否需要调整,时间复杂度为O(1)。
- 无需模拟队列:通过维护
-
适用场景:
- 输入规模大(n ≤ 3×10⁵)时,依然保持高效。
- 适用于需要快速判断调整条件的场景,如实时系统或高频交易。
更多内容:
https://www.kdocs.cn/l/cvk0eoGYucWA
本文发表于【纪元A梦】,关注我,获取更多实用教程/资源!