稀疏矩阵的压缩存储和快速逆置

稀疏矩阵啊,就是矩阵中的有效元素很稀疏,稀疏到什么程度呢?通常认为矩阵中非零元素的总数比上矩阵所有元素总数的值小于等于0.05时,则称该矩阵为稀疏矩阵(sparse matrix)。

我们想,一少部分的有效元素就占用大部分的地址空间,是不是很浪费,那么我们就要想办法让其不浪费那么多,就是压缩存储。之前我们写过对称矩阵的压缩,那是因为对称矩阵的分布很有规律,但是再看一眼稀疏矩阵,除了有效元素少一点,也没有什么规律,那么我们就要把这些元素的行和列也保存进去,也就是定义一个结构体——三元组。

    template<class T>
    struct Trituple           //三元组
    {
        Trituple(size_t row, size_t col, const T& data)
        : _row(row)
        , _col(col)
        , _data(data)
        {}

        size_t _row;
        size_t _col;
        T _data;
    };

之后就是具体的压缩了,也没什么难得,只需定义一个容器,然后将有效元素的三元组保存进去就行了,在这里我们选择的是vector。还原的过程也就是打印。也是用两个循环嵌套,不过这里需要注意一些细节问题,具体的代码中都有注释。

#include<iostream>
using namespace std;

#include<assert.h>
#include<vector>

template<class T>
class SparseMatrix
{
    template<class T>
    struct Trituple           //三元组
    {
        Trituple(size_t row, size_t col, const T& data)
        : _row(row)
        , _col(col)
        , _data(data)
        {}

        size_t _row;
        size_t _col;
        T _data;
    };

public:
    SparseMatrix()
    {}

    // 稀疏矩阵的压缩存储
    SparseMatrix(int* array, size_t row, size_t col, const T& invalid)
        : _row(row)
        , _col(col)
        , _invalid(invalid)
    {
        for (int i = 0; i < _row; i++)
        {
            for (int j = 0; j < _col; j++)
            {
                if (array[i*_col + j] != invalid)//数据有效
                {
                    Trituple<T> temp(i, j, array[i*_col + j]);
                    _vt.push_back(temp);
                }
            }
        }
    }

    // 访问稀疏矩阵中row行col中的元素
    T& Access(int row, int col)
    {
        int index = 0;
        int size = _vt.size();//将其写在外面,不必每一次循环都调用一次size函数
        for (; index <size; index++)
        {
            //注意:这里拿来和row比较的是三元组中的行,不是稀疏矩阵的行数
            if (_vt[index]._row == row&&_vt[index]._col == col)
            {
                return _vt[index]._data;
            }
        }
        return _invalid;
    }

    // 还原稀疏矩阵
    template<class T>
    friend ostream& operator<<(ostream& _cout, SparseMatrix<T>& s)
    {
        int index = 0;//用来遍历vector
        int size = s._vt.size();
        for (int i = 0; i < s._row; i++)
        {
            for (int j = 0; j < s._col; j++)
            {
                //如果三元组未遍历完,并且该位置正好是有效数据,那么输出
                //注意:s----_sm之间的关系,小心,容易出错
                if (index < size && s._vt[index]._row == i && s._vt[index]._col == j)
                {
                    cout << s._vt[index++]._data << " ";//注意将index++
                }
                else//输出无效数据
                    cout << s._invalid << " ";
            }
            cout << endl;
        }
        return _cout;
    }

    // 实现稀疏矩阵的逆置,并给出时间复杂度:列数*有效元素个数
    SparseMatrix<T> Transprot()
    {
        SparseMatrix<T> temp;//创建一个临时对象,保存逆置后的矩阵
        temp._row = _col;//将原矩阵的列数,给成新矩阵的行数
        temp._col = _row;
        temp._invalid = _invalid;

        for (size_t i = 0; i < _col; i++)//按列访问
        {
            int index = 0;
            while (index < _vt.size())
            {
                Trituple<T> &t = _vt[index];//将原来的有效元素另存为一个三元组结构体中
                if (t._col == i)//遍历存储有效数据的三元组结构体,找每一列的有效数据,保存
                {
                    temp._vt.push_back(Trituple<T>(t._col, t._row, t._data));
                }
                index++;
            }
        }
        return temp;
    }

