【问题标题】:Find largest value smallest value and value of largest key in Data Structure在数据结构中查找最大值最小值和最大键的值
【发布时间】:2018-01-08 03:38:03
【问题描述】:

我有一个存储大量任务的系统。每个任务都有以下参数:

Time t       
Priority p      

每个任务都有一个独特的时间。这意味着没有 2 个任务可以具有相同的时间参数。
但是,优先级并不是唯一的。多个任务可以具有相同的优先级。

我需要设计一个可以处理四种类型查询的系统。每种查询类型的概率均等。以下是查询的类型:

  1. 更新任务(t,p)
    此查询希望系统将时间t 的任务的优先级设置为优先级p。如果系统中不存在该任务,则会创建一个新任务。如果任务存在,则更新其优先级。

  2. 删除任务(t)
    此查询希望系统删除与时间t 关联的任务。如果系统中不存在此类任务,则无需执行任何操作。

  3. GetMaximumPriority() 和 GetMinimumPriority()
    此查询希望系统打印系统中可用任务的最低优先级和最高优先级。

  4. GetPriorityofTaskAtMaximumTime()
    该查询希望系统打印参数t(时间)最大值的任务的优先级

我需要为这个系统设计数据结构并为这些数据结构实现算法。我需要在 Java 中实现它。

我的方法:创建一个HashMap,其中键为时间,值为优先级。 HashMap 允许我在恒定时间内解决前两个查询。但最后两个查询的时间复杂度为O(n)

问题:有没有更好的时间效率和空间效率的数据结构和算法来解决这个问题?我主要需要一种方法来解决这个问题。不需要完全实现的代码。谢谢。

【问题讨论】:

  • 为什么不尝试自己提出一个结构,然后在此处(如果您有问题)或在 [codereview](如果您需要意见)询问?看起来你要么需要学习一些东西(最好自己尝试一下),要么就是你的工作(在这种情况下,你的努力得到了钱,所以先展示一些;))。
  • 你的时间是什么样的?您可以将其用作Map<Time, Priority> 中的键值吗?
  • 我认为最难的查询是Q4,你有更多关于这个查询的信息吗?喜欢它需要实时完成还是可以缓存一些查询并以批处理模式处理它们?或者你知道时间范围吗?如果您一次批处理所有查询,则可以在 O(lg n) 时间内完成,但如果您想要实时结果似乎并不容易
  • 所有查询都不会提前提供。仅当查询可用时才必须回答查询,具体取决于当时存储的数据。所以,它是一种实时的。
  • 看看Java TreeMap。这些将在 O(log(n)) 时间内处理所有剩余的查询。您需要一个按时键控,另一个按优先级键控。更新和删除必须在树形图中执行相同的操作。这些也是 O(log(n))。应该直截了当。线性空间提供了很好的加速。您可能会发现边缘有所改进,但这种操作组合不会承认所有解决方案都是 O(1)。

标签: java algorithm sorting data-structures


【解决方案1】:

一种可能的方法是维护两个索引:一个用于时间,一个用于优先级。让我们采用两个具有恒定时间firstKey() / lastKey() 操作的平衡搜索树。我将使用TreeMap 的接口,但它应该有一个类似于c++ 中的std::map 的实现(它只是在更新期间维护第一个和最后一个元素的迭代器)。

第一张地图是Map<Time, Priority> tasks,第二张是Map<Priority, Integer> priorities。每个现有优先级值的第二个映射存储具有此优先级值的任务数。因此,您可以将tasks 用于第四个查询,将priorities 用于第三个查询。

  1. UpdateTask(t, p)

    Priority oldp = tasks.put(t, p);
    if (oldp != null) {
        decreasePriority(oldp);     
    } 
    increasePriority(p);
    

    复杂度:O(log(n))

  2. 删除任务(t)

    if (tasks.containsKey(t)) {
        Priority oldp = tasks.get(t);
        tasks.remove(t);
        decreasePriority(oldp); 
    }
    

    复杂度:O(log(n))

  3. GetMaximumPriority()、GetMinimumPriority()

    return priorities.lastKey();
    
    return priorities.firstKey();     
    

    复杂性:O(1)(使用正确的lastKey()/firstKey() 实现,O(log(n)) 使用java.util.TreeMap)。

  4. GetPriorityofTaskAtMaximumTime()

    return tasks.lastEntry().getValue();
    

    复杂性:O(1)(通过正确的lastEntry() 实现,O(log(n))java.util.TreeMap


    void increasePriority(p) {
        if (priorities.hasKey(p)) { 
            priorities.put(p, priorities.get(p) + 1);
        } else {
            priorities.put(p, 1); 
        }    
    }

    void decreasePriority(p) {
        int count = priorities.get(p);
        if (count > 1) {  
            priorities.put(p, count - 1);
        } else {
            priorities.remove(p);
        }   
    }

因此,您将避免操作中的线性复杂性。

【讨论】:

    【解决方案2】:

    一般方法 - 可能会根据您的数据类型而变化,我认为您可以将 Map 数据类型用于您的时间和优先级,以时间为键。每当您想删除数据时,使用时间作为键在该地图中搜索并删除,同样您可以使用时间作为键进行更新。

    然后您可以分别列出时间和优先级,然后您可以根据需要对列表进行排序。

    【讨论】:

    • 感谢您的建议。当更新和删除查询出现时,我需要为每个此类查询更新 2 个列表。因此,前两种查询的时间复杂度显着增加。如何解决这个问题?
    • 嗯,这只是您查询的一个一般概念,您可以使用更好的数据类型,例如 deHaar 建议进行您想要的改进。
    【解决方案3】:

    您可以尝试使用 Heap 或 PriorityQueue,它能够在 O(1) 中找到最小值和最大值,如 here 所述。
    他们还在 O(log n) 中删除和提取 min 和 max,但搜索将停留在 O(n)。 您可能需要为您的 Task 类实现 Comparator...

    【讨论】:

    • 实际上,优先队列能够在恒定时间内提取最小或最大元素(但不能同时提取),还支持在对数时间内删除最小或最大元素(但不能同时删除)。您需要更复杂的结构来实现这一点(例如哈希和两个堆的组合)
    猜你喜欢
    • 2016-01-31
    • 2012-09-16
    • 2018-05-03
    • 1970-01-01
    • 2021-09-15
    • 2013-09-28
    • 2017-04-20
    相关资源
    最近更新 更多