繁体   English   中英

用于 Excel 克隆的正确数据结构

[英]The right data structure to use for an Excel clone

假设我正在 C# 中处理 Excel 克隆。 我的网格表示如下:

private struct CellValue
{
    private int column;
    private int row;
    private string text;
}
private List<CellValue> cellValues = new List<CellValue>();

每次用户添加文本时,我只需将 package 作为 CellValue 并将其添加到 cellValues 中。 给定一个 CellValue 类型,我可以在 O(1) 时间内确定它的行和列,这很棒。 但是,给定一列和一行,我需要遍历整个 cellValues 以查找该列和行中的文本,这非常慢。 另外,给定一个文本,我也需要遍历整个内容。 是否有任何数据结构可以在 O(1) 时间内完成所有 3 个任务?

更新:浏览一些答案,我认为我没有找到我喜欢的答案。 我可以吗:

  1. 不要保留超过 2 个 CellValue 副本,以避免同步它们。 在 C 世界中,我会很好地使用指针。
  2. 可以动态添加行和列(与 Excel 不同)。

我会选择一个稀疏数组(链表的链表)以提供最大的灵活性和最小的存储空间。

在此示例中,您有一个行的链接列表,其中每个元素都指向该行中的单元格的链接列表(您可以根据需要反转单元格和行)。

 |
 V
+-+    +---+             +---+
|1| -> |1.1| ----------> |1.3| -:
+-+    +---+             +---+
 |
 V
+-+             +---+
|7| ----------> |7.2| -:
+-+             +---+
 |
 =

每个行元素都有行号,每个单元格元素都有一个指向其行元素的指针,因此从单元格中获取行号是 O(1)。

同样,每个单元格元素都有其列号,也就是 O(1)。

没有简单的方法可以让 O(1) 立即找到给定行/列中的单元格,但是稀疏数组会尽可能快,除非您为每个可能的单元格预先分配信息以便您可以进行索引查找在阵列上 - 这在存储方面会非常浪费。

您可以做的一件事是使一维非稀疏,例如使列成为主数组(而不是链表)并将它们限制为 1,000 - 这将使列查找索引(快速),然后搜索稀疏行。

我认为您永远不会仅仅因为文本可以在多个单元格中重复(与行/列不同)而获得 O(1) 的文本查找。 我仍然相信稀疏数组将是搜索文本的最快方式,除非您在另一个数组中维护所有文本值的排序索引(同样,这可以使其更快,但会以大量内存为代价)。

我认为您应该使用索引的 collections 之一以使其工作相当快,完美的一个是KeyedCollection

您需要通过扩展此 class 来创建自己的集合。 这样,您的 object 仍将包含行和列(因此您不会丢失任何内容),但您将能够搜索它们。 可能您将不得不创建一个 class 封装(行、列)并将其设为键(因此使其不可变并覆盖等于并获取 hash 代码)

我会创建

 Collection<Collection<CellValue>> rowCellValues = new Collection<Collection<CellValue>>();

Collection<Collection<CellValue>> columnCellValues = new Collection<Collection<CellValue>>();

外部集合的每一行或每一列都有一个条目,由行号或列号索引,内部集合具有该行或列中的所有单元格。 这些 collections 应作为创建新 CellValue 对象的过程的一部分进行填充。

rowCellValues[newCellValue.Row].Add(newCellValue);
columnCellValues[newCellValue.Column].Add(newCellValue);

这有过早优化的味道。

也就是说,excel 的一些特性对于选择良好的结构很重要。

首先是 excel 以适度非线性的方式使用细胞。 解析公式的过程涉及以有效的随机顺序遍历电子表格。 该结构将需要一种机制来轻松地廉价地查找随机键的值,将它们标记为脏、已解决或由于循环引用而无法解决。 它还需要一些方法来知道何时没有更多未解析的单元格,以便它可以停止工作。 任何涉及链表的解决方案都可能不是最佳解决方案,因为它们需要线性扫描才能获得这些单元格。

另一个问题是 excel 一次显示一系列单元格。 这可能看起来微不足道,在很大程度上确实如此,但如果应用程序可以一次性提取绘制一系列单元格所需的所有数据,那肯定是理想的。 其中一部分可能是跟踪行和列的显示高度和宽度,以便显示系统可以在范围内迭代,直到收集到所需的单元格宽度和高度。 需要以这种方式进行迭代可能会排除使用散列策略来稀疏存储单元格。

最重要的是,电子表格的代表性 model 存在一些弱点,可以通过采用稍微不同的方法更有效地解决这些弱点。

例如,列聚合有点笨拙。 在 excel 中实现列总计很容易,但它有一种神奇的行为,在大多数时间但并非所有时间都有效。 例如,如果您在聚合区域中添加一行,则对该聚合的进一步计算可能会继续有效,也可能不有效,具体取决于您添加它的方式。 如果您复制并插入一行(并替换值)一切正常,但如果您将单元格剪切并粘贴一行,事情就不会那么顺利了。

鉴于数据是二维的,我将有一个二维数组来保存它。

好吧,您可以将它们存储在三个字典中:两个Dictionary<int,CellValue>对象用于行和列,一个Dictionary<string,CellValue>用于文本。 但是,您必须小心地保持所有三个同步。

我不确定我不会只是 go 有一个大的二维数组......

如果它是精确克隆,则为 CellValue[256] arrays 的数组支持列表。 Excel 有 256 列,但行数可以增长。

如果可以“动态”添加行和列,则不应将行/列存储为单元格的数字属性,而是作为对行或列 object 的引用。

例子:

private struct CellValue
{
  private List<CellValue> _column;
  private List<CellValue> _row;
  private string text;

  public List<CellValue> column {
     get { return _column; }
     set {
         if(_column!=null) { _column.Remove(this); }
         _column = value;
         _column.Add(this);
        }
     }

  public List<CellValue> row {
     get { return _row; }
     set {
         if(_row!=null) { _row.Remove(this); }
         _row = value;
         _row.Add(this);
        }
     }
}

private List<List<CellValue>> MyRows    = new List<List<CellValue>>;
private List<List<CellValue>> MyColumns = new List<List<CellValue>>;

每行和列 object 都实现为 CellValue 对象的列表。 这些是无序的——特定行中单元格的顺序与列索引不对应,反之亦然。

每个工作表都有一个行列表和一个列列表,按工作表的顺序排列(如上所示为 MyRows 和 MyColumns)。

这将允许您重新排列和插入新的行和列,而无需循环和更新任何单元格。

删除行应遍历行上的单元格,并在删除行本身之前将它们从各自的列中删除。 反之亦然。

要查找特定的 Row 和 Column,请找到适当的 Row 和 Column 对象,然后找到它们共同包含的 CellValue。

例子:

public CellValue GetCell(int rowIndex, int colIndex) {
  List<CellValue> row = MyRows[rowIndex];
  List<CellValue> col = MyColumns[colIndex];
  return row.Intersect(col)[0];
  }

(我对 .NET 3.5 中的这些扩展方法有点模糊,但这应该在球场上。)

如果我没记错的话,有一篇关于 Visicalc 是如何做到的文章,可能在 80 年代初的 Byte Magazine 上。 我相信这是某种稀疏数组。 但我认为上下和左右都有链接,因此任何给定的单元格都有一个指向其上方单元格的指针(无论可能有多少单元格),在它的下方,在它的左侧,在它的右边。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM