【华为笔试题汇总】2024-05-15-华为春招笔试题-三语言题解(Python/Java/Cpp)

🍭 大家好这里是清隆学长 ,一枚热爱算法的程序员

✨ 本系列打算持续跟新华为近期的春秋招笔试题汇总~

💻 ACM银牌🥈| 多次AK大厂笔试 | 编程一对一辅导

👏 感谢大家的订阅➕ 和 喜欢💗

📧 清隆这边最近正在收集近一年互联网各厂的笔试题汇总,如果有需要的小伙伴可以关注后私信一下 清隆领取,会在飞书进行同步的跟新。

01.K小姐的图书馆管理系统

问题描述

K小姐是某大学图书馆的管理员,她正在设计一个高效的图书管理系统。在这个系统中,需要实现一个缓存管理策略来提高图书的访问效率。这个缓存管理策略使用的是 LRU(最近最少使用)算法。

LRU 缓存算法是一种常用于管理缓存的策略,其目标是保留最近使用过的数据,淘汰最久未被使用的数据。实现一个简单的 LRU 缓存算法,支持查询、插入和删除操作。

最久未被使用的定义:查询、插入和删除操作均为一次访问操作,每个元素均有一个最后一次被访问时间,按照最后一次被访问时间排序,时间最早的即为最久未使用。插入操作:当缓存中已经存在,则刷新值;不存在,则插入;如果超过上限,则淘汰最久未被使用的元素。

输入格式

第一行包含两个整数 N N N K K K,分别表示缓存内最多可以存放的页数,以及操作序列中的总操作数。其中 N ∈ [ 1 , 100 ] N \in [1,100] N[1,100] K ∈ [ 1 , 10000 ] K \in [1,10000] K[1,10000]

接下来的 K K K 行,每行两个输入,以空格分隔。第一个输入是一个字符,表示操作类型: A A A 表示插入, Q Q Q 表示查询, D D D 表示删除。第二个输入是一个整数,表示页面编号。其中页面编号的范围为 [ 1 , 100000 ] [1,100000] [1,100000]

输出格式

输出一行,表示缓存内各页面的编号,按照从小到大排序。

样例输入

2 5
A 103
A 102
A 102
Q 103
A 101

样例输出

101 103

数据范围

  • 操作数 K K K 的范围: 1 ≤ K ≤ 10000 1 \leq K \leq 10000 1K10000
  • 缓存页数 N N N 的范围: 1 ≤ N ≤ 100 1 \leq N \leq 100 1N100
  • 页面编号的范围: 1 ≤ 页面编号 ≤ 100000 1 \leq 页面编号 \leq 100000 1页面编号100000

题解

这道题使用双向链表和哈希表来实现 LRU 缓存算法。

首先,使用一个双向链表来维护页面的访问顺序。链表头部表示最近使用的页面,尾部表示最久未使用的页面。每次查询或插入页面时,都将该页面移动到链表头部。

为了加快页面的查找速度,我们使用一个哈希表来存储页面编号和对应的链表节点。这样每次查找、插入和删除操作的时间复杂度都是 O ( 1 ) O(1) O(1)

当插入新的页面时,如果缓存已满,需要删除链表尾部的页面(即最久未使用的页面),然后将新页面插入到链表头部。

具体实现请参考以下代码:

参考代码

  • Python
class DoubleLinkedNode:
    def __init__(self, key=0, value=0):
        self.key = key
        self.value = value
        self.prev = None
        self.next = None

