51好读  ›  专栏  ›  吴师兄学算法

华为笔试一面!

吴师兄学算法  · 公众号  ·  · 2025-03-10 16:15

正文

请到「今天看啥」查看全文


大家好,我是吴师兄。

题目练习网址:https://www.algomooc.com/problem/X4029

视频讲解回放:https://www.algomooc.com/problem/X4029

下周二,全新的一期大厂算法训练营即将开营,每周定期讲解春招秋招真题,老学员可以继续免费参加,新同学可以添加微信 278166530 咨询

题目描述

小慕是一位工程师,他为主机配置了CPU监控告警系统。他想知道在一段时间内,主机上报了多少次告警。CPU监控数据以字符串数组 dataItems 的形式存储。

告警触发规则如下:如果在最近的时间段 interval 内,监控值连续 limit 次大于等于CPU使用率阈值 threshold ,则会触发告警。如果持续满足告警条件,可以多次上报告警。

请你帮助小慕计算该主机上报告警的总次数。

输入格式

输入分为五行:

第一行:监控数据的总个数 dataItemsLength ,整数,单位为个,例如 3

第二行:监控数据 dataItems ,字符串数组形式,其中 dataItems[i] 是第 i 条监控数据,包括当前CPU使用率值(取值范围 0 ( 100 ,整数)和当前时间戳(取值范围 0 ) 2147483647 ,整数,以升序排列)两部分内容,以英文逗号分隔,例如: 65,1705386526 。元素间以空格分隔,例如: 85,1705386526 80,1705386528 95,1705386530 90,1705386531 92,1705386532

第三行:告警统计时间段 interval ,整数,单位为秒,例如 4 ,表示假定当前时间戳为 1705386532 ,则统计从 1705386529 1705386532 时间范围内的数据,包含两端时间点的数据。

第四行:告警计算阈值 threshold ,整数,单位为百分比,例如 80

第五行:告警上报阈值 limit ,整数,单位为个,例如 2

输入范围说明:

1 ≤ dataItems.length ≤ 86400
1 ≤ interval ≤ 86400
1 ≤ threshold ≤ 100
1 ≤ limit ≤ 100

输出格式

返回该主机上报告警的次数。

样例

输入样例 1

1
85,1705386526
1
80
1

输出样例 1

1

提示 1

在最近1秒内,存在1条监控数据,连续超过CPU阈值80,满足告警触发条件,最终上报1条告警。

输入样例 2

5
85,1705386526 80,1705386528 95,1705386530 90,1705386531 92,1705386532
4
80
2

输出样例 2

4

提示 2

在最近4秒内,存在5条监控数据,连续超过CPU阈值80,其中4条监控数据(第2、3、4、5条)达到告警上报阈值2,满足告警触发条件,最终上报4条告警。

题目解析

以第二个用例为例:

故返回4个结果。

理解题目

本题要求计算主机在一段时间内上报的告警次数。告警的触发条件是:在一个给定的时间段 interval 内,CPU 使用率 CPU_usage 连续 limit 次超过阈值 threshold ,则会触发一次告警。如果在该时间段内持续满足条件,每次满足都会上报告警。

题目给定的输入包括:

  • 监控数据的数量 n
  • 监控数据 dataItems ,其中每个数据由 CPU_usage timestamp 组成。
  • 统计告警的时间段 interval
  • CPU 使用率告警阈值 threshold
  • 连续超过阈值的最小次数 limit

输出需要返回满足告警触发条件的总次数。

使用算法

本题的核心算法是滑动窗口(Sliding Window),结合双指针方法(Two Pointers)。

  • 滑动窗口 :通过移动起始指针 i 和终止指针 j ,找到连续 limit 个满足 CPU_usage >= threshold 的子数组。
  • 双指针 :遍历 dataItems ,当 CPU_usage 超过 threshold 时,利用 j 找到连续的超限区间,并通过 limit interval 计算告警次数。

实现

代码实现思路如下:

  1. 读取输入
    1. 解析输入的 n
    2. 解析 dataItems ,并拆分成 CPU_usage timestamp 存入数组。
    3. 读取 interval threshold limit
  2. 遍历数据,查找满足告警条件的子区间
    1. 使用 i 遍历数据,跳过 CPU_usage 小于 threshold 的数据。
    2. 通过 j 记录 CPU_usage >= threshold 的连续区间。
    3. 计算 limit 位置的时间戳,判断是否满足 interval 约束。
    4. 若满足,则增加告警次数 ans
  3. 输出最终结果

代码

Python

