一、问题描述
问题描述
我们需要从多个已排序的列表中选取元素,以填充多个窗口。每个窗口需要展示一定数量的元素,且元素的选择需要遵循特定的穿插策略。具体来说,我们需要:
- 从第一个列表中为每个窗口选择一个元素,然后从第二个列表中为每个窗口选择一个元素,依此类推。
- 每个列表的元素应尽量均分到所有窗口中。如果某个列表的元素不足以均分到所有窗口,则应将剩余元素全部分配到前面的窗口中。
- 最终输出的元素列表应按照窗口顺序排列,即先输出第一个窗口的所有元素,再输出第二个窗口的所有元素,依此类推。
输入输出描述
输入
- 第一行输入为
N
,表示需要输出的窗口数量,取值范围为[1, 10]
。 - 第二行输入为
K
,表示每个窗口需要的元素数量,取值范围为[1, 100]
。 - 之后的行数不定(行数取值范围为
[1, 10]
),表示每个列表输出的元素列表。元素之间以空格隔开,已经过排序处理,每个列表输出的元素数量取值范围为[1, 100]
。
输出
- 输出元素列表,元素数量为
窗口数量 * 窗口大小
,元素之间以空格分隔。多个窗口合并为一个列表输出,先输出窗口1的元素列表,再输出窗口2的元素列表,依此类推。
解题思路
我们可以将最终的窗口集视为一个矩阵 windows
,该矩阵有 K
行 N
列,矩阵的每一列对应一个窗口。最终按列打印该矩阵,即为题解。
填充策略
- 初始化:创建一个大小为
K * N
的一维数组windows
,用于存储最终的元素列表。 - 填充过程:
- 从第一个列表开始,依次为每个窗口选择一个元素。
- 如果当前列表的元素不足以分配给所有窗口,则从下一个列表中“借”元素来补充。
- 每次填充后,更新指针
idx
,指向下一个需要填充的位置。
- 特殊情况处理:
- 如果某个列表的元素不足以分配给所有窗口,则需要从下一个列表中“借”元素。此时,需要确保存在下一个列表,否则无法继续填充。
示例解析
以题目中的示例为例:
输入:
4
7
0 1 2 3 4 5 6 7 8 9
10 11 12 13 14 15 16 17 18 19
20 21 22 23 24 25 26 27 28 29
输出:
0 10 20 4 14 24 8 1 11 21 5 15 25 9 2 12 22 6 16 26 18 3 13 23 7 17 27 19
解析:
- 从第一个列表中选择 4 条元素
0 1 2 3
,分别放到 4 个窗口中。 - 从第二个列表中选择 4 条元素
10 11 12 13
,分别放到 4 个窗口中。 - 从第三个列表中选择 4 条元素
20 21 22 23
,分别放到 4 个窗口中。 - 再次从第一个列表中选择 4 条元素
4 5 6 7
,分别放到 4 个窗口中。 - 从第一个列表中选择剩下的 2 条元素
8 9
,分别放到窗口1和窗口2。 - 从第二个列表中选择剩下的 2 条元素
18 19
,分别放到窗口3和窗口4。
最终输出的元素列表按照窗口顺序排列,即先输出窗口1的所有元素,再输出窗口2的所有元素,依此类推。
总结
通过将窗口集视为一个矩阵,并按照特定的填充策略依次从各个列表中选择元素,我们可以有效地解决这个问题。关键在于正确处理元素不足时的“借”元素操作,并确保最终输出的元素列表按照窗口顺序排列。
二、JavaScript算法源码
代码结构
-
输入处理:
- 使用
readline
模块读取输入数据。 - 解析窗口数量
n
和每个窗口的元素数量k
。 - 读取多个列表,并将每个列表转换为数字数组,存储在
lists
中。
- 使用
-
窗口矩阵填充:
- 创建一个一维数组
windows
,用于模拟k
行n
列的窗口矩阵。 - 使用指针
idx
记录当前填充的位置。 - 使用变量
level
记录当前从哪个列表中取值。
- 创建一个一维数组
-
填充逻辑:
- 从当前列表
lists[level]
中取出n
个元素,依次填充到windows
中。 - 如果当前列表元素不足,则从下一个列表中“借”元素。
- 更新指针
idx
和level
,确保填充过程正确。
- 从当前列表
-
输出结果:
- 按列遍历
windows
矩阵,将元素按窗口顺序收集到ans
数组中。 - 最终输出
ans
,元素之间用空格分隔。
- 按列遍历
代码逐行注释
const rl = require("readline").createInterface({ input: process.stdin });
var iter = rl[Symbol.asyncIterator]();
const readline = async () => (await iter.next()).value;void (async function () {// 读取窗口数量 nconst n = parseInt(await readline());// 读取每个窗口的元素数量 kconst k = parseInt(await readline());// 存储所有列表const lists = [];// 循环读取输入,直到输入结束while (true) {try {// 读取一行输入const s = await readline();// 如果输入为空,结束读取if (s.length == 0) break;// 将输入行按空格分割,转换为数字数组,并存入 listslists.push(s.split(" ").map(Number));} catch (e) {// 捕获异常(如输入结束),退出循环break;}}// 创建一个大小为 k * n 的一维数组,用于模拟窗口矩阵const windows = new Array(k * n);// 当前填充位置的索引let idx = 0;// 当前从第 level 个列表中取值let level = 0;// 当窗口矩阵未填满时,继续填充while (idx < windows.length) {// 标记当前轮次是否发生了“借”动作let flag = false;// 从当前列表 lists[level] 中取 n 个元素for (let i = 0; i < n; i++) {// 将列表的第一个元素取出,放入 windows 中windows[idx++] = lists[level].shift();// 如果当前列表为空且还有其他列表,则从下一个列表中“借”元素if (lists[level].length == 0 && lists.length > 1) {// 删除当前空列表lists.splice(level, 1);// 防止 level 越界level %= lists.length;// 标记发生了“借”动作flag = true;}}// 如果没有发生“借”动作,则切换到下一个列表if (!flag) {level = (level + 1) % lists.length; // 防止越界}}// 用于存储最终结果的数组const ans = [];// 按列遍历窗口矩阵for (let j = 0; j < n; j++) {// 按行遍历窗口矩阵for (let i = 0; i < k; i++) {// 按列收集元素,存入 ansans.push(windows[i * n + j]);}}// 输出结果,元素之间用空格分隔console.log(ans.join(" "));
})();
代码逻辑详解
1. 输入处理
- 使用
readline
模块逐行读取输入。 - 第一行读取窗口数量
n
,第二行读取每个窗口的元素数量k
。 - 后续行读取每个列表,并将其转换为数字数组,存储在
lists
中。
2. 窗口矩阵填充
- 创建一个大小为
k * n
的一维数组windows
,用于模拟窗口矩阵。 - 使用指针
idx
记录当前填充的位置。 - 使用变量
level
记录当前从哪个列表中取值。
3. 填充逻辑
- 从当前列表
lists[level]
中取出n
个元素,依次填充到windows
中。 - 如果当前列表元素不足,则从下一个列表中“借”元素,并删除当前空列表。
- 更新指针
idx
和level
,确保填充过程正确。
4. 输出结果
- 按列遍历
windows
矩阵,将元素按窗口顺序收集到ans
数组中。 - 最终输出
ans
,元素之间用空格分隔。
示例运行
输入
4
7
0 1 2 3 4 5 6 7 8 9
10 11 12 13 14 15 16 17 18 19
20 21 22 23 24 25 26 27 28 29
输出
0 10 20 4 14 24 8 1 11 21 5 15 25 9 2 12 22 6 16 26 18 3 13 23 7 17 27 19
解析
- 从第一个列表中选择 4 条元素
0 1 2 3
,分别放到 4 个窗口中。 - 从第二个列表中选择 4 条元素
10 11 12 13
,分别放到 4 个窗口中。 - 从第三个列表中选择 4 条元素
20 21 22 23
,分别放到 4 个窗口中。 - 再次从第一个列表中选择 4 条元素
4 5 6 7
,分别放到 4 个窗口中。 - 从第一个列表中选择剩下的 2 条元素
8 9
,分别放到窗口1和窗口2。 - 从第二个列表中选择剩下的 2 条元素
18 19
,分别放到窗口3和窗口4。
最终输出的元素列表按照窗口顺序排列。
通过以上注释和讲解,你应该能够理解代码的每一部分功能和实现逻辑。如果还有疑问,欢迎随时提问!
三、Java算法源码
代码结构
-
输入处理:
- 使用
Scanner
读取输入数据。 - 解析窗口数量
n
和每个窗口的元素数量k
。 - 读取多个列表,并将每个列表转换为
LinkedList<Integer>
,存储在lists
中。
- 使用
-
窗口矩阵填充:
- 创建一个一维数组
windows
,用于模拟k
行n
列的窗口矩阵。 - 使用指针
idx
记录当前填充的位置。 - 使用变量
level
记录当前从哪个列表中取值。
- 创建一个一维数组
-
填充逻辑:
- 从当前列表
lists.get(level)
中取出n
个元素,依次填充到windows
中。 - 如果当前列表元素不足,则从下一个列表中“借”元素。
- 更新指针
idx
和level
,确保填充过程正确。
- 从当前列表
-
输出结果:
- 使用
StringJoiner
按列遍历windows
矩阵,将元素按窗口顺序拼接成字符串。 - 最终输出结果。
- 使用
代码逐行注释
import java.util.*;public class Main {public static void main(String[] args) {// 创建 Scanner 对象,用于读取输入Scanner sc = new Scanner(System.in);// 读取窗口数量 nint n = Integer.parseInt(sc.nextLine());// 读取每个窗口的元素数量 kint k = Integer.parseInt(sc.nextLine());// 存储所有列表,每个列表用 LinkedList 存储ArrayList<LinkedList<Integer>> lists = new ArrayList<>();// 循环读取输入,直到输入结束while (sc.hasNextLine()) {String line = sc.nextLine();// 本地测试,以空行作为输入截止条件if (line.length() == 0) break;// 将输入行按空格分割,转换为 Integer 数组Integer[] nums =Arrays.stream(line.split(" ")).map(Integer::parseInt).toArray(Integer[]::new);// 将数组转换为 LinkedList,并存入 listslists.add(new LinkedList<>(Arrays.asList(nums)));}// 创建一个大小为 k * n 的一维数组,用于模拟窗口矩阵int[] windows = new int[k * n];// 当前填充位置的索引int idx = 0;// 当前从第 level 个列表中取值int level = 0;// 当窗口矩阵未填满时,继续填充while (idx < windows.length) {// 标记当前轮次是否发生了“借”动作boolean flag = false;// 从当前列表 lists.get(level) 中取 n 个元素for (int i = 0; i < n; i++) {// 将列表的第一个元素取出,放入 windows 中windows[idx++] = lists.get(level).removeFirst();// 如果当前列表为空且还有其他列表,则从下一个列表中“借”元素if (lists.get(level).size() == 0 && lists.size() > 1) {// 删除当前空列表lists.remove(level);// 防止 level 越界level %= lists.size();// 标记发生了“借”动作flag = true;}}// 如果没有发生“借”动作,则切换到下一个列表if (!flag) {level = (level + 1) % lists.size(); // 防止越界}}// 使用 StringJoiner 拼接结果,元素之间用空格分隔StringJoiner sj = new StringJoiner(" ");// 按列遍历窗口矩阵for (int j = 0; j < n; j++) { // 遍历列号for (int i = 0; i < k; i++) { // 遍历行号// 按列收集元素,拼接成字符串sj.add(windows[i * n + j] + "");}}// 输出结果System.out.println(sj);}
}
代码逻辑详解
1. 输入处理
- 使用
Scanner
逐行读取输入。 - 第一行读取窗口数量
n
,第二行读取每个窗口的元素数量k
。 - 后续行读取每个列表,并将其转换为
LinkedList<Integer>
,存储在lists
中。
2. 窗口矩阵填充
- 创建一个大小为
k * n
的一维数组windows
,用于模拟窗口矩阵。 - 使用指针
idx
记录当前填充的位置。 - 使用变量
level
记录当前从哪个列表中取值。
3. 填充逻辑
- 从当前列表
lists.get(level)
中取出n
个元素,依次填充到windows
中。 - 如果当前列表元素不足,则从下一个列表中“借”元素,并删除当前空列表。
- 更新指针
idx
和level
,确保填充过程正确。
4. 输出结果
- 使用
StringJoiner
按列遍历windows
矩阵,将元素按窗口顺序拼接成字符串。 - 最终输出结果。
示例运行
输入
4
7
0 1 2 3 4 5 6 7 8 9
10 11 12 13 14 15 16 17 18 19
20 21 22 23 24 25 26 27 28 29
输出
0 10 20 4 14 24 8 1 11 21 5 15 25 9 2 12 22 6 16 26 18 3 13 23 7 17 27 19
解析
- 从第一个列表中选择 4 条元素
0 1 2 3
,分别放到 4 个窗口中。 - 从第二个列表中选择 4 条元素
10 11 12 13
,分别放到 4 个窗口中。 - 从第三个列表中选择 4 条元素
20 21 22 23
,分别放到 4 个窗口中。 - 再次从第一个列表中选择 4 条元素
4 5 6 7
,分别放到 4 个窗口中。 - 从第一个列表中选择剩下的 2 条元素
8 9
,分别放到窗口1和窗口2。 - 从第二个列表中选择剩下的 2 条元素
18 19
,分别放到窗口3和窗口4。
最终输出的元素列表按照窗口顺序排列。
通过以上注释和讲解,你应该能够理解代码的每一部分功能和实现逻辑。如果还有疑问,欢迎随时提问!
四、Python算法源码
以下是 Python 代码的详细注释和讲解,帮助你理解每一部分的功能和实现逻辑:
代码结构
-
输入处理:
- 使用
input()
读取输入数据。 - 解析窗口数量
n
和每个窗口的元素数量k
。 - 读取多个列表,并将每个列表转换为整数列表,存储在
lists
中。
- 使用
-
窗口矩阵填充:
- 创建一个一维列表
windows
,用于模拟k
行n
列的窗口矩阵。 - 使用指针
idx
记录当前填充的位置。 - 使用变量
level
记录当前从哪个列表中取值。
- 创建一个一维列表
-
填充逻辑:
- 从当前列表
lists[level]
中取出n
个元素,依次填充到windows
中。 - 如果当前列表元素不足,则从下一个列表中“借”元素。
- 更新指针
idx
和level
,确保填充过程正确。
- 从当前列表
-
输出结果:
- 按列遍历
windows
矩阵,将元素按窗口顺序收集到ans
列表中。 - 最终将
ans
转换为字符串并输出。
- 按列遍历
代码逐行注释
# 读取窗口数量 n
n = int(input())
# 读取每个窗口的元素数量 k
k = int(input())# 存储所有列表
lists = []
while True:try:# 读取一行输入,并将其转换为整数列表,存入 listslists.append(list(map(int, input().split())))except:# 捕获异常(如输入结束),退出循环break# 算法入口
def getResult():# 创建一个大小为 k * n 的一维列表,用于模拟窗口矩阵windows = [0] * (k * n)# 当前填充位置的索引idx = 0# 当前从第 level 个列表中取值level = 0# 当窗口矩阵未填满时,继续填充while idx < len(windows):# 标记当前轮次是否发生了“借”动作flag = False# 从当前列表 lists[level] 中取 n 个元素for _ in range(n):# 将列表的第一个元素取出,放入 windows 中windows[idx] = lists[level].pop(0)idx += 1# 如果当前列表为空且还有其他列表,则从下一个列表中“借”元素if len(lists[level]) == 0 and len(lists) > 1:# 删除当前空列表lists.pop(level)# 防止 level 越界level %= len(lists)# 标记发生了“借”动作flag = True# 如果没有发生“借”动作,则切换到下一个列表if not flag:level = (level + 1) % len(lists) # 防止越界# 用于存储最终结果的列表ans = []# 按列遍历窗口矩阵for j in range(n): # 遍历列号for i in range(k): # 遍历行号# 按列收集元素,存入 ansans.append(windows[i * n + j])# 将结果列表转换为字符串,元素之间用空格分隔return " ".join(map(str, ans))# 调用算法并输出结果
print(getResult())
代码逻辑详解
1. 输入处理
- 使用
input()
逐行读取输入。 - 第一行读取窗口数量
n
,第二行读取每个窗口的元素数量k
。 - 后续行读取每个列表,并将其转换为整数列表,存储在
lists
中。
2. 窗口矩阵填充
- 创建一个大小为
k * n
的一维列表windows
,用于模拟窗口矩阵。 - 使用指针
idx
记录当前填充的位置。 - 使用变量
level
记录当前从哪个列表中取值。
3. 填充逻辑
- 从当前列表
lists[level]
中取出n
个元素,依次填充到windows
中。 - 如果当前列表元素不足,则从下一个列表中“借”元素,并删除当前空列表。
- 更新指针
idx
和level
,确保填充过程正确。
4. 输出结果
- 按列遍历
windows
矩阵,将元素按窗口顺序收集到ans
列表中。 - 最终将
ans
转换为字符串并输出。
示例运行
输入
4
7
0 1 2 3 4 5 6 7 8 9
10 11 12 13 14 15 16 17 18 19
20 21 22 23 24 25 26 27 28 29
输出
0 10 20 4 14 24 8 1 11 21 5 15 25 9 2 12 22 6 16 26 18 3 13 23 7 17 27 19
解析
- 从第一个列表中选择 4 条元素
0 1 2 3
,分别放到 4 个窗口中。 - 从第二个列表中选择 4 条元素
10 11 12 13
,分别放到 4 个窗口中。 - 从第三个列表中选择 4 条元素
20 21 22 23
,分别放到 4 个窗口中。 - 再次从第一个列表中选择 4 条元素
4 5 6 7
,分别放到 4 个窗口中。 - 从第一个列表中选择剩下的 2 条元素
8 9
,分别放到窗口1和窗口2。 - 从第二个列表中选择剩下的 2 条元素
18 19
,分别放到窗口3和窗口4。
最终输出的元素列表按照窗口顺序排列。
通过以上注释和讲解,你应该能够理解代码的每一部分功能和实现逻辑。如果还有疑问,欢迎随时提问!
五、C/C++算法源码:
以下是 C 语言和 C++ 代码的详细注释和讲解,帮助你理解每一部分的功能和实现逻辑:
C 语言代码
代码结构
-
链表实现:
- 定义链表节点
Node
和链表Link
结构。 - 实现链表的创建、尾插、头删等操作。
- 定义链表节点
-
输入处理:
- 使用
scanf
和gets
读取输入数据。 - 解析窗口数量
n
和每个窗口的元素数量k
。 - 读取多个列表,并将每个列表转换为链表,存储在
lists
数组中。
- 使用
-
窗口矩阵填充:
- 创建一个一维数组
windows
,用于模拟k
行n
列的窗口矩阵。 - 使用指针
idx
记录当前填充的位置。 - 使用变量
level
记录当前从哪个列表中取值。
- 创建一个一维数组
-
填充逻辑:
- 从当前列表
lists[level]
中取出n
个元素,依次填充到windows
中。 - 如果当前列表元素不足,则从下一个列表中“借”元素。
- 更新指针
idx
和level
,确保填充过程正确。
- 从当前列表
-
输出结果:
- 按列遍历
windows
矩阵,将元素按窗口顺序输出。
- 按列遍历
C 语言代码逐行注释
#include <stdio.h>
#include <string.h>
#include <stdlib.h>#define MAX_ROWS 100 // 最大列表数量
#define MAX_ROW_LEN 10000 // 每行最大长度/* 链表节点 */
typedef struct Node {int val; // 节点值struct Node *next; // 下一个节点指针
} Node;/* 链表 */
typedef struct Link {int size; // 链表大小Node *head; // 链表头节点Node *tail; // 链表尾节点
} Link;// 创建链表
Link *new_Link() {Link *link = (Link *) malloc(sizeof(Link));link->size = 0;link->head = NULL;link->tail = NULL;return link;
}// 尾插
void addLast_Link(Link *link, int val) {Node *node = (Node *) malloc(sizeof(Node));node->val = val;node->next = NULL;if (link->size == 0) {link->head = node;link->tail = node;} else {link->tail->next = node;link->tail = node;}link->size++;
}// 头删
int removeFirst_Link(Link *link) {if (link->size == 0) exit(-1);Node *removed = link->head;if (link->size == 1) {link->head = NULL;link->tail = NULL;} else {link->head = link->head->next;}link->size--;int val = removed->val;free(removed);return val;
}int main() {int n, k;scanf("%d %d", &n, &k); // 读取窗口数量 n 和每个窗口的元素数量 kgetchar(); // 消耗换行符Link *lists[MAX_ROWS]; // 存储所有列表int lists_size = 0; // 当前列表数量char s[MAX_ROW_LEN];while (gets(s)) { // 逐行读取输入// 本地测试,以空行作为输入截止条件if (strlen(s) == 0) break;Link *link = new_Link(); // 创建一个新链表// 将输入行按空格分割,转换为链表节点char *token = strtok(s, " ");while (token != NULL) {addLast_Link(link, atoi(token)); // 将节点添加到链表尾部token = strtok(NULL, " ");}lists[lists_size++] = link; // 将链表存入 lists}// 创建一个大小为 k * n 的一维数组,用于模拟窗口矩阵int windows[k * n];// 当前填充位置的索引int idx = 0;// 当前从第 level 个列表中取值int level = 0;// 当窗口矩阵未填满时,继续填充while (idx < k * n) {// 标记当前轮次是否发生了“借”动作int flag = 0;// 从当前列表 lists[level] 中取 n 个元素for (int i = 0; i < n; i++) {windows[idx++] = removeFirst_Link(lists[level]); // 取出元素并填充到 windows// 如果当前列表为空且还有其他列表,则从下一个列表中“借”元素if (lists[level]->size == 0 && lists_size > 1) {// 删除第 level 个空列表for (int j = level + 1; j < lists_size; j++) {lists[j - 1] = lists[j];}lists_size--;level %= lists_size; // 防止越界flag = 1; // 标记发生了“借”动作}}// 如果没有发生“借”动作,则切换到下一个列表if (!flag) {level = (level + 1) % lists_size; // 防止越界}}// 按列遍历窗口矩阵for (int j = 0; j < n; j++) { // 遍历列号for (int i = 0; i < k; i++) { // 遍历行号// 按列打印元素printf("%d", windows[i * n + j]);if (j != n - 1 || i != k - 1) {printf(" ");}}}return 0;
}
C++ 代码
代码结构
-
输入处理:
- 使用
cin
读取输入数据。 - 解析窗口数量
n
和每个窗口的元素数量k
。 - 读取多个列表,并将每个列表存储在
vector<deque<int>>
中。
- 使用
-
窗口矩阵填充:
- 创建一个一维数组
windows
,用于模拟k
行n
列的窗口矩阵。 - 使用指针
idx
记录当前填充的位置。 - 使用变量
level
记录当前从哪个列表中取值。
- 创建一个一维数组
-
填充逻辑:
- 从当前列表
lists[level]
中取出n
个元素,依次填充到windows
中。 - 如果当前列表元素不足,则从下一个列表中“借”元素。
- 更新指针
idx
和level
,确保填充过程正确。
- 从当前列表
-
输出结果:
- 按列遍历
windows
矩阵,将元素按窗口顺序输出。
- 按列遍历
C++ 代码逐行注释
#include <iostream>
#include <vector>
#include <deque>
#include <sstream>
using namespace std;int main() {int n, k;cin >> n >> k; // 读取窗口数量 n 和每个窗口的元素数量 kcin.ignore(); // 忽略换行符vector<deque<int>> lists; // 存储所有列表string s;while (getline(cin, s)) { // 逐行读取输入if (s.empty()) break; // 本地测试,以空行作为输入截止条件deque<int> list; // 创建一个新列表stringstream ss(s);int num;while (ss >> num) { // 将输入行按空格分割,转换为整数list.push_back(num); // 将元素添加到列表尾部}lists.push_back(list); // 将列表存入 lists}// 创建一个大小为 k * n 的一维数组,用于模拟窗口矩阵vector<int> windows(k * n);// 当前填充位置的索引int idx = 0;// 当前从第 level 个列表中取值int level = 0;// 当窗口矩阵未填满时,继续填充while (idx < k * n) {// 标记当前轮次是否发生了“借”动作bool flag = false;// 从当前列表 lists[level] 中取 n 个元素for (int i = 0; i < n; i++) {windows[idx++] = lists[level].front(); // 取出元素并填充到 windowslists[level].pop_front(); // 删除已取出的元素// 如果当前列表为空且还有其他列表,则从下一个列表中“借”元素if (lists[level].empty() && lists.size() > 1) {lists.erase(lists.begin() + level); // 删除第 level 个空列表level %= lists.size(); // 防止越界flag = true; // 标记发生了“借”动作}}// 如果没有发生“借”动作,则切换到下一个列表if (!flag) {level = (level + 1) % lists.size(); // 防止越界}}// 按列遍历窗口矩阵for (int j = 0; j < n; j++) { // 遍历列号for (int i = 0; i < k; i++) { // 遍历行号// 按列打印元素cout << windows[i * n + j];if (j != n - 1 || i != k - 1) {cout << " ";}}}return 0;
}
总结
- C 语言代码:使用链表和数组实现,适合对内存管理有较高要求的场景。
- C++ 代码:使用
vector
和deque
实现,代码更简洁,适合快速开发。
两种代码的逻辑完全一致,只是实现方式不同。如果你有更多问题,欢迎随时提问!
六、尾言
什么是华为OD?
华为OD(Outsourcing Developer,外包开发工程师)是华为针对软件开发工程师岗位的一种招聘形式,主要包括笔试、技术面试以及综合面试等环节。尤其在笔试部分,算法题的机试至关重要。
为什么刷题很重要?
-
机试是进入技术面的第一关:
华为OD机试(常被称为机考)主要考察算法和编程能力。只有通过机试,才能进入后续的技术面试环节。 -
技术面试需要手撕代码:
技术一面和二面通常会涉及现场编写代码或算法题。面试官会注重考察候选人的思路清晰度、代码规范性以及解决问题的能力。因此提前刷题、多练习是通过面试的重要保障。 -
入职后的可信考试:
入职华为后,还需要通过“可信考试”。可信考试分为三个等级:- 入门级:主要考察基础算法与编程能力。
- 工作级:更贴近实际业务需求,可能涉及复杂的算法或与工作内容相关的场景题目。
- 专业级:最高等级,考察深层次的算法以及优化能力,与薪资直接挂钩。
刷题策略与说明:
2024年8月14日之后,华为OD机试的题库转为 E卷,由往年题库(D卷、A卷、B卷、C卷)和全新题目组成。刷题时可以参考以下策略:
-
关注历年真题:
- 题库中的旧题占比较大,建议优先刷历年的A卷、B卷、C卷、D卷题目。
- 对于每道题目,建议深度理解其解题思路、代码实现,以及相关算法的适用场景。
-
适应新题目:
- E卷中包含全新题目,需要掌握全面的算法知识和一定的灵活应对能力。
- 建议关注新的刷题平台或交流群,获取最新题目的解析和动态。
-
掌握常见算法:
华为OD考试通常涉及以下算法和数据结构:- 排序算法(快速排序、归并排序等)
- 动态规划(背包问题、最长公共子序列等)
- 贪心算法
- 栈、队列、链表的操作
- 图论(最短路径、最小生成树等)
- 滑动窗口、双指针算法
-
保持编程规范:
- 注重代码的可读性和注释的清晰度。
- 熟练使用常见编程语言,如C++、Java、Python等。
如何获取资源?
-
官方参考:
- 华为招聘官网或相关的招聘平台会有一些参考信息。
- 华为OD的相关公众号可能也会发布相关的刷题资料或学习资源。
-
加入刷题社区:
- 找到可信的刷题交流群,与其他备考的小伙伴交流经验。
- 关注知名的刷题网站,如LeetCode、牛客网等,这些平台上有许多华为OD的历年真题和解析。
-
寻找系统性的教程:
- 学习一本经典的算法书籍,例如《算法导论》《剑指Offer》《编程之美》等。
- 完成系统的学习课程,例如数据结构与算法的在线课程。
积极心态与持续努力:
刷题的过程可能会比较枯燥,但它能够显著提升编程能力和算法思维。无论是为了通过华为OD的招聘考试,还是为了未来的职业发展,这些积累都会成为重要的财富。
考试注意细节
-
本地编写代码
- 在本地 IDE(如 VS Code、PyCharm 等)上编写、保存和调试代码,确保逻辑正确后再复制粘贴到考试页面。这样可以减少语法错误,提高代码准确性。
-
调整心态,保持冷静
- 遇到提示不足或实现不确定的问题时,不必慌张,可以采用更简单或更有把握的方法替代,确保思路清晰。
-
输入输出完整性
- 注意训练和考试时都需要编写完整的输入输出代码,尤其是和题目示例保持一致。完成代码后务必及时调试,确保功能符合要求。
-
快捷键使用
- 删除行可用
Ctrl+D
,复制、粘贴和撤销分别为Ctrl+C
,Ctrl+V
,Ctrl+Z
,这些可以正常使用。 - 避免使用
Ctrl+S
,以免触发浏览器的保存功能。
- 删除行可用
-
浏览器要求
- 使用最新版的 Google Chrome 浏览器完成考试,确保摄像头开启并正常工作。考试期间不要切换到其他网站,以免影响考试成绩。
-
交卷相关
- 答题前,务必仔细查看题目示例,避免遗漏要求。
- 每完成一道题后,点击【保存并调试】按钮,多次保存和调试是允许的,系统会记录得分最高的一次结果。完成所有题目后,点击【提交本题型】按钮。
- 确保在考试结束前提交试卷,避免因未保存或调试失误而丢分。
-
时间和分数安排
- 总时间:150 分钟;总分:400 分。
- 试卷结构:2 道一星难度题(每题 100 分),1 道二星难度题(200 分)。及格分为 150 分。合理分配时间,优先完成自己擅长的题目。
-
考试环境准备
- 考试前请备好草稿纸和笔。考试中尽量避免离开座位,确保监控画面正常。
- 如需上厕所,请提前规划好时间以减少中途离开监控的可能性。
-
技术问题处理
- 如果考试中遇到断电、断网、死机等技术问题,可以关闭浏览器并重新打开试卷链接继续作答。
- 出现其他问题,请第一时间联系 HR 或监考人员进行反馈。
祝你考试顺利,取得理想成绩!