最佳二维数据结构
Posted
技术标签:
【中文标题】最佳二维数据结构【英文标题】:An Optimum 2D Data Structure 【发布时间】:2010-01-03 17:03:18 【问题描述】:我想了很多,但还没有真正想出什么。
假设我想要一个 m X n 的元素集合,该集合可按 O(m*n) 下的任何列和任何行排序,并且还能够插入或删除 O(m+n) 或更小的行...有可能吗?
我想出的是一个链接网格,其中节点被插入到一个向量中,所以我有它们的索引,并索引第一行和第一列以消除在任何一个方向上遍历列表的必要性.使用我的方法,我已经实现了上述复杂性,但我只是想知道是否可以通过非常数因子进一步降低它。
可排序性示例:
1 100 25 34
2 20 15 16
3 165 1 27
按第三行排序:
25 1 34 100
15 2 16 20
1 3 27 165
按第一列排序:
1 3 27 165
15 2 16 20
25 1 34 100
【问题讨论】:
不,一点也不。我的数据结构课是去年。但如果是的话,这有关系吗?我问的是解决方案还是答案?关于编程问题是否可能在一定的时间复杂度内以及使用哪些数据结构在您的道德规范中仍然可以回答的问题不是吗?为什么没有提到应用程序的问题会立即被标记为作业? 【参考方案1】:我将创建两个索引数组,一个用于列,一个用于行。所以对于你的数据
1 100 25 34
2 20 15 16
3 165 1 27
你创建了两个数组:
cols = [0, 1, 2, 3]
rows = [0, 1, 2]
然后,当您想按第 3 行对矩阵进行排序时,您保持原始矩阵不变,但只需相应地更改索引数组:
cols = [2, 0, 3, 1]
rows = [0, 1, 2]
现在的诀窍是通过一种间接方式访问您的矩阵。因此,不是使用m[x][y]
访问它,而是通过m[cols[x]][rows[y]]
访问它。在执行 rows/cols 数组的重新排序时,您还必须使用 m[cols[x]][rows[y]]
。
这种方式排序是O(n*log(n))
,访问是O(1)
。
对于数据结构,我会使用一个带有指向另一个数组的链接的数组:
+-+
|0| -> [0 1 2 3 4]
|1| -> [0 1 2 3 4]
|2| -> [0 1 2 3 4]
+-+
要插入一行,只需将其插入到最后一个位置并相应地更新rows
索引数组,并使用正确的位置。例如。当rows
是[0, 1, 2]
并且您想将它插入到前面时,行将变为[3, 0, 1, 2]
。这样插入一行是O(n)
。
要插入一列,您还可以将其添加为最后一个元素,并相应地更新 cols。插入列是O(m)
,行是O(n)
。
删除也是O(n)
或O(m)
,这里只要把要删除的列/行替换为最后一个,然后从索引数组中删除索引即可。
【讨论】:
但是一个元素的插入和删除是O(m*n),而一行的插入和删除是O(m^2*n).. 嗨 Vanwaril,我已经更新了条目,我认为插入和删除也可以在 O(n) 或 O(m) 中完成。【参考方案2】:只是补充一下 martinus 和 Mike 的答案:本质上,您需要的是旋转,这是他们建议的,并且是几乎所有涉及矩阵的数值算法中使用的一种众所周知的技术。例如,您可以快速搜索“带部分旋转的 LU 分解”和“带完全旋转的 LU 分解”。存储排列的附加向量称为“枢轴”。
【讨论】:
【参考方案3】:如果遇到这个问题,我会创建行和列重映射向量。例如。要对行进行排序,我会像往常一样确定行顺序,但不是复制行,而是更改行重映射向量。
看起来像这样:
// These need to be set up elsewhere.
size_t nRows, nCols;
std::vector<T> data;
// Remapping vectors. Initially a straight-through mapping.
std::vector<size_t> rowMapping(nRows), colMapping(nCols);
for(size_t y = 0; y < nRows; ++y)
rowMapping[y] = y;
for(size_t x = 0; x < nCols; ++x)
colMapping[x] = x;
// Then you read data(row, col) with
T value = data[rowMapping[row] * nCols + colMapping[col]];
附:一个小的优化是将指针存储在rowMapping
而不是索引中。这可以让您执行T value = rowMapping[row][colMapping[col]];
,但是,每次data
的尺寸发生变化时,您都必须重新计算指针,这可能容易出错。
【讨论】:
同样,这个问题是虽然访问和排序很快,但插入和删除却不是。 如果您预先分配行和列,则插入和删除是 O(n)。此外,没有将快速插入和删除指定为要求。【参考方案4】:您可以使用哈希表并插入 (i,j) -> 节点,其中 (i,j) 是包含 2 个整数的 2 元组。您可以编写自己的自定义类,为该类定义 Equals 方法和 GetHash() 方法……或者 Python 免费提供给您。
现在......你到底是什么意思 - 按行或列排序?请举例说明值!
【讨论】:
想到了一个哈希表,结果排序变得很麻烦。 不,一点也不麻烦,但是需要O(m*n)。 您需要查看第 n 行或第 n 列,将其提取为列表,对其进行排序,推导出排列列表,例如(1,2,5,4,3,6) 显示索引的位置,然后将此顺序应用于字典中的所有元素。这是一个有趣的问题,Python 实现可以相当简洁。 嗯,你说的是使用哈希表而不是我的向量,但这没有区别,因为向量仍然是摊销的常数时间插入。节点结构仍然必须有 4 个指针,排序和插入逻辑仍然需要处理这些。 是的。我这样做的方式是通过索引向量——我根据排列列表的值对索引进行排序。我的实现是用 C++ 编写的,虽然很长,呵呵 :)【参考方案5】:也许通过为它创建一个小型数据库?
数据库排序算法可能比重新发明***更好。 mysql 会做。为了获得性能,可以在内存中创建表。然后,您可以像普通表一样对列进行索引,并让数据库引擎完成脏活(排序等)。然后你就可以收获结果了。
【讨论】:
问题本质上是关于如何实现提供这些服务的数据库系统。说“使用数据库”是没有答案的。 这取决于你如何理解这个问题,如果作为 a)“我如何对我的 MxN 矩阵进行排序?”,或者作为 b)“矩阵排序算法本质上是如何工作的?”跨度>以上是关于最佳二维数据结构的主要内容,如果未能解决你的问题,请参考以下文章