# https://www.algomooc.com/problem/X4029
n = int(input())   # 读取监控数据的总个数
info_str = input().split()  # 读取监控数据,并按照空格分割为字符串列表
info = [list(map(int, s.split(','))) for s in info_str]  # 将字符串转换为整数列表,存储 CPU 使用率和时间戳
interval = int(input())  # 读取告警统计时间段
threshold = int(input())  # 读取告警计算阈值
limit = int(input())  # 读取告警上报阈值
ans = 0# 初始化告警次数

i = 0# 初始化索引,遍历数据
while i < n:
    # 如果当前 CPU 使用率小于阈值,直接跳过
    if info[i][0] < threshold:
        i += 1# 继续检查下一个数据
    else:
        j = i  # 记录当前连续超限的起始索引
        # 统计从 i 开始连续超过 threshold 的数据段
        while j < n and info[j][0] >= threshold:
            j += 1# 找到连续超限的终止位置
        
        # 遍历找到的连续超限数据段,检查是否满足上报告警的条件
        for k in range(i + limit - 1, j):  # 从满足 limit 条件的起始位置开始遍历
            # 检查 k 位置和 k-limit+1 位置的时间戳是否在 interval 内
            if info[k][1] - info[k - limit + 1][1] + 1 <= interval:
                ans += 1# 如果满足条件,告警次数加 1
        
        i = j  # 跳过已经处理的连续超限数据段

print(ans)  # 输出告警次数

C++

// https://www.algomooc.com/problem/X4029
#include 
#include 
#include 

usingnamespacestd;

int main() {
    int n;
    cin >> n; // 读取监控数据的总个数
    cin.ignore(); // 清除换行符
    
    vectorintint>> info; // 存储CPU使用率和时间戳
    string line, temp;
    getline(cin, line); // 读取监控数据字符串
    stringstream ss(line);
    
    while (ss >> temp) {
        size_t pos = temp.find(',');
        int cpuUsage = stoi(temp.substr(0, pos));
        int timestamp = stoi(temp.substr(pos + 1));
        info.emplace_back(cpuUsage, timestamp);
    }
    
    int interval, threshold, limit;
    cin >> interval >> threshold >> limit; // 读取告警统计时间段、计算阈值、上报阈值
    int ans = 0;
    
    int i = 0;
    while (i < n) {
        if (info[i].first < threshold) {
            i++; // 若CPU使用率小于阈值,跳过
        } else {
            int j = i;
            while (j < n && info[j].first >= threshold) {
                j++; // 统计连续超过阈值的数据段
            }
            
            for (int k = i + limit - 1; k < j; k++) {
                if (info[k].second - info[k - limit + 1].second + 1 <= interval) {
                    ans++; // 满足告警条件,增加告警次数
                }
            }
            i = j; // 跳过已经处理的超限数据段
        }
    }
    cout << ans <endl// 输出告警次数
    return0;
}

Java

import java.util.*;

publicclass Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt(); // 读取监控数据的总个数
        scanner.nextLine(); // 清除换行符
        
        List<int[]> info = new ArrayList<>(); // 存储CPU使用率和时间戳
        String[] dataItems = scanner.nextLine().split(" "); // 读取监控数据字符串
        
        for (String item : dataItems) {
            String[] parts = item.split(",");
            int cpuUsage = Integer.parseInt(parts[0]);
            int timestamp = Integer.parseInt(parts[1]);
            info.add(newint[]{cpuUsage, timestamp});
        }
        
        int interval = scanner.nextInt(); // 读取告警统计时间段
        int threshold = scanner.nextInt(); // 读取告警计算阈值
        int limit = scanner.nextInt(); // 读取告警上报阈值
        int ans = 0;
        
        int i = 0;
        while (i < n) {
            if (info.get(i)[0] < threshold) {
                i++; // 若CPU使用率小于阈值,跳过
            } else {
                int j = i;
                while (j < n && info.get(j)[0] >= threshold) {
                    j++; // 统计连续超过阈值的数据段
                }
                
                for (int k = i + limit - 1; k < j; k++) {
                    if (info.get(k)[1] - info.get(k - limit + 1)[1] + 1 <= interval) {
                        ans++; // 满足告警条件,增加告警次数
                    }
                }
                i = j; // 跳过已经处理的超限数据段
            }
        }
        System.out.println(ans); // 输出告警次数
        scanner.close();
    }
}

时空复杂度

时间复杂度: O(n)

空间复杂度: O(n)

相关题目

485. 最大连续 1 的个数 - 力扣(LeetCode)

END

下周二,全新的一期大厂算法训练营即将开营,每周定期讲解春招秋招真题,老学员可以继续免费参加,新同学可以添加微信 278166530 咨询。







请到「今天看啥」查看全文