不寻常的hashset实现:访问随机元素?

时间:2015-04-08 13:35:24

标签: c# data-structures hashset

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

问题:有人向我指出,使用散列集而不是列表会快得多,因为散列集的“顺序”对我来说实际上是随机的,并且从中移除元素会很多快点。 我该怎么做(即如何访问哈希集中的随机元素?我只知道如何检查哈希集是否包含我已经拥有的元素)?

另外,我不太清楚如何删除某个区域的所有节点。我是否必须覆盖/定义比较函数来比较节点区域?同样,我知道如何从hashset中删除已知元素,但在这里我不知道如何删除某个区域的所有节点。

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

3 个答案:

答案 0 :(得分:1)

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

也就是说,HastSet<T>实现了IEnumerable<T>,因此您可以只选择一个随机数n并删除第n项:

// 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轻松完成)并单独删除每个项目。您是通过比较属性还是定义相等比较器来做到这一点取决于您。

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 :(得分:0)

您可以采用另一种替代方法,最终可能比哈希集中的删除更快,并且创建一个可以一次为您完成工作的结构。

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

var rnd = new Random();

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

这给了我这样的数据:

source

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

var pickable =
    nodes
        .OrderBy(n => rnd.Next())
        .ToLookup(n => n.region, n => n.id);

这给了我这个:

pickable

注意区域和单个ID在查找中是如何随机化的。现在可以迭代查找并只获取每个组的第一个元素来获取随机区域和随机节点ID,而无需从哈希集中删除任何项目。

我不希望性能太大问题,因为我只用1,000,000个节点尝试了1000个区域,并且在600多分钟内得到了结果。

答案 2 :(得分:-1)

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

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),但这并不意味着它比List更快。您甚至没有解决方案并且正在优化。那是不成熟的优化。

使用List,您可以使用RemoveAll

ll.RemoveAll(x =>  x.region == region);