class LRUCache:
    def __init__(self, cap):
        self.cache = dict()
        self.head = DoubleLinkedNode()
        self.tail = DoubleLinkedNode()
        self.head.next = self.tail
        self.tail.prev = self.head
        self.cap = cap
        self.size = 0

    def output(self):
        temp = self.head.next
        ans = []
        while temp and temp != self.tail:
            ans.append(temp.value)
            temp = temp.next
        print(*sorted(ans))

    def get(self, key):
        if key not in self.cache:
            return -1
        node = self.cache[key]
        self.move_to_head(node)
        return node.value

    def put(self, key, value):
        if key not in self.cache:
            node = DoubleLinkedNode(key, value)
            self.cache[key] = node
            self.add_to_head(node)
            self.size += 1
            if self.size > self.cap:
                removed = self.remove_tail()
                self.cache.pop(removed.key)
                self.size -= 1
        else:
            node = self.cache[key]
            node.value = value
            self.move_to_head(node)

    def add_to_head(self, node):
        node.prev = self.head
        node.next = self.head.next
        self.head.next.prev = node
        self.head.next = node

    def remove_node(self, node):
        node.prev.next = node.next
        node.next.prev = node.prev

    def move_to_head(self, node):
        self.remove_node(node)
        self.add_to_head(node)

    def remove_tail(self):
        node = self.tail.prev
        self.remove_node(node)
        return node

ans = []
cap, q = map(int, input().split())
lrucache = LRUCache(cap)

for i in range(q):
    line = input().split()
    op, key = line[0], int(line[1])
    if op == 'Q':
        lrucache.get(key)
    else:
        lrucache.put(key, key)

lrucache.output()
  • Java
import java.util.*;

class DoubleLinkedNode {
    int key;
    int value;
    DoubleLinkedNode prev;
    DoubleLinkedNode next;
    public DoubleLinkedNode() {}
    public DoubleLinkedNode(int key, int value) {
        this.key = key;
        this.value = value;
    }
}

class LRUCache {
    private Map<Integer, DoubleLinkedNode> cache = new HashMap<>();
    private int size;
    private int capacity;
    private DoubleLinkedNode head, tail;

    public LRUCache(int capacity) {
        this.size = 0;
        this.capacity = capacity;
        head = new DoubleLinkedNode();
        tail = new DoubleLinkedNode();
        head.next = tail;
        tail.prev = head;
    }

    public void output() {
        List<Integer> ans = new ArrayList<>();
        DoubleLinkedNode temp = head.next;
        while (temp != null && temp != tail) {
            ans.add(temp.value);
            temp = temp.next;
        }
        Collections.sort(ans);
        for (int val : ans) {
            System.out.print(val + " ");
        }
    }

    public int get(int key) {
        DoubleLinkedNode node = cache.get(key);
        if (node == null) {
            return -1;
        }
        moveToHead(node);
        return node.value;
    }

    public void put(int key, int value) {
        DoubleLinkedNode node = cache.get(key);
        if (node == null) {
            DoubleLinkedNode newNode = new DoubleLinkedNode(key, value);
            cache.put(key, newNode);
            addToHead(newNode);
            size++;
            if (size > capacity) {
                DoubleLinkedNode tail = removeTail();
                cache.remove(tail.key);
                size--;
            }
        } else {
            node.value = value;
            moveToHead(node);
        }
    }

    private void addToHead(DoubleLinkedNode node) {
        node.prev = head;
        node.next = head.next;
        head.next.prev = node;
        head.next = node;
    }

    private void removeNode(DoubleLinkedNode node) {
        node.prev.next = node.next;
        node.next.prev = node.prev;
    }

    private void moveToHead(DoubleLinkedNode node) {
        removeNode(node);
        addToHead(node);
    }

    private DoubleLinkedNode removeTail() {
        DoubleLinkedNode res = tail.prev;
        removeNode(res);
        return res;
    }
}

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int capacity = scanner.nextInt();
        int queryCount = scanner.nextInt();
        LRUCache lruCache = new LRUCache(capacity);

        for (int i = 0; i < queryCount; i++) {
            String op = scanner.next();
            int key = scanner.nextInt();
            if (op.equals("Q")) {
                lruCache.get(key);
            } else {
                lruCache.put(key, key);
            }
        }
        lruCache.output();
    }
}
#include <iostream>
#include <unordered_map>
#include <list>
#include <vector>
#include <algorithm>

using namespace std;

class LRUCache {
public:
    LRUCache(int capacity) : capacity(capacity) {}

