【数据结构】稀疏结构及稀疏矩阵的压缩存储,矩阵的(快速)转置

原创 2016年05月30日 17:18:34

在矩阵中,有一类很重要的矩阵,就是-----稀疏矩阵。


所谓的稀疏矩阵呢,就是指的是,在矩阵中,有效的数据个数远远小于无效的数据个数(并且这些数据排列顺序没有规律)。我们下面先举个稀疏矩阵的例子:

wKiom1cNoXqgYIYUAAAPLNUT7Mw525.png

有效数据个数仅仅6个,其余都为无效数据0.


那我们将稀疏矩阵存在压缩矩阵中,设定一个三元组,使用{row,col,value}存储每一个有效数据,三元组按原矩阵中的位置,以行优先级先后顺序依次存放。

我们建立一个结构体:

struct Triple//定义一个三元组,用来存储稀疏矩阵的x,y,坐标值
{
    int _row;
    int _col;
    T _value;
};

将每一个有效数据(三元组)存在顺序表vector中,打印数据就按照顺序表的特点打印。

矩阵的转置:


wKiom1cNoyLgdIoCAAAda7pd8GI613.png

所以,转置就是将原矩阵的行、列对换,也就是将[i][j]和[j][i]位置上的数据对换。


代码如下:


#include<vector>

template<class T>
struct Triple//定义一个三元组,用来存储稀疏矩阵的x,y,坐标值
{
    int _row;
    int _col;
    T _value;

    Triple(int row, int col, int value)
        :_row(row)
        , _col(col)
        , _value(value)
    {}
};



template<class T>
class SparseMatrix
{
public:
    SparseMatrix(T* a, int m, int n, const T& invalid)
        
    {
        for (int i = 0; i < m; i++)
        {
            for (int j = 0; j < n; j++)
            {
                if (invalid != a[i*n+j])
                {
                         //将每一个有效数据(三元组)存在顺序表vector中
                    Triple <T> tmp(i, j, a[i*n + j]);
                    
                    _a.push_back(tmp);                    
                }
                
            }
        }    
    }


    //用坐标形式打印稀疏矩阵
    void Display(int m, int n, const T& invalid)
    {
        cout << "用坐标形式打印稀疏矩阵" << endl;
        cout <<"{" <<"x," << "  " << "y," << "  " << "value" <<"}" <<endl;
        for (int k = 0; k < _a.size(); k++)
        {
  
          cout << "{" << _a[k]._row << ",  " << 
_a[k]._col << ",  " << _a[k]._value << "  " << 
"}" << endl;
        }                                
    }

    //用矩阵形式打印稀疏矩阵
    void DisplayMatrix(int m, int n, const T& invalid)
    {        
        cout << "用矩阵形式打印稀疏矩阵" << endl;
        int k = 0;
        for (int i = 0; i < m; i++)
        {
            for (int j = 0; j < n; j++)
            {        
                
                if (k<_a.size()&&_a[k]._row == i && _a[k]._col == j)
                {
                    cout << _a[k]._value << "  ";
                    k++;
                }
                else
                {
                    cout << invalid << "  ";
                }                                
            }
            cout << endl;
        }
    }
    
    SparseMatrix<T> Transport(T* a, int m, int n, const T& invalid);

protected:
    vector <Triple <T>> _a;
    
};



void Test()
{

    int a[6][5] = {
                    { 1, 0, 3, 0, 5 },
                    { 0, 0, 0, 0, 0 },
                    { 0, 0, 0, 0, 0 },
                    { 2, 0, 4, 0, 6 },
                    { 0, 0, 0, 0, 0 },
                    { 0, 0, 0, 0, 0 },
                  }; 

    int m = 6;
    int n = 5;
    SparseMatrix<int> sm((int*)a, m, n, 0);
    sm.Display( m, n, 0);
    sm.DisplayMatrix( m, n, 0);
    SparseMatrix<int> sm1((int*)a, m, n, 0);
    sm1 = sm.Transport((int*)a, m, n, 0);

    
    sm1.Display( n, m, 0);
    sm1.DisplayMatrix(n, m, 0);
}

int main()
{
    Test();
    system("pause");
    return 0;
}

下面我们单独说说如何实现矩阵转置的代码:


现在我们知道,此时的矩阵不再是几乘几,而是以一个三元组Triple形式存储的。那我们就没有办法像线性代数中将矩阵的第一行放在新矩阵的第一列,以此重复。


而是换一种思路来想这个问题:

(1)转置后的矩阵第一列是由之前矩阵的第一行得来的。

(2)之前是行优先存储,现在要用列优先存储。


那我们现在想,是不是可以遍历矩阵的第0列分别拿该列的元素去与三元组中的元素比较是否满足是第0列。即:拿第0列元素,去与三元组比较,发现1,2是第0列。则把1,2按顺序存放在新的三元组中。再第一列,第二列,……时间复杂度:O(列数*有效数据个数)。


我们现在看下代码:

template<class T>
SparseMatrix<T> SparseMatrix<T>::Transport(T* a, int m, int n, const T& invalid)
{
    cout << "矩阵转置:" << endl;
    SparseMatrix<T> ret;
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < _a.size(); j++)
        {
            if (_a[j]._col == i)
            {
                Triple<T> tri(_a[j]._col, _a[j]._row, _a[j]._value);
                ret._a.push_back(tri);
            }
        }
    }
    return ret;
}


快速转置:

思想是统计好转置后上一行的元素个数RowCounts,及转置后每行在压缩矩阵的起始位置RowStarts,由此确定下一行元素位置。

template<class T>
SparseMatrix<T> SparseMatrix<T>::FastTransport(int m, int n, const T& invalid)
{
    SparseMatrix<T> ret;
    ret._a.resize(_a.size());
        
    vector<int> RowCounts;     //转置后每行元素的个数
    RowCounts.resize(n);
    RowCounts.assign(n, 0);
    for (int i = 0; i < _a.size(); i++)
    {
        RowCounts[_a[i]._col]++;    //元素相同列的话,列号对应的顺序表加加
    }
        
    vector<int> RowStarts;
    RowStarts.resize(n);
    RowStarts.assign(n, 0);
    
    //下一行元素的起始位置等于上一行中的元素个数与上一行元素的起始位置之和
    for (int j = 0; j < n-1; j++) 
    {
        RowStarts[j + 1] = RowStarts[j] + RowCounts[j];
    }
     
    //行列数互换
    for (int k = 0; i < _a.size(); k++)
    {
        ret._a[RowStarts[_a[k]._col]]._col = _a[k]._row;
        ret._a[RowStarts[_a[k]._col]]._row = _a[k]._col;
        ret._a[RowStarts[_a[k]._col]++]._value = _a[k]._value;
    }
    return ret;
}

则:时间复杂度为:

2*元素个数+列数

即:O(元素个数+列数)

本文出自 “Han Jing's Blog” 博客,请务必保留此出处http://10740184.blog.51cto.com/10730184/1763284

版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

【数据结构】稀疏矩阵的压缩存储和转置算法(C++代码)

稀疏矩阵的压缩存储和转置算法

数据结构 对称矩阵的压缩存储与稀疏矩阵的转置

对称矩阵的压缩存储与稀疏矩阵的转置

C语言数据结构——稀疏矩阵的快速转置

#include #include #include#define OK 1 #define MAXSIZE 12500typedef int Status;typedef struct { ...

数据结构--稀疏矩阵(转置)

/ sparse matrix.cpp : Defines the entry point for the console application. /*-----CODE FOR FUN------...

数据结构稀疏矩阵的实现及转置

昨天在机房没有做出来,我想了很久,都不知道原因。在机房时就是不明白那些Triple和TSMatrix结构之间的联系。不知道如何进行数据输入和输出。       且我一直在思考如果创建矩阵成功,又如何...

《数据结构》复习之稀疏矩阵(矩阵转置)

矩阵的表示方法 矩阵的转置矩阵的表示方法  矩阵在数据结构中常用二维数组(int A[m][n],m为列的元素个数,n为行的元素个数)表示,这也是最简单的表示方法。   然而当矩阵中含有大量0元素时...

数据结构——稀疏矩阵的压缩存储

1.矩阵的压缩存储         对于一些阶树很高的矩阵,如果有许多值相同的元素或者是零元素,可以对其进行压缩以节省存储空间。可以为多个值相同的元只分配一个存储空间;对零元不分配空间。 2.稀疏...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)