有一个存储单元,可容纳N个项目。最初这个单位是空的。 该空间以线性方式布置,即一条线旁边的一个。 每个存储空间都有一个数字,增加到N.
当有人丢弃他们的包裹时,会为其分配第一个可用空间。包裹也可以被拾取,在这种情况下空间变得空白。 示例:如果总容量为4且1和2已满,则第3个进入的人将被分配空间3.如果1,2和3已满并且第2个空间变空,则下一个将来的人将是分配了空间2.
他们放弃的包有2个独特的属性,分配给立即识别。首先,它们根据其内容进行颜色编码,然后为它们分配唯一的标识号(UIN)。
我们想要的是查询系统:
我想知道在这种情况下如何使用哪种数据结构,以便系统尽可能高效地工作? 并且我没有给出最常见的这些操作,这意味着我将不得不针对所有情况进行优化。
请注意,即使查询过程不是直接询问存储空间编号,但是当从商店中删除某个项目时,它会通过查询存储空间编号来删除。
答案 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。 包信息可以存储在单独的数组中。
等等。
如果您有分段树来获取范围最小值,则可以在 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存储在存储空间索引中。
使用这种方法直接查询:
现在,当我们获得要删除的存储空间时,必须更新两个BiMaps。在BiMap1中,从数组中获取条目,获取corersponding Set,并从该集合中删除空格编号。从BiMap2中获取数组中的UIN,将其删除并将其从HashMap中删除。
对于两个BiMaps,移除和插入操作都是O(1)。并且Min堆在O(Log n)中工作,因此总时间复杂度为O(Log N)