存储具有多个查询条件的多个实体时要使用哪些数据结构?

时间:2017-01-20 02:35:34

标签: java algorithm data-structures

有一个存储单元,可容纳N个项目。最初这个单位是空的。 该空间以线性方式布置,即一条线旁边的一个。 每个存储空间都有一个数字,增加到N.

当有人丢弃他们的包裹时,会为其分配第一个可用空间。包裹也可以被拾取,在这种情况下空间变得空白。 示例:如果总容量为4且1和2已满,则第3个进入的人将被分配空间3.如果1,2和3已满并且第2个空间变空,则下一个将来的人将是分配了空间2.

他们放弃的包有2个独特的属性,分配给立即识别。首先,它们根据其内容进行颜色编码,然后为它们分配唯一的标识号(UIN)。

我们想要的是查询系统:

  1. 当输入为彩色时,显示与此颜色关联的所有UIN。
  2. 当输入为彩色时,显示放置这些包的所有数字(存储空间编号)。
  3. 显示放置了给定UIN的项目的位置,即存储空间编号。
  4. 我想知道在这种情况下如何使用哪种数据结构,以便系统尽可能高效地工作? 并且我没有给出最常见的这些操作,这意味着我将不得不针对所有情况进行优化。

    请注意,即使查询过程不是直接询问存储空间编号,但是当从商店中删除某个项目时,它会通过查询存储空间编号来删除。

5 个答案:

答案 0 :(得分:3)

您提到了三个要进行的查询。让我们一个一个地处理它们。

我想不出可以同时帮助您处理所有三个查询的单个数据结构。因此,我将给出一个包含三个数据结构的答案,您必须维护所有三个DS状态才能使应用程序正常运行。将此视为从应用程序获得所需功能的相当快的性能的成本。

  

当输入为彩色时,显示与此颜色关联的所有UIN。

使用将Color映射到一组UIN的HashMap。每当一个项目:

    添加
  • - 查看HashMap中是否存在颜色。如果是,请将此UIN添加到集合中,否则使用新集合创建新条目,然后添加UIN。

  • 已删除 - 找到此颜色的集合并从集合中删除此UIN。如果该集现在为空,您可以完全删除此条目。

  

当输入为彩色时,显示放置这些包的所有数字。

维护一个HashMap,将UIN映射到放置传入包的编号。从我们在前一种情况下创建的HashMap中,您可以获得与给定Color关联的所有UIN的列表。然后使用此HashMap,您可以获得该颜色集中存在的每个UIN的编号。

现在,当要添加一个包时,您必须将该条目添加到特定Color桶中的先前HashMap以及此HashMap。在删除时,您必须从此处.Remove()输入。

最后,

  

显示放置了给定UIN的项目的位置。

如果您已完成上一个操作,则您已将HashMap映射到数字UIN。这个问题只是前一个问题的一个子问题。

正如我在顶部提到的那样,第三个DS将是一个最小的整数。堆将在开始时使用第一个 N 整数进行初始化。然后,随着包的到来,堆将被轮询。返回的数字将代表放置此包的存储空间。如果存储单元已满,则堆将为空。每当删除一个包时,它的编号都会被添加回堆中。由于它是一个小堆,最小数量将冒泡到顶部,满足你的情况,当4和2为空时,下一个要填充的空间将是4。

让我们对此解决方案进行 Big O分析以便完成。

  • 初始化的时间:此设置的将是O( N ),因为我们必须初始化一堆 N 。其他两个HashMaps一开始就是空的,因此不会产生时间成本。

  • 添加包的时间:将包含获取数字的时间,然后在HashMaps中输入相应的条目。要从堆中获取数字,最多需要O( Log N )时间。在HashMaps中添加条目将是O(1)。因此,总体时间最差的情况为O( Log N )。

  • 删除软件包的时间:最差也是O( Log N ),因为从HashMaps中删除的时间将是O(1)只有当时,将释放的数字添加回min-heap的时间将由O( Log N )限制。

答案 1 :(得分:1)

这就是家庭作业或管理层真的很糟糕。

无论哪种方式,我决定做一个你最关心查询速度的版本,但不关心内存或插入和删除的额外开销。这并不是说我认为我会疯狂地刻录内存或永远插入和删除内存,只是因为我最关注的是查询。

Tl; DR - 为了解决你的问题,我使用PriorityQueue,一个数组,一个HashMap和一个ArrayListMultimap (来自guava,一个通用的外部库),每一个都解决了一个不同的问题

以下部分是工作代码,介绍了一些简单的插入,查询和删除操作。下一步实际上并不是Java,因为我删除了大部分导入,类声明等。此外,它引用了另一个名为' Packg'的类。这只是一个简单的数据结构,你应该能够从调用它中找出它。

解释在代码