    void put(int key, int value) {
        auto it = cache.find(key);
        if (it != cache.end()) {
            cache.erase(it);
            keys.remove(key);
        } else if (keys.size() >= capacity) {
            int lru = keys.back();
            keys.pop_back();
            cache.erase(lru);
        }
        keys.push_front(key);
        cache[key] = { value, keys.begin() };
    }

    int get(int key) {
        auto it = cache.find(key);
        if (it == cache.end()) return -1;
        keys.erase(it->second.second);
        keys.push_front(key);
        cache[key] = { it->second.first, keys.begin() };
        return it->second.first;
    }

    void output() {
        vector<int> res(keys.begin(), keys.end());
        sort(res.begin(), res.end());
        for (int val : res) {
            cout << val << " ";
        }
        cout << endl;
    }

private:
    int capacity;
    list<int> keys;
    unordered_map<int, pair<int, list<int>::iterator>> cache;
};

int main() {
    int capacity, queryCount;
    cin >> capacity >> queryCount;
    LRUCache lruCache(capacity);

    for (int i = 0; i < queryCount; ++i) {
        char op;
        int key;
        cin >> op >> key;
        if (op == 'Q') {
            lruCache.get(key);
        } else {
            lruCache.put(key, key);
        }
    }
    lruCache.output();
    return 0;
}

02.K 小姐的正则表达式实验

问题描述

K 小姐是一名计算机专业的大学生,这学期她选修了一门编译原理课程。这门课的实验作业十分繁重,其中有一个实验要求利用正则表达式对文本进行切词处理。已知存在一种字符串解析语法,其中的语法元素如下:

  • N:用于匹配单个数字( 0 0 0- 9 9 9)。
  • A:用于匹配单个字母( a a a- z z z A A A- Z Z Z)。
  • n():用于表示一个分组,分组中至少有一个 N 语法元素或者 A 语法元素, n n n 为一个数值,表示匹配 n n n 次, 1 ≤ n ≤ 200 1 \leq n \leq 200 1n200

给定解析语法和目标字符串,要求从目标字符串中找到第一个满足解析语法的子串。

输入格式

输入两行数据,第一行是给定的解析语法,第二行是目标字符串。

输出格式

输出匹配的子字符串内容,如果没有匹配中任何字符串,输出 !(英文感叹号)。

样例输入

2(AN)
BA3A3ABB

样例输出

A3A3

样例输入

2(A2(N))
A3322A33P20BB

样例输出

A33P20

数据范围

  • 解析语法中的 n n n 满足 1 ≤ n ≤ 200 1 \leq n \leq 200 1n200
  • 目标字符串的长度不超过 1 0 5 10^5 105

题解

这道题目要求我们根据给定的解析语法,从目标字符串中找到第一个满足条件的子串。解析语法中包含了数字和字母的匹配规则,以及分组和重复的规则。我们需要先将解析语法转换成一个具体的正则表达式,然后在目标字符串中进行匹配。

具体步骤如下:

  1. 解析输入的解析语法,将其转换成一个具体的正则表达式。
  2. 使用转换后的正则表达式在目标字符串中进行匹配。
  3. 输出第一个匹配的子串,如果没有匹配到则输出 !

参考代码

  • Python
import re

def parse_pattern(pattern):
    stack = []
    i = 0
    while i < len(pattern):
        if pattern[i].isdigit():
            num = 0
            while i < len(pattern) and pattern[i].isdigit():
                num = num * 10 + int(pattern[i])
                i += 1
            stack.append(num)
        elif pattern[i] == '(':
            stack.append('(')
            i += 1
        elif pattern[i] == ')':
            sub_pattern = []
            while stack and stack[-1] != '(':
                sub_pattern.append(stack.pop())
            stack.pop()  # pop '('
            sub_pattern.reverse()
            repeat = stack.pop()
            stack.append(''.join(sub_pattern) * repeat)
            i += 1
        else:
            stack.append(pattern[i])
            i += 1
    return ''.join(stack)

