我经常发现自己需要一个具有以下属性的数据结构:
- 可以使用O(n)中的n个对象数组进行初始化。
- 一个人可以在O(1)中获得一个随机元素,在此操作之后拾取 元素从结构中删除。 (无需更换)
- 可以在O(p)
中撤消p'选择而无需替换'操作- 可以从O(log(n))
中的结构中删除特定对象(例如,通过id)- 可以获得当前在结构中的对象数组 为O(n)。
其他行为(例如插入)的复杂性(甚至可能性)无关紧要。除了复杂性之外,它对于少量的n也应该是有效的。
有人能给我指导实施这样的结构吗?我目前实现了一个具有上述所有属性的结构,除了元素的选择需要O(d)和d过去选择的数量(因为我明确检查它是否'尚未被选中')。我可以找出允许在O(1)中进行拣选的结构,但这些结构在至少一个其他操作中具有更高的复杂性。
顺便说一句: 请注意,上面的O(1)意味着复杂性与#earlier拾取的元素无关,并且与总的#elements无关。
*在蒙特卡罗算法中(从n个元素的'集'中迭代选择p个随机元素)。
答案 0 :(得分:2)
HashMap在插入和删除方面都有复杂度O(1)。 你指定了很多操作,但除了插入,删除和遍历之外,它们都不是其他任何操作:
可以使用O(n)中的n个对象数组进行初始化。
n * O(1)插入。 HashMap很好
可以获得随机元素 O(1),此操作后挑选 元素从结构中删除。 (无需替换)
这是唯一需要O(n)的操作。
可以撤消p'选择而不用 替换'O(p)中的操作
这是一个插入操作:O(1)。
可以删除特定对象(例如 通过id)来自O(log(n))
中的结构
O(1)。
可以获得对象的数组 目前在O(n)的结构中。
你可以在O(n)中遍历HashMap
编辑: 在O(n)中拾取随机元素的例子:
HashMap map ....
int randomIntFromZeroToYouHashMapSize = ...
Collection collection = map.values();
Object[] values = collection.toArray();
values[randomIntFromZeroToYouHashMapSize];
答案 1 :(得分:1)
如果一个数组(或ArrayList
)分为“已挑选”和“未挑出”?你跟踪边界的位置,并选择,你在边界下面生成一个随机索引,然后(因为你不关心顺序),将该索引处的项目与最后一个未被删除的项目交换,并减少边界。要取消选择,只需增加边界。
更新:忘记删除O(log(n))。但是,如果你为索引保留HashMap
个ID,那就不那么难了,只需要一点内存。
如果你在线搜索,你会发现各种IndexedHashSet
实现都或多或少地遵循这个原则 - 数组或ArrayList
加上HashMap
。
(我希望看到一个更优雅的解决方案,如果存在的话。)
更新2:嗯......或者实际移除是否会再次成为O(n),如果您必须重新复制数组或移动它们?
答案 2 :(得分:1)
以下是我在Collections.shuffle()
上使用ArrayList
的分析:
✔可以使用O(n)中的n个对象数组进行初始化。
是的,虽然费用摊销,除非事先知道n。
✔可以在O(1)中获得随机元素,在此操作之后,拾取的元素将从结构中移除,而无需替换。
是的,选择混洗数组中的最后一个元素;用剩余元素的subList()
替换数组。
✔可以在O(p)中撤消p'选择而无需替换'操作。
是的,通过add()
将元素附加到此列表的末尾。
❍可以从O(log(n))中的结构中删除特定对象(例如,通过id)。
不,看起来像是O(n)。
✔可以在O(n)中获取结构中当前对象的数组。
是的,使用toArray()
看起来很合理。
答案 3 :(得分:1)
好的,与0verbose相同的答案,只需要一个简单的修复就可以获得O(1)随机查找。创建一个存储相同n个对象的数组。现在,在HashMap中,存储对。例如,假设您的对象(简单的字符串)是:
{"abc" , "def", "ghi"}
创建
List<String> array = ArrayList<String>("abc","def","ghi")
使用以下值创建HashMap地图:
for (int i = 0; i < array.size(); i++)
{
map.put(array[i],i);
}
通过选择数组中的任何索引,可以轻松实现O(1)随机查找。出现的唯一复杂因素是删除对象时。为此,请执行:
在map
中查找对象。获取其数组索引。让我们调用这个索引i
(map.get(i)
) - O(1)
使用array [size of array - 1](数组中的最后一个元素)交换数组[i]。将数组的大小减小1(因为现在减少了一个数字) - O(1)
在i
中更新数组位置map
中新对象的索引(map.put(array [i],i)) - O(1)
我为java和cpp表示法的混合道歉,希望这有帮助