华为OD机试 - 查找满足条件的数据包 - HashMap + 单调栈(Java 新系统 100分)

在这里插入图片描述

华为OD机试 新系统 题库疯狂收录中,刷题点这里

专栏导读

本专栏收录于《华为OD机试(JAVA)真题》

刷的越多,抽中的概率越大,私信哪吒,备注华为OD,加入华为OD刷题交流群,每一题都有详细的答题思路、详细的代码注释、3个测试用例、为什么这道题采用XX算法、XX算法的适用场景,发现新题目,随时更新,全天CSDN在线答疑。

一、题目描述

在数据包传输系统中,为了优化网络带宽利用率,系统需要对数据包序列进行处理,找出每个数据包右侧第一个满足某些条件、优先级更高的数据包。

每个数据包的格式为:<priority>:<weight>,其中:

  • <priority> 是数据包的优先级(正整数,1 ≤ priority ≤ 10^5,数值越大优先级越高)
  • <weight> 是数据包的权重(正整数,1 ≤ weight ≤ 10^5

系统需要按照以下规则处理数据包:搜索整个数据包序列(从左到右),对于每个数据包,找出其右侧第一个同时满足如下条件的数据包:

  • 条件1:目标数据包的 priority 必须大于当前数据包的 priority
  • 条件2:目标数据包的 weight 必须等于当前数据包的 weight

请编写一个方法,实现数据包右侧第一个满足条件的数据包查找功能。

二、输入描述

参数 packets:数据包内容,长度为 n(正整数,1 ≤ n ≤ 10^5)的数组,每个元素为 [priority, weight]

三、输出描述

右侧第一个满足条件的数据包 ID 组成的数组序列;数据包 ID 是其在数组中的位置,即第 i i i 个数据包的 ID 为(从 1 开始计数,非数组下标 0);如果某个数据包右侧没有满足条件的数据包,则输出 0。

四、测试用例

测试用例1:

1、输入

5,10 6,10 4,10

2、输出

2,0,0

3、说明

数据包:

ID1 = 5,10
ID2 = 6,10
ID3 = 4,10

对于 ID1:

ID2 weight = 10
ID2 priority = 6 > 5

所以答案是 ID2。

ID2 和 ID3 后面都没有符合要求的数据包。

输出:2,0,0

测试用例2:

1、输入

5,10 7,22

2、输出

0,0

3、说明

虽然:7 > 5

但是:10 != 22

weight 不相等,所以 ID1 不能选择 ID2。

输出:0,0

五、解题思路

使用 HashMap + 单调栈:Map<weight, Stack<index>>

不同 weight 的数据包互不影响,因此每个 weight 单独维护一个单调栈。

从左向右扫描数据包。假设当前数据包为:

priority = currentPriority
weight = currentWeight

只查看 currentWeight 对应的栈。如果:栈顶数据包 priority < currentPriority

说明当前数据包满足栈顶数据包的两个要求:weight 相同,并且 priority 更大。

而且,因为是从左往右扫描,所以当前数据包一定是栈顶数据包右侧第一个符合条件的数据包。

不断弹出所有 priority 小于当前 priority 的栈顶元素,并把它们的答案设置成当前数据包 ID。

六、Java算法源码

public class OdTest {

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        List<int[]> packets = new ArrayList<>();

        String[] arr = scanner.nextLine().split(" ");
        for (int i = 0; i < arr.length; i++) {
            String[] split = arr[i].split(",");
            packets.add(new int[]{Integer.valueOf(split[0]), Integer.valueOf(split[1])});
        }

        scanner.close();

        int[] result = findNextPackets(packets);

        // 按照题目要求输出:2,0,0
        StringJoiner joiner = new StringJoiner(",");
        for (int id : result) {
            joiner.add(String.valueOf(id));
        }

        System.out.println(joiner);
    }

    /**
     * 查找每个数据包右侧第一个:
     * 1. weight 相同
     * 2. priority 更大
     * 的数据包 ID。
     */
    private static int[] findNextPackets(List<int[]> packets) {
        int n = packets.size();

        /*
         * Java 的 int 数组默认初始化为 0。
         * 因此最终仍没有找到符合条件的数据包时,
         * 不需要额外处理,答案天然就是 0。
         */
        int[] result = new int[n];

        /*
         * key   :weight
         * value :当前 weight 下尚未找到答案的数据包下标栈
         *
         * 每个 weight 都拥有自己独立的单调栈。
         * 栈中的 priority 从栈底到栈顶保持单调不增。
         */
        Map<Integer, Deque<Integer>> stacksByWeight = new HashMap<>();

        for (int i = 0; i < n; i++) {
            int currentPriority = packets.get(i)[0];
            int currentWeight = packets.get(i)[1];

            /*
             * 取得当前 weight 对应的栈。
             * 如果之前没有出现过这种 weight,
             * 则新建一个空栈。
             */
            Deque<Integer> stack =
                    stacksByWeight.computeIfAbsent(
                            currentWeight,
                            k -> new ArrayDeque<>()
                    );

            /*
             * 栈里的数据包和当前数据包 weight 一定相同。
             *
             * 如果当前 priority 大于栈顶 priority,
             * 那么当前数据包就是栈顶数据包右侧
             * 第一个满足条件的数据包。
             *
             * 为什么可以确定是“第一个”?
             * 因为我们是严格按照从左到右的顺序扫描,
             * 如果之前已经出现过符合条件的数据包,
             * 栈顶元素早就会被弹出,不可能保留到现在。
             */
            while (!stack.isEmpty()
                    && packets.get(stack.peek())[0] < currentPriority) {

                int previousIndex = stack.pop();

                /*
                 * 数组下标从 0 开始,
                 * 题目中的数据包 ID 从 1 开始,
                 * 所以当前数据包 ID = i + 1。
                 */
                result[previousIndex] = i + 1;
            }

            /*
             * 当前数据包暂时还不知道答案,
             * 放入对应 weight 的单调栈中,
             * 等待后面出现优先级更大的数据包。
             */
            stack.push(i);
        }

        return result;
    }
}

七、效果展示

1、输入

3,5 5,5 4,5 6,5

2、输出

2,4,4,0

3、说明

对应:

ID1: 3,5
ID2: 5,5
ID3: 4,5
ID4: 6,5

因此:

ID1 -> ID2
ID2 -> ID4
ID3 -> ID4
ID4 -> 0

输出:2,4,4,0

在这里插入图片描述


🏆下一篇:华为OD机试 - 简易内存池 - 逻辑分析(Java 新系统 200分)

🏆本专栏收录于《华为OD机试(JAVA)真题》

刷的越多,抽中的概率越大,私信哪吒,备注华为OD,加入华为OD刷题交流群,每一题都有详细的答题思路、详细的代码注释、3个测试用例、为什么这道题采用XX算法、XX算法的适用场景,发现新题目,随时更新,全天CSDN在线答疑。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

哪 吒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值