获取随机元素并将其移除

Get random element and remove it

本文关键字:随机 元素 获取      更新时间:2023-10-16

问题:我需要为容器获取一个随机元素,并将其从该容器中删除。容器不需要排序我不在乎订单

  • Vector可以在O(1)中获取我的随机元素,但只能在O(N)中删除它
  • List删除O(1)中的元素,但只能获得O(N)中的随机元素

因此,我想出了一个想法,制作一个自定义向量,允许您通过O(1)+复杂度的索引来删除任何元素。其想法是交换最后一个元素和要删除的元素,然后再交换pop_back()。如果您需要删除最后一个元素-只需pop_back()。向量的顺序将不相同,但您可以获得快速移除方法。

正如我所理解的,与我的解决方案相比,deque的索引访问速度较慢,删除复杂性较差,但我不能100%确定。

我很好奇O(1)O(logN)中是否存在按索引或按值进行随机访问和元素移除的数据结构?

您有了解决方案,它看起来非常好。用C++编写它的惯用方法不是创建另一个类(不要从std::vector继承),而是编写一个函数:

template <typename T>
void remove_at(std::vector<T>& v, typename std::vector<T>::size_type n)
{
    std::swap(v[n], v.back());
    v.pop_back();
}

用法:

remove_at(v, 42);

这提供了与std::swap<T>相同的异常保证。

现在,如果您想返回对象,并且您可以访问C++11编译器,您可以按照以下方式进行。困难的部分是在所有情况下提供基本的例外保证:

template <typename T>
T remove_at(std::vector<T>&v, typename std::vector<T>::size_type n)
{
    T ans = std::move_if_noexcept(v[n]);
    v[n] = std::move_if_noexcept(v.back());
    v.pop_back();
    return ans;
}

事实上,如果在移动操作期间引发异常,您不希望向量处于无效状态。

具有O(n)复杂性

vec.erase(vec.begin()+randomIdx);randomIdx将介于0和vec.size()-1 之间

如果你想要O(1)的复杂性,你可以使用列表容器,或者用最后一个交换元素并删除它。(如其他人所述)

是的,有一个解决方案,一个平衡良好的二进制树。

每个节点一个,您需要存储每一侧的节点数。从中找到第N个元素是O(log N)。

删除第N个元素也是O(logN),因为您必须遍历回树以更正所有计数。任何再平衡至多也是O(logN)。

如果没有叶节点比另一个叶节点深2个节点,则认为树是平衡的。查找AVL树以获得Rabalance算法。

如果标准库"开放"使用std::set和std::map所使用的树,作为在自定义树中使用的公共接口,那就太好了。