def match_pattern(pattern, string):
    regex = parse_pattern(pattern)
    regex = regex.replace('A', '[a-zA-Z]').replace('N', '[0-9]')
    match = re.search(regex, string)
    if match:
        return match.group(0)
    else:
        return '!'

if __name__ == "__main__":
    pattern = input().strip()
    string = input().strip()
    result = match_pattern(pattern, string)
    print(result)
  • Java
import java.util.Scanner;
import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String pattern = scanner.nextLine().trim();
        String string = scanner.nextLine().trim();
        scanner.close();
        
        String regex = parsePattern(pattern);
        regex = regex.replace("A", "[a-zA-Z]").replace("N", "[0-9]");
        
        Pattern compiledPattern = Pattern.compile(regex);
        Matcher matcher = compiledPattern.matcher(string);
        
        if (matcher.find()) {
            System.out.println(matcher.group(0));
        } else {
            System.out.println("!");
        }
    }

    private static String parsePattern(String pattern) {
        Stack<Object> stack = new Stack<>();
        int i = 0;
        while (i < pattern.length()) {
            if (Character.isDigit(pattern.charAt(i))) {
                int num = 0;
                while (i < pattern.length() && Character.isDigit(pattern.charAt(i))) {
                    num = num * 10 + (pattern.charAt(i) - '0');
                    i++;
                }
                stack.push(num);
            } else if (pattern.charAt(i) == '(') {
                stack.push('(');
                i++;
            } else if (pattern.charAt(i) == ')') {
                StringBuilder subPattern = new StringBuilder();
                while (!stack.isEmpty() && stack.peek() instanceof String) {
                    subPattern.insert(0, stack.pop());
                }
                stack.pop();  // pop '('
                int repeat = (int) stack.pop();
                String repeatedPattern = subPattern.toString().repeat(repeat);
                stack.push(repeatedPattern);
                i++;
            } else {
                stack.push(String.valueOf(pattern.charAt(i)));
                i++;
            }
        }
        StringBuilder result = new StringBuilder();
        for (Object obj : stack) {
            result.append(obj);
        }
        return result.toString();
    }
}
  • Cpp
#include <bits/stdc++.h>
using namespace std;

string parsePattern(const string &pattern) {
    stack<int> cnt;
    stack<string> stk;
    string tmp;
    for (int i = 0; i < pattern.size(); ++i) {
        if (isdigit(pattern[i])) {
            int sum = 0;
            while (i < pattern.size() && isdigit(pattern[i])) {
                sum = sum * 10 + pattern[i] - '0';
                ++i;
            }
            cnt.push(sum);
            stk.push(tmp);
            tmp.clear();
        } else if (pattern[i] == ')') {
            string subPattern = tmp;
            tmp = stk.top();
            stk.pop();
            int repeat = cnt.top();
            cnt.pop();
            while (repeat--) {
                tmp += subPattern;
            }
        } else if (pattern[i] != '(') {
            tmp += pattern[i];
        }
    }
    return tmp;
}

bool check(char a, char b) {
    if (b == 'A' && isalpha(a)) return true;
    if (b == 'N' && isdigit(a)) return true;
    return false;
}

int main() {
    string t, s;
    cin >> t >> s;
    string pattern = parsePattern(t);
    pattern = regex_replace(pattern, regex("A"), "[a-zA-Z]");
    pattern = regex_replace(pattern, regex("N"), "[0-9]");
    
    regex re(pattern);
    smatch match;
    if (regex_search(s, match, re)) {
        cout << match.str(0) << endl;
    } else {
        cout << "!" << endl;
    }
    return 0;
}

03.K小姐的网络设备安装挑战

问题描述

K小姐最近接到了一项任务,需要为客户安装两台网络设备。每台设备需要配备接口板才能正常工作,接口板用于接入用户业务,且接口板的转发能力之和不能超过设备的整机转发能力。客户订购了两台设备和若干块接口板。K小姐需要计算是否存在一种安装方法,使得所有接口板刚好能装到两台设备上,且每台设备配置的接口板的转发能力之和刚好等于整机的转发能力。