下面
import com.google.common.collect.ArrayListMultimap;

private PriorityQueue<Integer> openSlots;
private Packg[] currentPackages;
Map<Long, Packg> currentPackageMap;
private ArrayListMultimap<String, Packg> currentColorMap;
private Object $outsideCall;


public CrazyDataStructure(int howManyPackagesPossible) {
    $outsideCall = new Object();
    this.currentPackages = new Packg[howManyPackagesPossible];
    openSlots = new PriorityQueue<>();
    IntStream.range(0, howManyPackagesPossible).forEach(i -> openSlots.add(i));//populate the open slots priority queue
    currentPackageMap = new HashMap<>();
    currentColorMap = ArrayListMultimap.create();
}

/*
 * args[0] = integer, maximum # of packages
 */
public static void main(String[] args)
{
    int howManyPackagesPossible = Integer.parseInt(args[0]);
    CrazyDataStructure cds = new CrazyDataStructure(howManyPackagesPossible);
    cds.addPackage(new Packg(12345, "blue"));
    cds.addPackage(new Packg(12346, "yellow"));
    cds.addPackage(new Packg(12347, "orange"));
    cds.addPackage(new Packg(12348, "blue"));
    System.out.println(cds.getSlotsForColor("blue"));//should be a list of {0,3}
    System.out.println(cds.getSlotForUIN(12346));//should be 1 (0-indexed, remember)
    System.out.println(cds.getSlotsForColor("orange"));//should be a list of {2}
    System.out.println(cds.removePackage(2));//should be the orange one
    cds.addPackage(new Packg(12349, "green"));
    System.out.println(cds.getSlotForUIN(12349));//should be 2, since that's open
}

public int addPackage(Packg packg)
{
    synchronized($outsideCall)
    {
        int result = openSlots.poll();
        packg.setSlot(result);
        currentPackages[result] = packg;
        currentPackageMap.put(packg.getUIN(), packg);
        currentColorMap.put(packg.getColor(), packg);
        return result;
    }
}

public Packg removePackage(int slot)
{
    synchronized($outsideCall)
    {
        if(currentPackages[slot] == null)
            return null;
        else
        {
            Packg packg = currentPackages[slot];
            currentColorMap.remove(packg.getColor(), packg);
            currentPackageMap.remove(packg.getUIN());
            currentPackages[slot] = null;
            openSlots.add(slot);//return slot to priority queue
            return packg;
        }
    }
}

public List<Packg> getUINsForColor(String color)
{
    synchronized($outsideCall)
    {
        return currentColorMap.get(color);
    }
}

public List<Integer> getSlotsForColor(String color)
{
    synchronized($outsideCall)
    {
        return currentColorMap.get(color).stream().map(packg -> packg.getSlot()).collect(Collectors.toList());
    }
}

public int getSlotForUIN(long uin)
{
    synchronized($outsideCall)
    {
        if(currentPackageMap.containsKey(uin))
            return currentPackageMap.get(uin).getSlot();
        else
            return -1;
    }
}

我在班上使用了4种不同的数据结构。

PriorityQueue 我使用优先级队列来跟踪所有打开的广告位。它的log(n)表示插入,而常量表示删除,所以不应该太糟糕。在记忆方面,它并不是特别有效,但它也是线性的,所以不会太糟糕。

数组 我使用常规数据来按插槽#进行跟踪。这对于内存是线性的,对于插入和删除是常量。如果你需要更多的插槽灵活性,你可能需要为ArrayList或其他东西切换它,但是你必须找到一个更好的方法来跟踪&#39;空&# 39;槽。

HashMap 啊,HashMap,BigO复杂的金童。作为一些内存开销和恼人的大写字母的回报,它是一个非常棒的数据结构。插入是合理的,查询是不变的。我使用它来映射UID和Packg的插槽。

ArrayListMultimap 我使用的唯一数据结构不是普通的Java。这个来自Guava(谷歌,基本上),它只是编写自己的列表地图的一个很好的小捷径。此外,它可以很好地使用空值,这对我来说是一个奖励。这可能是所有数据结构中效率最低的,但它也是处理最难的任务的人,因此......不能责怪它。这个允许我们按照颜色,在相对于插槽数的恒定时间内以及相对于它返回的Packg对象数量的线性时间内获取Packg的列表。

当你拥有这么多数据结构时,它会使插入和删除变得有点麻烦,但这些方法仍然应该是非常简单的。如果代码的某些部分没有意义,我会很乐意解释更多(通过在代码中添加注释),但我认为它应该是最好的原样。

答案 2 :(得分:0)

查询3 :使用哈希映射,键为UIN,值为对象(存储空间编号,颜色)(以及包的任何其他信息)。成本是O(1)查询,插入或删除。空格为O(k),k是当前的UIN数。

