在O(n)时间内更新C#键控集合中的所有值?

时间:2012-10-30 16:57:35

标签: c# .net collections

这是我在C#中一次又一次遇到的问题,但还没有找到一般解决方案。在C ++ / STL中,可以使用迭代器在O(n)时间内更新映射中的所有值,而无需使用键来访问每个元素。有没有办法获得与任何C#集合类似的行为,如SortedList,SortedDictionary?

我可以做类似

的事情
foreach (int key in list.Keys)
{
    list[key] *= 3;
}

但是这需要O(n * log(n)),因为使用key搜索每个元素需要log(n)。

只是提出一个想法,我正在寻找以下内容:

SortedList<int, double> list = new SortedList<int,double>();

// Add few values fist

// E.g. first try ...
IList<double> values = list.Values;
for (int i = 0; i < values.Count; i++)
{
    values[i] *= 3;
}

// E.g. second try
foreach (KeyValuePair<int, double> kv in list)
{
    kv.Value *= 3;
}

由于List已经排序,因此应该可以遍历它同时更新值(而不是键)。从实现的角度来看,它看起来并不存在问题,但由于某种原因,似乎没有提供功能。

这也不是一个简单的例子,因为可以使用相同的方法从已知位置迭代到该范围内的另一个修改值。

有没有办法在C#中使用.NET中的任何键控集合而不使用第三方库?

谢谢

Jeeves的

3 个答案:

答案 0 :(得分:3)

最简单的解决方案是将值包装在引用类型中。

class WrappedInt { public int Value; }
Dictionary<TKey, WrappedInt> dictionary = ...;
foreach (var wrappedVal in dictionary.Values)
{
    wrappedVal.Value *= 3;
}
// or 
foreach (var wrappedVal in dictionary)
{
    wrappedVal.Value.Value *= 3;
}

此方法适用于任何集合(列表或字典)。在没有包装器的情况下通过设计实现此目的的少数集合之一是LinkedList

当然,如果您有一个非常特定的集合(由某个外部组件创建),您可以始终回退使用反射并直接在底层存储中更改值。

答案 1 :(得分:1)

SortedList<int, double> list = new SortedList<int,double>
{
    { 1, 3.14 },
    { 2, 1.618 },
};

var newList = new SortedList<int, double>(list.ToDictionary(kv => kv.Key, kv => kv.Value * 3)).Dump();

根据docs,SortedList构造函数为O(n) .ToDictionary在引擎盖下循环遍历Dictionary.Add。当容量足够source

时,Dictionary.Add为O(1)

O(n)* O(1)仍然是O(n)所以你很好: - )

使用这种方法,空间要求当然加倍。

答案 2 :(得分:0)

在C#中访问Dictionary元素接近于O(1)的常量时间。

您可以通过调用Dictionary.Values并更新它们来获取值列表:

foreach(var value in valuesDict.Values)
{
    // update value;
}

获取此属性(Dictionary.Values)的值为also an O(1) operation.