Data structures 是否有用于行和列交换的有效数据结构?

Data structures 是否有用于行和列交换的有效数据结构?,data-structures,computer-science,Data Structures,Computer Science,我有一个数字矩阵,我希望能够: 交换行 交换列 如果我使用一个指向行的指针数组,那么我可以很容易地在O(1)中的行之间切换,但是交换一个列就是O(N),其中N是行的数量 我有一种明显的感觉,没有一种双赢的数据结构可以为两种操作都提供O(1),尽管我不知道如何证明它。还是我错了?没有完全考虑到这一点: 我认为你关于行指针的想法是正确的开始。然后,为了能够“交换”列,我只需要使用另一个列数大小的数组,并在每个字段中存储列当前物理位置的索引 m = [0] -> 1 2 3 [1] ->

我有一个数字矩阵,我希望能够:

  • 交换行
  • 交换列
  • 如果我使用一个指向行的指针数组,那么我可以很容易地在O(1)中的行之间切换,但是交换一个列就是O(N),其中N是行的数量


    我有一种明显的感觉,没有一种双赢的数据结构可以为两种操作都提供O(1),尽管我不知道如何证明它。还是我错了?

    没有完全考虑到这一点:

    我认为你关于行指针的想法是正确的开始。然后,为了能够“交换”列,我只需要使用另一个列数大小的数组,并在每个字段中存储列当前物理位置的索引

    m = 
    [0] -> 1 2 3
    [1] -> 4 5 6
    [2] -> 7 8 9 
    
    c[] {0,1,2}
    
    现在要交换第1列和第2列,只需将c更改为{0,2,1}

    当你想读第1行的时候

    for (i=0; i < colcount; i++) {
       print m[1][c[i]];
    }
    
    for(i=0;i
    这里只是一个随机的例子(没有经验表明这是多么有效,而且这是一个没有咖啡的深夜):

    我认为矩阵的内部是一个哈希表,而不是数组

    阵列中的每个单元都有三条信息:

  • 单元格所在的行
  • 单元格所在的列
  • 单元格的值
  • 在我看来,这很容易用元组
    ((i,j),v)
    来表示,其中
    (i,j)
    表示单元的位置(第i行,第j列),和v

    这将是一个矩阵的正常表示。但是,让我们把这些想法引入这里。而不是<代码> i>代码>将行表示为一个位置(即在1之前的0,在3之前的2之前),让我们考虑<代码> i <代码>是它对应行的某种规范标识符。让我们对
    j
    执行同样的操作。(在最一般的情况下,
    i
    j
    可以不受限制,让我们假设一个简单的情况,它们将保持在M x N矩阵的[0..M]和[0..N]范围内,但不表示单元的实际坐标)

    现在,我们需要一种方法来跟踪一行的标识符,以及与该行关联的当前索引。这显然需要一个键/值数据结构,但由于索引的数量是固定的(矩阵通常不会增长/收缩),并且只处理整数索引,因此我们可以将其实现为固定的一维数组。对于M行的矩阵,我们可以有(在C中):

    对于第m行,
    RowMap[m]
    给出当前矩阵中该行的标识符

    我们将对列使用相同的内容:

    int ColumnMap[N];
    
    其中,
    ColumnMap[n]
    是第n列的标识符

    现在回到我在开头提到的哈希表:

    因为我们有完整的信息(矩阵的大小),我们应该能够生成一个完美的散列函数(没有冲突)。这里有一种可能性(对于大小适中的阵列):

    如果这是哈希表的哈希函数,那么对于大多数大小的数组,我们应该得到零冲突。这允许我们在O(1)时间内从哈希表读取/写入数据

    酷的部分是将每一行/列的索引与哈希表中的标识符连接起来:

    // row and column are given in the usual way, in the range [0..M] and [0..N]
    // These parameters are really just used as handles to the internal row and
    // column indices
    int MatrixLookup(int row, int column)
    {
        // Get the canonical identifiers of the row and column, and hash them.
        int canonicalRow = RowMap[row];
        int canonicalColumn = ColumnMap[column];
        int hashCode = Hash(canonicalRow, canonicalColumn);
    
        return HashTableLookup(hashCode);
    }
    
    现在,由于矩阵的接口只使用这些句柄,而不使用内部标识符,因此行或列的
    swap
    操作对应于
    RowMap
    ColumnMap
    数组中的简单更改:

    // This function simply swaps the values at
    // RowMap[row1] and RowMap[row2]
    void MatrixSwapRow(int row1, int row2)
    {
        int canonicalRow1 = RowMap[row1];
        int canonicalRow2 = RowMap[row2];
    
        RowMap[row1] = canonicalRow2
        RowMap[row2] = canonicalRow1;
    }
    
    // This function simply swaps the values at
    // ColumnMap[row1] and ColumnMap[row2]
    void MatrixSwapColumn(int column1, int column2)
    {
        int canonicalColumn1 = ColumnMap[column1];
        int canonicalColumn2 = ColumnMap[column2];
    
        ColumnMap[row1] = canonicalColumn2
        ColumnMap[row2] = canonicalColumn1;
    }
    
    所以应该是这样的-一个具有O(1)访问和变异的矩阵,以及O(1)行交换和O(1)列交换。当然,即使是O(1)哈希访问也会比基于数组的访问的O(1)慢,并且会使用更多的内存,但至少行/列之间是相等的


    当涉及到矩阵的具体实现方式时,我尽量做到不可知论,所以我写了一些C。如果你喜欢另一种语言,我可以修改它(如果你能理解的话,这将是最好的),但我认为它是非常自描述性的,尽管我不能保证它在C语言中是正确的,因为我现在是个C++的家伙,他们现在想表现得像个C人(我提到过我没有咖啡吗?)就个人而言,用完整的OO语言编写会使entrie设计更加合理,也会使代码更加美观,但正如我所说的,这是一个快速启动的实现。

    对于大多数数组来说,这可能比转置矩阵和
    memcpy
    -交换行慢几个数量级。@Brian:实际上,就交换而言,它的速度非常快——它只是INT之间的交换操作!至于代码的其余部分,它也可以非常快,因为我们可以很容易地实现一个非常专用的哈希表-该表将只是一个二维数组。因此,在我的代码的性能版本中,访问归结为4个数组访问(一个用于
    行映射
    ,一个用于
    列映射
    ,两个用于哈希表),而不是直接数组的2个数组访问。当然,这是速度的两倍,但是对于更复杂的操作,我的代码可以快得多。arr[y][x]可以优化为arr[(y*sizeof x)+x],并且sizeof x甚至在编译时是已知的。但现在我们讨论的是微小的性能差异。@Brian:非常正确。我太习惯于在其他语言中使用交错数组,其中2d数组(静态或动态)实际上是指向数组的指针数组。
    // row and column are given in the usual way, in the range [0..M] and [0..N]
    // These parameters are really just used as handles to the internal row and
    // column indices
    int MatrixLookup(int row, int column)
    {
        // Get the canonical identifiers of the row and column, and hash them.
        int canonicalRow = RowMap[row];
        int canonicalColumn = ColumnMap[column];
        int hashCode = Hash(canonicalRow, canonicalColumn);
    
        return HashTableLookup(hashCode);
    }
    
    // This function simply swaps the values at
    // RowMap[row1] and RowMap[row2]
    void MatrixSwapRow(int row1, int row2)
    {
        int canonicalRow1 = RowMap[row1];
        int canonicalRow2 = RowMap[row2];
    
        RowMap[row1] = canonicalRow2
        RowMap[row2] = canonicalRow1;
    }
    
    // This function simply swaps the values at
    // ColumnMap[row1] and ColumnMap[row2]
    void MatrixSwapColumn(int column1, int column2)
    {
        int canonicalColumn1 = ColumnMap[column1];
        int canonicalColumn2 = ColumnMap[column2];
    
        ColumnMap[row1] = canonicalColumn2
        ColumnMap[row2] = canonicalColumn1;
    }