LeetCode – 15 – 替换数组中的非互质数 – 16 – 设计数字容器系统 – 17 – 设计任务管理器 – 18 – 设计电子表格

25 – 09 – 15 (补于 25 – 09 – 20)

初等数论其实是

今日份题面

给你一个整数数组 nums 。请你对数组执行下述操作:

  1. 从 nums 中找出 任意 两个 相邻 的 非互质 数。
  2. 如果不存在这样的数,终止 这一过程。
  3. 否则,删除这两个数,并 替换 为它们的 最小公倍数(Least Common Multiple,LCM)。
  4. 只要还能找出两个相邻的非互质数就继续 重复 这一过程。

返回修改后得到的 最终 数组。可以证明的是,以 任意 顺序替换相邻的非互质数都可以得到相同的结果。

生成的测试用例可以保证最终数组中的值 小于或者等于 108 。

这题其实考察的就是一个公式,

$$\text{GCD}(x, y)\times\text{LCM}(x, y)=x\times y$$

所以实现起来就很简单了:

class Solution {
    private int gcd(int m, int n) {
        return (n == 0) ? m : gcd(n, m % n);
    }
    
    private int lcm(int m, int n) {
        return m / gcd(m, n) * n; //  防溢出处理
    }
    public List<Integer> replaceNonCoprimes(int[] nums) {
        Stack<Integer> stack = new Stack<>();
        for (int num : nums) {
            int current = num;
            while (!stack.isEmpty()) {
                int top = stack.peek();
                int g = gcd(current, top);
                if (g == 1) break; // 共质,不做处理
                stack.pop(); // 否则移出当前元素进行处理
                current = lcm(current, top); // 以最小公倍数回填
            }
            stack.push(current);
        }
        List<Integer> result = new ArrayList<>(stack);
        return result;
    }
}

以上


25 – 09 – 16 (补于 25 – 09 – 21)

异言盯帧,鉴定为这周LeetCode开的是设计模式

今日份题面:

设计一个数字容器系统,可以实现以下功能:

  • 在系统中给定下标处 插入 或者 替换 一个数字。
  • 返回 系统中给定数字的最小下标。

请你实现一个 NumberContainers 类:

  • NumberContainers() 初始化数字容器系统。
  • void change(int index, int number) 在下标 index 处填入 number 。如果该下标 index 处已经有数字了,那么用 number 替换该数字。
  • int find(int number) 返回给定数字 number 在系统中的最小下标。如果系统中没有 number ,那么返回 -1 。

考虑维护 num2loc 和 loc2num 两个映射表进行双选。不过需要注意,位置对数字唯一但数字对位置不唯一,所以 num2loc 可以开一个 TreeSet 作为 Value 同时满足多位置和最小两个行为。另外位置上数字更新时注意同时 Remove 掉num2loc 中 TreeSet 里的元素

给出个人实现如下:

class NumberContainers {
    HashMap<Integer, TreeSet<Integer>> num2loc; 
    HashMap<Integer, Integer> loc2num; 

    public NumberContainers() {
        this.num2loc = new HashMap<>();
        this.loc2num = new HashMap<>();
    }
    
    public void change(int index, int number) {
        if (!loc2num.containsKey(index)) {
            // 新增键值对
            loc2num.put(index, number);
            // 同步更新 num2loc
            if (!num2loc.containsKey(number)) {
                TreeSet<Integer> curElementLocs = new TreeSet<>();
                curElementLocs.add(index);
                num2loc.put(number, curElementLocs);
            } else {
                TreeSet<Integer> curElementLocs = num2loc.get(number);
                curElementLocs.add(index);
            }
        } else {
            // 替换当前数字
            int replacedNum = loc2num.get(index);
            loc2num.put(index, number);
            
            // 从旧数字的位置集合中移除当前index
            TreeSet<Integer> oldLocs = num2loc.get(replacedNum);
            oldLocs.remove(index);
            if (oldLocs.isEmpty()) {
                num2loc.remove(replacedNum);
            }
            
            // 添加到新数字的位置集合
            if (!num2loc.containsKey(number)) {
                TreeSet<Integer> newLocs = new TreeSet<>();
                newLocs.add(index);
                num2loc.put(number, newLocs);
            } else {
                TreeSet<Integer> newLocs = num2loc.get(number);
                newLocs.add(index);
            }
        }
    }
    
