数据结构问题

时间:2008-11-29 21:37:30

标签: c# .net

我有一个包含大量行和一个数字列的数据库表,我想在内存中表示这些数据。我可以使用一个大整数数组,这会非常快,但行数可能太大了。

大多数行(超过99%)的值为零。是否有一个有效的数据结构,我只能为具有非零值的行分配内存,并且几乎与数组一样快?

更新:作为一个例子,我尝试的一件事是Hashtable,读取原始表并添加任何非零值,由原始表中的行号键入。如果找不到请求的索引,我得到的函数返回0,或者Hashtable中的值。与常规阵列相比,这种方法有效,但是很慢 - 我可能做得不对。

更新2:这是示例代码。

private Hashtable _rowStates;
private void SetRowState(int rowIndex, int state)
{
    if (_rowStates.ContainsKey(rowIndex))
    {
        if (state == 0)
        {
            _rowStates.Remove(rowIndex);
        }
        else
        {
            _rowStates[rowIndex] = state;
        }
    }
    else
    {
        if (state != 0) 
        {
            _rowStates.Add(rowIndex, state);
        }
    }
}
private int GetRowState(int rowIndex)
{
    if (_rowStates.ContainsKey(rowIndex))
    {
        return (int)_rowStates[rowIndex];
    }
    else
    {
        return 0; 
    }
}

8 个答案:

答案 0 :(得分:3)

这是稀疏数据结构的一个示例,有多种方法可以实现这种稀疏数组(或矩阵) - 这一切都取决于您打算如何使用它。两种可能的策略是:

  • 仅存储非零值。对于不同于零的每个元素存储一对(索引,值),默认情况下所有其他值都已知为零。您还需要存储元素总数。
  • 压缩连续的零值。存储多个(计数,值)对。例如,如果连续有12个零后跟200和另外22个零,则存储(12,0),(1,200),(22,0)。

答案 1 :(得分:2)

我希望非零值的地图/字典/散列表应该是一种快速而经济的解决方案。

在Java中,使用Hashtable类会引入锁定,因为它应该是线程安全的。也许类似的东西会减慢你的实施速度。

---更新:使用Google-fu建议C#Hashtable 会产生线程安全开销。请尝试使用词典。

答案 2 :(得分:1)

你究竟要如何实现它取决于你的要求,它是内存和速度之间的权衡。纯整数数组是最快的,具有恒定的复杂性查找。

使用基于哈希的集合,例如Hashtable或Dictionary(Hashtable似乎比其他人指出的更慢但是线程安全)会为你的稀疏数据结构提供非常低的内存使用量但是可能有点执行查找时更昂贵。您为每个索引和非零值存储键值对。

您可以使用ContainsKey来确定密钥是否存在,但使用TryGetValue进行检查并一次性获取数据要快得多。对于密集数据,捕获缺失元素的异常是值得的,因为这只会在特殊情况下产生成本而不是每次查找。

再次编辑 因为我感到困惑 - 这会教会我在应该睡觉时发帖。

答案 3 :(得分:1)

你正在使用Hashtable支付拳击咒语。尝试切换到Dictionary<int, int>。另外,我们谈论了多少行 - 你需要多快?

答案 4 :(得分:0)

如果特定行包含非零值,则为非零值和位数组保持指示符创建整数数组。

你可以在第一个数组中找到必要的元素,将第二个数组中的位从0开始累加到行索引位置。

答案 5 :(得分:0)

我不确定此解决方案的效率,但您可以尝试。所以它取决于你将使用它的场景,但我会在这里写下我想到的其中两个。第一个解决方案是,如果你只有一个整数字段,你可以简单地使用通用的整数列表:

List<int> myList = new List<int>();

第二个几乎相同,但您可以创建自己类型的列表,例如,如果您有两个字段,count和非零值,您可以创建一个具有两个属性的类,然后您可以创建其中的类和商店信息列表。但您也可以尝试通用链接列表。所以解决方案二的代码可以是这样的:

public class MyDbFields
{
    public MyDbFields(int count, int nonzero)
    {
        Count = count;
        NonZero = nonzero;
    }

    public int Count { get; set; }
    public int NonZero { get; set; }
}

然后你可以创建一个这样的列表:

List<MyDbFields> fields_list = new List<MyDbFields>();

然后用数据填充它:

fields_list.Add(new MyDbFields(100, 11));

我不确定这是否能完全帮助您解决问题,而只是我的建议。

答案 6 :(得分:0)

如果我理解正确,你不能只选择非零行,因为对于每一行索引(也就是PK值),你的数据结构必须不仅能够报告值,还能报告它是否在那里一点都不因此,假设您在数据结构中找不到它可能不是一个好主意。

只是为了确保 - 我们在这里谈论了多少行?百万?一百万个整数只占用4MB RAM作为阵列。真的不多。我想它必须至少有100'000'000行。

基本上我建议使用整数对的排序数组来存储非零值。每对中的第一个元素是PK值,这就是数组的排序依据。第二个元素是价值。当然,您可以使DB选择仅返回这些非零值。由于数组将被排序,您将能够使用二进制搜索来查找您的值。

如果PK值中没有“漏洞”,那么除此之外您唯一需要的是最小和最大PK值,以便您可以确定给定索引是否属于您的数据集。

如果在使用的PK值之间存在未使用的PK值,则需要一些其他机制来确定哪些PK值有效。也许是一个位掩码或另一个有效(或无效,以较少者为准)PK值的数组。

如果选择bitmask方式,还有另外一个想法。每个PK值使用两位。第一位将显示PK值是否有效。第二位将显示它是否为零。将所有非零值存储在另一个数组中。然而,这将具有以下缺点:您将不知道哪个数组项对应于哪个位掩码条目。你必须从头开始一直计算。这可以通过一些索引来缓解。比如,对于值数组中的每1000个条目,您存储另一个整数,该整数告诉您此条目在位掩码中的位置。

答案 7 :(得分:0)

也许您正在查找错误的区域 - 您为每个值存储的是数据库行的行号,这表明您可能只是使用它来检索行? 为什么不尝试在数字列上索引表 - 这将为任何给定的数值提供快速访问表行(这似乎是最终的目标?)如果它仍然太慢你可以将索引本身移动到记忆等 我的观点是,你的数据库可以比你更优雅地解决这个问题。