输入格式

第一行是设备的整机转发能力 m m m,单位为 Gbps。

第二行是客户订购的接口板数量 n n n

第三行是包含 n n n 个接口板的转发能力的列表。

输出格式

如果存在满足要求的安装方法,请分两行输出两台设备配置的接口板的转发能力的列表,且要求每台设备的接口板按转发能力从小到大排列。两台设备的接口板,第一个接口板转发能力小的优先输出。如果第一个接口板转发能力相同,那接口板数量多的优先输出。如果不存在对应的安装方案,则返回 -1。

样例输入

100
5
40 10 10 40 100

样例输出

10 10 40 40
100

数据范围

  • 设备整机转发能力 m m m 的范围为 [1, 2000]。
  • 接口板数量 n n n 的范围为 [1, 200]。
  • 接口板转发能力为正整数。

题解

这道题目可以通过动态规划来解决。我们需要找到一种方法将接口板分成两组,每组的转发能力之和等于设备的整机转发能力。具体步骤如下:

  1. 首先,检查所有接口板的转发能力之和是否等于 2 m 2m 2m,如果不等于,直接返回 -1。
  2. 使用动态规划来判断是否可以将接口板分成两组,每组的转发能力之和等于 m m m
  3. 如果可以分成两组,输出两组接口板的转发能力列表,并按照题目要求进行排序和输出。

其中 dp[j] 表示是否可以通过选择若干个接口板使得它们的转发能力之和为 j j j。初始时,dp = 0,表示不选择任何接口板时,转发能力之和为 0。我们还需要一个二维数组 pre 来记录每个状态的前驱状态,以便于最后回溯找到具体的接口板组合。具体步骤如下:

  1. 初始化 dp 数组和 pre 数组。
  2. 遍历每个接口板,对于每个接口板,更新 dp 数组和 pre 数组。
  3. 最后检查 dp[m] 是否为 -1,如果是,表示无法找到满足条件的组合,返回 -1。
  4. 否则,通过 pre 数组回溯找到具体的接口板组合,并输出结果。

参考代码

  • Python
def solve():
    max_capacity = int(input())
    num_boards = int(input())
    board_capacities = list(map(int, input().split()))
    
    if sum(board_capacities) != 2 * max_capacity:
        print(-1)
        return
    
    dp = [-1] * (max_capacity + 1)
    dp[0] = 0
    pre = [[-1] * (max_capacity + 1) for _ in range(num_boards)]
    
    for i in range(num_boards):
        for j in range(max_capacity, board_capacities[i] - 1, -1):
            if dp[j - board_capacities[i]] != -1:
                dp[j] = i
                pre[i][j] = dp[j - board_capacities[i]]
    
    if dp[max_capacity] == -1:
        print(-1)
        return
    
    used = [False] * num_boards
    i, j = max_capacity, dp[max_capacity]
    while i > 0:
        used[j] = True
        i -= board_capacities[j]
        j = pre[j][i]
    
    device1 = [board_capacities[i] for i in range(num_boards) if used[i]]
    device2 = [board_capacities[i] for i in range(num_boards) if not used[i]]
    
    device1.sort()
    device2.sort()
    
    if device1[0] > device2[0] or (device1[0] == device2[0] and len(device1) < len(device2)):
        device1, device2 = device2, device1
    
    print(" ".join(map(str, device1)))
    print(" ".join(map(str, device2)))

