对称矩阵及稀疏矩阵浅谈

时光怂恿深爱的人放手 提交于 2019-11-30 13:11:12

1.对称矩阵

特点:
关于对角线对称,Aij == Aji。
下面实现:
①对称矩阵的压缩存储
②对称矩阵的访问
③对称矩阵的还原

实现代码如下:

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
//对称矩阵的压缩存储
template<class T>
class SymmetricMatrix
{
public:
    SymmetricMatrix(T* array, size_t N)
        :_N(N)
        ,_a(NULL)
    {
        _a = new T[(N * (N + 1))>> 1];
        for (size_t i = 0; i < N; ++i)
        {
            for (size_t j = 0; j < N; ++j)
            {
                if (i >= j)//表明是下三角数据时
                {
                    _a[(i * (i + 1))>>1 + j] = array[i * N + j];
                }
                else
                {
                    break;
                }
            }
        }
    }

    T& Access(size_t x, size_t y)
    {
        if (x < y)//如果访问上三角元素
        {
            swap(x, y);
        }
        return _a[(x * (x + 1)) >> 1 + y];
    }

    void DisPlay()
    {
        for (size_t i = 0; i < _N; ++i)
        {
            for (size_t j = 0; j < _N; ++j)
            {
                if (i >= j)//访问下三角元素
                {
                    cout<<Access(i, j)<<" ";
                }
                else
                {
                    cout << Access(j, i) << " ";
                }
            }
            cout << endl;
        }
        cout << endl;
    }

    ~SymmetricMatrix()
    {
        if (_a)
        {
            delete[] _a;
            _a = NULL;
        }
    }

private:
    T* _a;
    size_t _N;
};

测试代码如下:

void Test()
{
    int a[5][5] =
    {
        { 0, 1, 2, 3, 4 },
        { 1, 0, 1, 2, 3 },
        { 2, 1, 0, 1, 2 },
        { 3, 2, 1, 0, 1 },
        { 4, 3, 2, 1, 0 },
    };
    SymmetricMatrix<int> sm((int*)a, 5);
    cout << sm.Access(0, 3) << endl;
    cout << sm.Access(3, 0) << endl;
    sm.DisPlay();
}

int main()
{
    Test();
    return 0;
}

2.稀疏矩阵:

这里写图片描述

(1)稀疏矩阵的压缩存储:

所谓稀疏矩阵的压缩存储,其实就是将稀疏矩阵中的有效元素保存起来,但由于稀疏矩阵中有效元素的分布没有规律可寻,所以需要使用一个三元组来保存其位置(row、col)以及有效值。—>三元组最好使用结构体来存储。

因为不知道矩阵中究竟有多少个有效元素(即就是不知需要多少个结构体),那么就可以使用vector(动态增长型)来保存有效元素。

//三元组--->存储有效元素的位置及值
    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;
    };
//稀疏矩阵的压缩存储
    SparseMatrix(T* array, int row, int 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)
                {
                    _sm.push_back(Trituple<T>(i, j, array[i * col + j]));
                }
            }
        }
    }

(2)稀疏矩阵的转置:

这里写图片描述

稀疏矩阵的转置过程如下:

这里写图片描述

实现代码如下:

   // 稀疏矩阵的转置
   //时间复杂度:(稀疏矩阵的列*稀疏矩阵中有效元素的个数)
    SparseMatrix<T> Transprot()
    {
        SparseMatrix<T> sm;
        sm._row = _col;
        sm._col = _row;

        for (size_t index = 0; index < _col; ++index)
        {
            vector<Trituple<T> >::iterator it = _sm.begin();
            while (it != _sm.end())
            {
                if ( it->_col == index)
                {
                    sm._sm.push_back(Trituple<T>(it->_col, it->_row, it->_data));
                }
                it++;
            }
        }
        return sm;
    }

3.稀疏矩阵的快速转置:

稀疏矩阵的转置的时间复杂度:(稀疏矩阵的列N*稀疏矩阵中有效元素的个数)—-》时间复杂度较大,所以引出了快速转置

稀疏矩阵的快速转置的时间复杂度(2*有效元素的个数 + 稀疏矩阵的列)

快速转置的思想如下:

这里写图片描述

代码实现:


//快速转置
SparseMatrix<T,N,M> FastTransport(const size_t m,const size_t n)
    {
    SparseMatrix < T, N, M> sm;//转置后的压缩矩阵
        sm._row = _col;
        sm._col = _row;
        sm._v.resize(_v.size());//为了保证后面可以直接访问vector中的数据(调用operator[])
        //保存转置前的每列有效元素的个数,即就是转置后每行有效元素的个数
        int rows[N] = {0};
        size_t index = 0;
        while (index < _v.size())
        {
            rows[_v[index]._col]++;
            index++;
        }
        //保存转置后的每行第一个有效元素在转置后的三元组数组中的下标
        int starts[N] = { 0 };
        for (size_t i = 1; i < _col; ++i)
        {
            starts[i] = starts[i - 1] + rows[i - 1];
        }

        for (size_t i = 0; i < _v.size(); ++i)
        {
            int row = _v[i]._col;//转置前列的下标即是转置后行的下标
            Triple<T> t;
            t._value = _v[i]._value;
            t._col = _v[i]._row;
            t._row = _v[i]._col;
            sm._v[starts[row]] = t;
            starts[row]++;//每次需要更新starts数组中的数据
        }
        return sm;
    }