    public int find(int number) {
        return num2loc.containsKey(number) && !num2loc.get(number).isEmpty() 
               ? num2loc.get(number).first() 
               : -1;
    }
}

/**
 * Your NumberContainers object will be instantiated and called as such:
 * NumberContainers obj = new NumberContainers();
 * obj.change(index,number);
 * int param_2 = obj.find(number);
 */

我都不敢想纯 C 战士写这题得多得劲(

以上


25 – 09 – 17 (补于 25 – 09 – 23)

什么业务需求(

今日份题面

一个任务管理器系统可以让用户管理他们的任务,每个任务有一个优先级。这个系统需要高效地处理添加、修改、执行和删除任务的操作。

请你设计一个 TaskManager 类:

  • TaskManager(vector<vector<int>>& tasks) 初始化任务管理器,初始化的数组格式为 [userId, taskId, priority] ,表示给 userId 添加一个优先级为 priority 的任务 taskId 。
  • void add(int userId, int taskId, int priority) 表示给用户 userId 添加一个优先级为 priority 的任务 taskId ,输入 保证 taskId 不在系统中。
  • void edit(int taskId, int newPriority) 更新已经存在的任务 taskId 的优先级为 newPriority 。输入 保证 taskId 存在于系统中。
  • void rmv(int taskId) 从系统中删除任务 taskId 。输入 保证 taskId 存在于系统中。
  • int execTop() 执行所有用户的任务中优先级 最高 的任务,如果有多个任务优先级相同且都为 最高 ,执行 taskId 最大的一个任务。执行完任务后,taskId 从系统中 删除 。同时请你返回这个任务所属的用户 userId 。如果不存在任何任务,返回 -1 。

注意 ,一个用户可能被安排多个任务。

其实从 OO 的角度出发,这题比较严谨的做法是设计一个 Task 类对输入参数进行对象化然后操纵这个类进行相关任务

class Task implements Comparable<Task>{
    public int userId;
    public int taskId;
    public int priority;
    public Task(){
        this.userId = -1;
        this.taskId = -1;
        this.priority = -1;
    }
    public Task(int userId, int taskId, int priority){
        this.userId = userId;
        this.taskId = taskId;
        this.priority = priority;
    }
    @Override
    public int compareTo(Task other){
        return this.priority == other.priority ? other.taskId - this.taskId : other.priority - this.priority; 
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Task task = (Task) o;
        return taskId == task.taskId;
    }
    @Override
    public int hashCode() {
        return Objects.hash(taskId);
    }
}

然后你就发现空时双爆了(

所以还是老老实实用数组替代吧。注意到 TaskId 唯一,这就意味着我们可以使用 HashMap 储存相关任务并做到 $O(1)$ 查询以及删除,同时由于部分任务要求优先级,所以考虑开一个自定义顺序 Heap 对任务进行维护,数据结构有了实现起来就很简单了

class TaskManager {
    private Map<Integer, int[]> taskInfo;
    private PriorityQueue<int[]> heap;
    
    public TaskManager(List<List<Integer>> tasks) {
        taskInfo = new HashMap<>();
        heap = new PriorityQueue<>((a, b) -> {
            if (a[0] != b[0]) {
                return b[0] - a[0];
            }
            return b[1] - a[1];
        });
        
        for (List<Integer> task : tasks) {
            int userId = task.get(0), taskId = task.get(1), priority = task.get(2);
            taskInfo.put(taskId, new int[]{priority, userId});
            heap.offer(new int[]{priority, taskId});
        }
    }
    
    public void add(int userId, int taskId, int priority) {
        taskInfo.put(taskId, new int[]{priority, userId});
        heap.offer(new int[]{priority, taskId});
    }
    
    public void edit(int taskId, int newPriority) {
        if (taskInfo.containsKey(taskId)) {
            taskInfo.get(taskId)[0] = newPriority;
            heap.offer(new int[]{newPriority, taskId});
        }
    }
    
    public void rmv(int taskId) {
        taskInfo.remove(taskId);
    }
    
    public int execTop() {
        while (!heap.isEmpty()) {
            int[] task = heap.poll();
            int priority = task[0], taskId = task[1];
            
            if (taskInfo.containsKey(taskId) && taskInfo.get(taskId)[0] == priority) {
                int userId = taskInfo.get(taskId)[1];
                taskInfo.remove(taskId);
                return userId;
            }
        }
        return -1;
    }
}

注意到这块其实是用了一个 LazyTag 的小技巧,我们在 Remove 时只对 HashMap 进行移除操作,对 Heap 的操作是在涉及到 Heap 时临时对堆顶进行排查的。

虽然是另一种意义上的 Mem Leak,不过只要力大就能砖飞(´-ω-`)

以上


25 – 09 – 18 (补于 25 – 09 – 24)

别拦着我,我觉得我能开发出 Excel!

今日份题面

电子表格是一个网格,它有 26 列(从 'A' 到 'Z')和指定数量的 rows。每个单元格可以存储一个 0 到 105 之间的整数值。

请你实现一个 Spreadsheet 类:

  • Spreadsheet(int rows) 初始化一个具有 26 列(从 'A' 到 'Z')和指定行数的电子表格。所有单元格最初的值都为 0 。
  • void setCell(String cell, int value) 设置指定单元格的值。单元格引用以 "AX" 的格式提供(例如,"A1""B10"),其中字母表示列(从 'A' 到 'Z'),数字表示从 1 开始的行号。
  • void resetCell(String cell) 重置指定单元格的值为 0 。
  • int getValue(String formula) 计算一个公式的值,格式为 "=X+Y",其中 X 和 Y 要么 是单元格引用,要么非负整数,返回计算的和。

注意: 如果 getValue 引用一个未通过 setCell 明确设置的单元格,则该单元格的值默认为 0 。

题目的思路是很明确的,行列对应的单元格值唯一,那么必然是 Map 映射最为方便,但偏偏我在这个时候犯蠢,明明引用是唯一的可以直接作为键,我还离散化了一下之后再设置键值对

class Spreadsheet {
    private final static int COLS = 26;
    private int rows;
    private HashMap<Integer, Integer> fills = new HashMap<>();
    
    // 愚蠢的离散化
    private int rc2index(int curRow, char curCol) {
        return curRow * COLS + (curCol - 'A');
    }

    private int[] extractRowAndColumn(String cell) {
        char colChar = cell.charAt(0);
        String rowStr = cell.substring(1);
        int row = Integer.parseInt(rowStr);
        return new int[] { row, colChar };
    }

    private int getCell(String cell) {
        int[] coordinates = extractRowAndColumn(cell);
        int index = rc2index(coordinates[0], (char) coordinates[1]);
        return fills.getOrDefault(index, 0);
    }

    public Spreadsheet(int rows) {
        this.rows = rows;
    }

    public void setCell(String cell, int value) {
        int[] coordinates = extractRowAndColumn(cell);
        int index = rc2index(coordinates[0], (char) coordinates[1]);
        fills.put(index, value);
    }

    public void resetCell(String cell) {
        int[] coordinates = extractRowAndColumn(cell);
        int index = rc2index(coordinates[0], (char) coordinates[1]);
        fills.put(index, 0); // 直接设置为0,无需检查是否存在
    }

    public int getValue(String formula) {
        String[] elements = formula.substring(1).split("\\+"); // 跳过开头的"=",然后按"+"分割
        int res = 0;
        for (String element : elements) {
            if (element.matches("\\d+")) {
                // 如果元素是整数
                res += Integer.parseInt(element);
            } else {
                // 否则视为单元格引用
                res += getCell(element);
            }
        }
        return res;
    }
}

至于其他的,注意一下查找引用时是不是大写字母开头就行了。上述版本过于繁琐,参考一下 0xf3 佬的简洁实现:

class Spreadsheet {
    private final Map<String, Integer> data = new HashMap<>();

    public Spreadsheet(int rows) {
    }

    public void setCell(String cell, int value) {
        data.put(cell, value);
    }

    public void resetCell(String cell) {
        data.remove(cell);
    }

    public int getValue(String formula) {
        int ans = 0;
        for (String cell : formula.substring(1).split("\\+")) {
            if (Character.isUpperCase(cell.charAt(0))) {
                ans += data.getOrDefault(cell, 0);
            } else {
                ans += Integer.parseInt(cell);
            }
        }
        return ans;
    }
}

自愧不如啊自愧不如

以上


评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注