查询1和2 :使用哈希映射+多个链接列表

哈希映射,键是颜色,值是指针(或Java中的引用),用于链接该颜色的相应UIN列表。

每个链接列表都包含UIN。

对于查询1:询问哈希映射,然后返回相应的链接列表。成本为O(k1),其中k1是查询颜色的UIN数。空间为O(m + k1),其中m是唯一颜色的数量。

对于查询2:执行查询1,然后应用查询3.成本为O(k1)其中k1是查询颜色的UIN数。空间为O(m + k1),其中m是唯一颜色的数量。

要插入:给定颜色,数字和UIN,在查询3的哈希映射中插入一个对象(num,color); hash(color)转到相应的链接列表并插入UIN。

删除:给定UIN,询问查询3的颜色,然后询问查询1以删除链接列表中的UIN。然后在查询3的哈希映射中删除UIN。

奖励:要管理存储空间,情况与操作系统中的内存管理相同:read more

答案 3 :(得分:0)

这与SegmentTree非常简单。 只需在每个地方存储一个位置并查询 min 它将与空置地点匹配,当您捕获一个地方时,只需为此地点指定0。 包信息可以存储在单独的数组中。

  • Initiall它有以下值:
  • 1 2 3 4
  • 捕获后会看到以下内容:
  • 0 2 3 4
  • 再捕获一次之后,它会看起来如下:
  • 0 0 3 4
  • 再捕获一次之后,它会看起来如下:
  • 0 0 0 4
  • 清理2之后,它将显示为以下内容:
  • 0 2 0 4
  • 再捕获一次之后,它会看起来如下:
  • 0 0 0 4

等等。

如果您有分段树来获取范围最小值,则可以在 O(LogN)中为每个操作完成。

这是我在C#中的实现,这很容易转换为Java的C ++。

public class SegmentTree
{
    private int Mid;
    private int[] t;

    public SegmentTree(int capacity)
    {
        this.Mid = 1;
        while (Mid <= capacity) Mid *= 2;
        this.t = new int[Mid + Mid];


        for (int i = Mid; i < this.t.Length; i++) this.t[i] = int.MaxValue;
        for (int i = 1; i <= capacity; i++) this.t[Mid + i] = i;
        for (int i = Mid - 1; i > 0; i--) t[i] = Math.Min(t[i + i], t[i + i + 1]);
    }

    public int Capture()
    {
        int answer = this.t[1];
        if (answer == int.MaxValue)
        {
            throw new Exception("Empty space not found.");
        }

        this.Update(answer, int.MaxValue);

        return answer;
    }

    public void Erase(int index)
    {
        this.Update(index, index);
    }

    private void Update(int i, int value)
    {
        t[i + Mid] = value;
        for (i = (i + Mid) >> 1; i >= 1; i = (i >> 1))
            t[i] = Math.Min(t[i + i], t[i + i + 1]);
    }
}

这里的用法示例:

    int n = 4;
    var st = new SegmentTree(n);

    Console.WriteLine(st.Capture());
    Console.WriteLine(st.Capture());
    Console.WriteLine(st.Capture());
    st.Erase(2);
    Console.WriteLine(st.Capture());
    Console.WriteLine(st.Capture());

答案 4 :(得分:0)

为获取存储空间编号,我使用了最小堆方法PriorityQueue。这适用于O(log n)时间,删除和插入。

我使用了2个BiMaps,自创建的数据结构,用于存储UIN,颜色和存储空间编号之间的映射。这些BiMaps在内部使用HashMap和大小为N的数组。

在第一个BiMap(BiMap1)中,HashMap<color, Set<StorageSpace>>存储颜色到存储空间列表的映射。还有一个String数组String[] colorSpace,它将颜色存储在存储空间索引中。

在第二个BiMap(BiMap2)中,HashMap<UIN, storageSpace> stores the mapping between UIN and storageSpace. And a string array String [] uinSpace`将UIN存储在存储空间索引中。

使用这种方法直接查询:

  1. 当输入为彩色时,显示与此颜色关联的所有UIN。       从BiMap1获取存储空间列表,因为这些空间使用BiMap2中的数组来获取相应的UIN。
  2. 当输入为彩色时,显示放置这些包的所有数字(存储空间编号)。使用BiMap1的HashMap获取列表。
  3. 显示放置了给定UIN的项目的位置,即存储空间编号。使用BiMap2从HashMap中获取值。
  4. 现在,当我们获得要删除的存储空间时,必须更新两个BiMaps。在BiMap1中,从数组中获取条目,获取corersponding Set,并从该集合中删除空格编号。从BiMap2中获取数组中的UIN,将其删除并将其从HashMap中删除。

    对于两个BiMaps,移除和插入操作都是O(1)。并且Min堆在O(Log n)中工作,因此总时间复杂度为O(Log N)