C# 用于Excel克隆的正确数据结构
假设我正在用C#编写一个Excel克隆。 我的网格表示如下:C# 用于Excel克隆的正确数据结构,c#,data-structures,.net-2.0,C#,Data Structures,.net 2.0,假设我正在用C#编写一个Excel克隆。 我的网格表示如下: private struct CellValue { private int column; private int row; private string text; } private List<CellValue> cellValues = new List<CellValue>(); private结构单元值 { 私有int列; 私人int row; 私有字符串文本; } 私有
private struct CellValue
{
private int column;
private int row;
private string text;
}
private List<CellValue> cellValues = new List<CellValue>();
private结构单元值
{
私有int列;
私人int row;
私有字符串文本;
}
私有列表单元格值=新列表();
每次用户添加文本时,我只是将其打包为CellValue并添加到CellValue中。给定一个CellValue类型,我可以在O(1)时间内确定它的行和列,这很好。但是,给定一列和一行,我需要循环遍历整个单元格值,以找到该列和该行中的文本,这非常慢。另外,给定一个文本,我也需要循环整个内容。是否有任何数据结构可以让我在O(1)时间内完成所有3项任务
更新:
翻阅一些答案,我想我没有找到一个我喜欢的答案。我可以:
假设数据是二维的,我会有一个二维数组来保存它。好吧,你可以将它们存储在三个字典中:两个
字典
对象用于行和列,一个字典
对象用于文本。不过,你必须小心地保持三者同步
我不确定我会不会只使用一个大的二维数组,但是…我认为你应该使用一个索引集合来让它运行得相当快,最完美的是
您需要通过扩展此类来创建自己的集合。这样,您的对象仍将包含行和列(因此您不会丢失任何内容),但您将能够搜索它们。可能您必须创建一个封装类(行、列)并将其作为键(因此使其不可变并覆盖equals并获取哈希代码)如果它是一个精确的克隆,则需要一个以数组为基础的CellValue[256]数组列表。Excel有256列,但行数可以增加。我会创建
Collection<Collection<CellValue>> rowCellValues = new Collection<Collection<CellValue>>();
我会选择一个稀疏数组(链表的链表),以提供最大的灵活性和最小的存储空间 在本例中,您有一个行的链接列表,其中每个元素都指向该行中的单元格链接列表(您可以根据需要反转单元格和行) 每个行元素中都有行号,每个单元格元素都有一个指向其行元素的指针,因此从单元格中获取行号是O(1) 类似地,每个单元格元素都有其列号,也就是O(1) 要立即找到给定行/列上的单元格,没有简单的方法获得O(1),但稀疏数组的速度与它将获得的速度一样快,除非您为每个可能的单元格预分配信息,以便您可以在数组上进行索引查找—这在存储方面是非常浪费的 您可以做的一件事是使一维非稀疏,例如使列成为主数组(而不是链表),并将它们限制为1000-这将使列查找索引(fast),然后搜索稀疏行
我不认为仅仅因为文本可以在多个单元格中复制(不同于行/列),就可以得到文本查找的O(1)。我仍然相信稀疏数组将是搜索文本的最快方法,除非您在另一个数组中维护所有文本值的排序索引(同样,这会使它更快,但会以大量内存为代价)。如果行和列可以“动态”添加,然后,不应将行/列存储为单元格的数字属性,而应存储为对行或列对象的引用 例如:
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>>;
public CellValue GetCell(int rowIndex, int colIndex) {
List<CellValue> row = MyRows[rowIndex];
List<CellValue> col = MyColumns[colIndex];
return row.Intersect(col)[0];
}
private结构单元值
{
私有列表_列;
私有列表行;
私有字符串文本;
公共列表列{
获取{return\u column;}
设置{
如果(_column!=null){u column.Remove(this);}
_列=值;
_列。添加(此);
}
}
公共列表行{
获取{return\u row;}
设置{
如果(_row!=null){u row.Remove(this);}
_行=值;
_行。添加(此);
}
}
}
私有列表MyRows=新列表;
私有列表MyColumns=新列表;
每个行和列对象都实现为CellValue对象的列表。这些是无序的——特定行中单元格的顺序与列索引不对应,反之亦然
每个工作表都有一个行列表和一个列列表,按工作表的顺序排列(上面显示为MyRows和MyColumns)
这将允许您重新排列和插入新行和新列,而无需循环和更新任何单元格
删除行时,应在该行上的单元格中循环,并在删除行之前将其从各自的列中删除。对于列,反之亦然
要查找特定的行和列,请查找相应的行和列对象,然后查找它们共同包含的CellValue
例如:
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>>;
public CellValue GetCell(int rowIndex, int colIndex) {
List<CellValue> row = MyRows[rowIndex];
List<CellValue> col = MyColumns[colIndex];
return row.Intersect(col)[0];
}
public-CellValue-GetCell(int-rowIndex,int-colIndex){
列表行=MyRows[rowIndex];
列表列=MyColumns[colIndex];
返回行相交(列)[0];
}
(我对.NET3.5中的这些扩展方法有点模糊,但这应该是大概的。)如果我没记错的话,有一篇关于Visicalc是如何做到这一点的文章,可能是在80年代早期的Byte杂志上。我相信这是某种稀疏的数组。但是我认为有上下左右的链接,所以任何给定的单元格都有一个指向上面单元格(不管有多少单元格)、下面单元格、左边单元格和右边单元格的指针。这有点过早优化的味道 也就是说,excel的一些特性对于选择好的结构非常重要 首先,excel以一种适度非线性的方式使用单元格。解析公式的过程包括遍历排列
public CellValue GetCell(int rowIndex, int colIndex) {
List<CellValue> row = MyRows[rowIndex];
List<CellValue> col = MyColumns[colIndex];
return row.Intersect(col)[0];
}