    //实现稀疏矩阵的快速逆置,并给出时间复杂度
    SparseMatrix<T> FastTransprot()
    {
        SparseMatrix<T> temp;//定义一个临时对象保存逆置后的矩阵
        temp._row = _col;
        temp._col = _row;

        //统计原矩阵中每一列的有效个数:[2 0 2 0 2]
        int* pCount = new int[_col];
        memset(pCount, 0, _col*sizeof(int));

        int index = 0;
        while (index < _vt.size())
        {
            pCount[_vt[index]._col]++;
            index++;
        }

        // 原矩阵中的每一列的有效数据在新矩阵中的地址:[0 2 2 4 4]
        int *pAdd = new int[_col];
        memset(pAdd, 0, _col*sizeof(int));
        for (size_t i = 1; i < _col; i++)
        {
            pAdd[i] = pAdd[i - 1] + pCount[i - 1];
        }

        //放置元素
        index = 0;
        Trituple<T> &t = _vt[index];
        while (index<_vt.size())
        {
            //error:Debug Assertion Failed!   可能使用了野指针????
            temp._vt[pAdd[t._col]] = Trituple<T>(t._row, t._col, t._data);
            pAdd[t._col]++;
            index++;
        }

        delete[] pCount;
        delete[] pAdd;

        return temp;
    }


    // 实现稀疏矩阵的加法操作
    SparseMatrix<T> operator+(const SparseMatrix<T>& sp)
    {
        assert(_row == sp._row);
        assert(_col == sp._col);//两个矩阵规格相同才能相加

        SparseMatrix<T> sum;//定义一个和的矩阵,规格和原矩阵一样
        sum._row = _row;
        sum._col = _col;
        sum._invalid = _invalid;

        int iL = 0;//用来遍历存储有效元素的三元组
        int iR = 0;
        int iLAdd = 0;//有效元素在原矩阵中的地址
        int iRAdd = 0;
        while (iL < _vt.size() && iR < sp._vt.size())
        {
            iLAdd = _vt[iL]._row*_col + _vt[iL]._col;
            iRAdd = sp._vt[iR]._row*_col + sp._vt[iR]._col;

            if (iLAdd < iRAdd)
            {
                sum._vt.push_back(_vt[iL++]);
                continue;
            }
            else if (iLAdd > iRAdd)
            {
                sum._vt.push_back(sp._vt[iL++]);
                continue;
            }
            else
            {
                T data = _vt[iL]._data + sp._vt[iR]._data;
                if (data != _invalid)
                {
                    sum._vt.push_back(Trituple<T>(_vt[iL]._row, _vt[iL]._col, data));
                    iL++;
                    iR++;
                }
            }
        }
        if (!_vt.empty())
        {
            while (iL < _vt.size())
            {
                sum._vt.push_back(_vt[iL++]);
            }
            while (iR < _vt.size())
            {
                sum._vt.push_back(sp._vt[iR++]);
            }
        }
        return sum;
    }

private:
    vector<Trituple<T>> _vt; //用vector来保存有效数据的三元组
    size_t _row;//稀疏矩阵的行数
    size_t _col;//稀疏矩阵的列数
    T _invalid;//无效值
};

void FunTest()
{
    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 a1[6][5] =  {
        { 1, 0, 3, 0, 5 },
        { 0, 5, 0, 7, 0 },
        { 0, 0, 0, 0, 0 },
        { 2, 0, 4, 0, 6 },
        { 0, 0, 0, 0, 0 },
        { 0, 0, 0, 0, 0 } };

    int a2[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, 4, 0, 2, 0 } };

    SparseMatrix<int> s((int *)a, 6, 5, 0);

    //SparseMatrix<int> s1((int *)a1, 6, 5, 0);

    //SparseMatrix<int> s2((int *)a2, 6, 5, 0);

    cout << s << endl;//稀疏矩阵还原

    //cout<<s.Access(3, 2)<<endl;

    //std::cout << s.Transprot() << endl;

    cout << s.FastTransprot() << endl;

    //cout << s1 << endl;
    //cout << endl << s1 << endl;

    //cout << endl<< s1 + s2 << endl;
}

int main()
{
    FunTest();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值