solve()
  • Java
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int maxCapacity = sc.nextInt();
        int numBoards = sc.nextInt();
        int[] boardCapacities = new int[numBoards];
        for (int i = 0; i < numBoards; i++) {
            boardCapacities[i] = sc.nextInt();
        }
        
        if (Arrays.stream(boardCapacities).sum() != 2 * maxCapacity) {
            System.out.println(-1);
            return;
        }
        
        int[] dp = new int[maxCapacity + 1];
        Arrays.fill(dp, -1);
        dp[0] = 0;
        int[][] pre = new int[numBoards][maxCapacity + 1];
        for (int[] row : pre) {
            Arrays.fill(row, -1);
        }
        
        for (int i = 0; i < numBoards; i++) {
            for (int j = maxCapacity; j >= boardCapacities[i]; j--) {
                if (dp[j - boardCapacities[i]] != -1) {
                    dp[j] = i;
                    pre[i][j] = dp[j - boardCapacities[i]];
                }
            }
        }
        
        if (dp[maxCapacity] == -1) {
            System.out.println(-1);
            return;
        }
        
        boolean[] used = new boolean[numBoards];
        int i = maxCapacity, j = dp[maxCapacity];
        while (i > 0) {
            used[j] = true;
            i -= boardCapacities[j];
            j = pre[j][i];
        }
        
        List<Integer> device1 = new ArrayList<>();
        List<Integer> device2 = new ArrayList<>();
        for (int k = 0; k < numBoards; k++) {
            if (used[k]) {
                device1.add(boardCapacities[k]);
            } else {
                device2.add(boardCapacities[k]);
            }
        }
        
        Collections.sort(device1);
        Collections.sort(device2);
        
        if (device1.get(0) > device2.get(0) || (device1.get(0).equals(device2.get(0)) && device1.size() < device2.size())) {
            List<Integer> temp = device1;
            device1 = device2;
            device2 = temp;
        }
        
        for (int k = 0; k < device1.size(); k++) {
            System.out.print(device1.get(k));
            if (k < device1.size() - 1) {
                System.out.print(" ");
            }
        }
        System.out.println();
        for (int k = 0; k < device2.size(); k++) {
            System.out.print(device2.get(k));
            if (k < device2.size() - 1) {
                System.out.print(" ");
            }
        }
        System.out.println();
    }
}

  • Cpp
#include <bits/stdc++.h>
using namespace std;

int main() {
    int max_capacity, num_boards;
    cin >> max_capacity >> num_boards;
    vector<int> board_capacities(num_boards);
    for (int i = 0; i < num_boards; ++i) cin >> board_capacities[i];
    
    if (accumulate(board_capacities.begin(), board_capacities.end(), 0) != max_capacity * 2) {
        cout << -1 << endl;
        return 0;
    }
    
    vector<vector<int>> previous(num_boards, vector<int>(max_capacity + 1, -1));
    vector<int> dp(max_capacity + 1, -1);
    dp[0] = 0;
    
    for (int i = 0; i < num_boards; ++i) {
        for (int j = max_capacity; j >= board_capacities[i]; --j) {
            if (dp[j - board_capacities[i]] != -1) {
                previous[i][j] = dp[j - board_capacities[i]];
                dp[j] = i;
            }
        }
    }
    
    if (dp[max_capacity] == -1) {
        cout << -1 << endl;
        return 0;
    }
    
    vector<bool> used(num_boards, false);
    for (int i = max_capacity, j = dp[max_capacity]; i > 0;) {
        used[j] = true;
        int capacity = board_capacities[j];
        j = previous[j][i];
        i -= capacity;
    }
    
    vector<int> device1, device2;
    for (int i = 0; i < num_boards; ++i) {
        if (used[i]) device1.push_back(board_capacities[i]);
        else device2.push_back(board_capacities[i]);
    }
    
    sort(device1.begin(), device1.end());
    sort(device2.begin(), device2.end());
    
    if (device1[0] > device2[0] || (device1[0] == device2[0] && device1.size() < device2.size())) swap(device1, device2);
    
    for (int i = 0; i < device1.size(); ++i) cout << device1[i] << " \n"[i == device1.size() - 1];
    for (int i = 0; i < device2.size(); ++i) cout << device2[i] << " \n"[i == device2.size() - 1];
    
    return 0;
}

写在最后

📧 清隆这边最近正在收集近一年互联网各厂的笔试题汇总,如果有需要的小伙伴可以关注后私信一下 清隆领取,会在飞书进行同步的跟新。

在这里插入图片描述
在这里插入图片描述

  • 22
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值