全部代码如下:

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
#include<vector>

//三元组
template<class T>
struct Triple
{
    Triple()
    {}
    Triple(T& data, size_t row, size_t col)
        :_value(data)
        , _row(row)
        , _col(col)
    {}
    T _value;
    size_t _row;
    size_t _col;
};

template<class T,size_t M,size_t N>
class SparseMatrix
{
public:
    SparseMatrix()
    {}

    //稀疏矩阵的压缩存储
    SparseMatrix(T* array, const T& invalid)
        :_invalid(invalid)
        , _row(M)
        , _col(N)
    {
        for (size_t i = 0; i < _row; ++i)
        {
            for (size_t j = 0; j < _col; ++j)
            {
                if (array[i * N + j] != invalid)
                {
                    Triple<T> t;
                    t._value = array[i * N + j];
                    t._row = i;
                    t._col = j;
                    _v.push_back(t);
                }
            }
        }
    }

    //稀疏矩阵的还原
    void Display()
    {
        int index = 0;
        for (size_t i = 0; i < _row; ++i)
        {
            for (size_t j = 0; j < _col; ++j)
            {
                if (index < _v.size() && _v[index]._row == i && _v[index]._col == j)
                {
                    cout << _v[index]._value << " ";
                    index++;
                }
                else
                {
                    cout << _invalid << " ";
                }
            }
            cout << endl;
        }
        cout << endl;
    }

    //稀疏矩阵的转置
    SparseMatrix<T,N,M> Transport()
    {
        SparseMatrix<T,N,M> _sm;//转置后的
        _sm._invalid = _invalid;
        _sm._row = _col;
        _sm._col = _row;
        //按列查找(转置前的列优先即就是转置后的行优先)
        for (size_t i = 0; i < _col; ++i)
        {
            size_t index = 0;
            while (index < _v.size())
            {
                if (_v[index]._col == i)
                {
                    Triple<T> t;
                    t._value = _v[index]._value;
                    t._row = _v[index]._col;
                    t._col = _v[index]._row;
                    _sm._v.push_back(t);
                }
                index++;
            }
        }
        return _sm;
    }

    template<class T, size_t N, size_t M>
    friend class SparseMatrix;

//快速转置
SparseMatrix<T,N,M> FastTransport(const size_t m,const size_t n)
    {
    SparseMatrix < T, N, M> _sm;//转置后的压缩矩阵
        _sm._row = _col;
        _sm._col = _row;
        _sm._v.resize(_v.size());//为了保证后面可以直接访问vector中的数据(使用operator[])
        //保存转置前的每列有效元素的个数,即就是转置后每行有效元素的个数
        int rows[N] = {0};
        size_t index = 0;
        while (index < _v.size())
        {
            rows[_v[index]._col]++;
            index++;
        }
        //保存转置后的每行第一个有效元素在转置后的三元组数组中的下标
        int starts[N] = { 0 };
        for (size_t i = 1; i < _col; ++i)
        {
            starts[i] = starts[i - 1] + rows[i - 1];
        }

        for (size_t i = 0; i < _v.size(); ++i)
        {
            int row = _v[i]._col;//转置前列的下标即是转置后行的下标
            Triple<T> t;
            t._value = _v[i]._value;
            t._col = _v[i]._row;
            t._row = _v[i]._col;
            _sm._v[starts[row]] = t;
            starts[row]++;//每次需要更新starts数组中的数据
        }
        return _sm;
    }

private:
    vector<Triple<T> >  _v;
    T _invalid;
    size_t _row;
    size_t _col;
};

测试代码如下:


void Test()
{
    int array[6][5] = { 
    { 1, 0, 3, 0, 5 },
    { 0, 0, 0, 0, 0 },
    { 0, 0, 0, 0, 0 },
    { 1, 0, 3, 0, 5 },
    { 0, 0, 0, 0, 0 },
    { 0, 0, 0, 0, 0 },
    };
    SparseMatrix<int,6,5> sm((int*)array, 0);
    sm.Display();
    SparseMatrix<int,5,6> sm2 = sm.FastTransport(5,6);
    sm2.Display();
}

int main()
{
    Test();
    return 0;
}

由于能力有限,又是刚开始学习数据结构,难免会有思考的不正确或是不完善的地方,欢迎大家批评指正。。。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!