首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >不寻常的哈希集实现:访问随机元素?

不寻常的哈希集实现:访问随机元素?
EN

Stack Overflow用户
提问于 2015-04-08 13:35:23
回答 3查看 635关注 0票数 1

背景:在我的程序中,我有一个节点列表(我已经定义了一个类)。它们都有唯一的id号和非唯一的“区域”号。我想随机选择一个节点,记录它的id号,然后从列表中删除同一区域的所有节点。

问题:有人向我指出,使用哈希集而不是列表会更快,因为对我来说,哈希集的“顺序”实际上是随机的,从其中删除元素会快得多。如何做到这一点(例如,如何访问散列集中的随机元素?我只知道如何检查散列集是否包含已有的元素)?

另外,我不太确定如何删除某个区域的所有节点。是否必须重写/定义比较函数来比较节点区域?同样,我知道如何从哈希集中删除已知元素,但这里我不知道如何删除某个区域的所有节点。

如果这样做有帮助的话,我可以发布关于我的代码的详细信息。

EN

回答 3

Stack Overflow用户

发布于 2015-04-08 13:47:04

要明确的是,HashSet中的订单项不是随机的,它只是不容易确定。这意味着,如果您多次迭代哈希集,则每次项目的顺序都是相同的,但您无法控制它们的顺序。

尽管如此,HastSet<T>实现了IEnumerable<T>,因此您只需选择一个随机数n并删除第n项:

代码语言:javascript
复制
// assuming a Random object is defined somewhere (do not declare it here)
n  = rand.Next(hashSet.Count);
var item = hashSet.ElementAt(n);
hashSet.Remove(item);

另外,我不太确定如何删除某个区域的所有节点。是否必须重写/定义比较函数来比较节点区域?

不一定--您需要扫描hashSet以查找匹配项(使用Linq轻松完成),并分别删除每个条目。无论您是通过比较属性还是定义相等比较器,这都取决于您。

代码语言:javascript
复制
foreach (var dupe in hashSet.Where(x => x.Region == item.Region).ToList()) 
    hashSet.Remove(dupe);

注意ToList是必要的,因为您不能在迭代集合时修改它,所以要删除的项需要存储在不同的集合中。

请注意,您不能为此目的在Node类中重写Equals,否则您将无法将来自一个区域的多个节点放在哈希集中。

如果您还没有注意到,这两种需求都无法达到使用HashSet的目的--只有在查找已知项时,HashSet才会更快;基于属性迭代或查找项的速度并不比常规集合快。这就像翻阅电话簿,找出所有电话号码以5开头的人。

如果您总是希望按区域组织项目,那么Dictionary<int, List<Node>>可能是一个更好的结构。

票数 1
EN

Stack Overflow用户

发布于 2015-04-08 15:35:00

还有另一种可供选择的方法,它最终可能会比从哈希集中移除速度更快,并且正在创建一种可以一次完成工作的结构。

首先,给我一些我正在运行的示例数据:

代码语言:javascript
复制
var rnd = new Random();

var nodes =
    Enumerable
        .Range(0, 10)
        .Select(n => new Node() { id = n, region = rnd.Next(0, 3) })
        .ToList();

这给了我这样的数据:

现在我建立了这样的结构:

代码语言:javascript
复制
var pickable =
    nodes
        .OrderBy(n => rnd.Next())
        .ToLookup(n => n.region, n => n.id);

这给了我这个:

注意在查找中区域和单个ids是如何随机化的。现在,可以迭代查找,只使用每个组的第一个元素来获得随机区域和随机节点id,而不需要从散列集中删除任何项。

我不认为性能会有太大问题,因为我刚刚在1000个区域的1000个节点上尝试了这一点,并在600 as多一点的时间内得到了结果。

票数 0
EN

Stack Overflow用户

发布于 2015-04-08 14:00:55

在哈希集上,您可以使用ElementAt

代码语言:javascript
复制
notreallrandomObj nrrbase = HS.ElementAt(0);
int region = nrrbase.region;
List<notreallrandomObj> removeItems = new List<notreallrandomObj>();

foreach (notreallrandomObj nrr in HS.Where(x => x.region == region)) 
    removeItems.Add(nrr);
foreach (notreallrandomObj nrr in removeItems)
    HS.Remove(nrr);

不确定是否可以在循环中删除。

您可能需要建立删除列表。

是的,在HashSet上删除O(1),但这并不意味着它会比列表更快。您甚至没有解决方案,而且正在进行优化。这是过早的优化。

通过一个列表,您可以只使用RemoveAll

代码语言:javascript
复制
ll.RemoveAll(x =>  x.region == region);
票数 -1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/29516199

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档