矩阵之对称矩阵的压缩存储及访问和还原

  在矩阵层次上,有几个矩阵时很特殊的。这篇文章是主要讲述对称矩阵的压缩存储、访问和还原的。
  首先,何为对称矩阵?
  首先,对称矩阵是一个二维数组,也是一个方阵。它的行列对应的列列行的数据相等,即arr[i][j]==arr[j][i];如下:
  此矩阵关于红框的1对称
  那么我们在存储时还存储一个5*5的的二维数组是不是有点浪费空间了。既然存在arr[i][j]==arr[j][i]这个规律,何不妨只存储一个下三角或者一个上三角的元素呢?即:
  这里写图片描述
  存储时将对称矩阵的一半数据存储即为压缩存储。
  具体的首先逻辑为:我们申请一个一段连续的空间,即一个一维数组。然后用这个一维数组存储这个对称的元素,并且包括对称轴。
  很明显的一个规律,对称轴为(0,0),(1,1),(2,2),(3,3),(4,4),即每一行的元素我们存储到对称轴就可以放弃存储这行后面的元素。且我们发现总元素的个数为N*(N+1)/2,这里的N为矩阵的阶数。为了方便访问。在传参时将二位数组转为一维数组使用,则每一个元素的下标为:i*N+j,i为第i行,j为第j列。
  

    int idx = 0;
    _pData = new T[N * (N + 1) / 2];
    for (size_t i = 0; i < _row; i++)
    {
        for (size_t j = 0; j <= i; j++)
            _pData[idx++] = a[i * N + j];
    }

  访问函数:
  存储在空间中时是以一维数组保存的,且只保存了一半的元素,那么访问时该如何访问任一元素呢?
  因为arr[i][j]==arr[j][i],且如果保存下三角的话,上三角的元素的航和列就是对应下三角的行和列的交换(注:上三角的列>行,下三角的元素行<列),例如:访问arr[3][1],我们只需要返回arr[1][3]的值就行。这很简单,那么问题来了,如何使二维数组的下标和一维数组的下标对应起来呢?
  一开始,我们使用压缩存储时的方法为开辟:N*(N+1)/2个大小的空间,所以N*(N+1)/2表示前N行的下三角一个储存了多少个元素,所以第N+1行的元素的下表为N*(N+1)/2+i,i为第N+1行的列数。即:
  左侧第一行为当前二维数组存储一维数组中的下标的起始值,在访问行中每一个元素时,只需加上对应的列的下标就行。
  

    T& Acess(int row, int col)
    {
        if (col > row)
            std::swap(row, col);
        return _pData[row * (row + 1) / 2 + col];
    }
    const T& Acess(int row, int col)const
    {
        if (col > row)
            std::swap(row, col);
        return _pData[row * (row + 1) / 2 + col];
    }

  还原输出对称矩阵就简单了,直接调用Acess函数就ok。
  

friend ostream& operator<<(ostream& os, SymmetricMatrix& sm)
    {
        for (size_t i = 0; i < sm._row; ++i)
        {
            for (size_t j = 0; j < sm._col; j++)
                os <<sm.Acess(i, j) << " ";
            os << endl;
        }
        return os;
    }

  关于对称矩阵的完整的实现如下:
  

#include <iostream>
using namespace std;

template<class T>
class SymmetricMatrix
{
public:
    SymmetricMatrix(int* a, size_t N)
        : _row(N)
        , _col(N)
    {
        int idx = 0;
        _pData = new T[N * (N + 1) / 2];
        for (size_t i = 0; i < _row; i++)
        {
            for (size_t j = 0; j <= i; j++)
                _pData[idx++] = a[i * N + j];
        }
    }
    T& Acess(int row, int col)
    {
        if (col > row)
            std::swap(row, col);
        return _pData[row * (row + 1) / 2 + col];
    }
    const T& Acess(int row, int col)const
    {
        if (col > row)
            std::swap(row, col);
        return _pData[row * (row + 1) / 2 + col];
    }
    ~SymmetricMatrix()
    {
        if (_pData)
            delete[] _pData;
    }

    friend ostream& operator<<(ostream& os, SymmetricMatrix& sm)
    {
        for (size_t i = 0; i < sm._row; ++i)
        {
            for (size_t j = 0; j < sm._col; j++)
                os <<sm.Acess(i, j) << " ";
            os << endl;
        }
        return os;
    }

private:
    T* _pData;
    size_t _row;
    size_t _col;
};


void FunTest()
{
    int arr[][5]={
    /* 0  1  2  3  4*/
/*0*/{ 0, 1, 2, 3, 4 }, 
/*1*/{ 1, 0, 1, 2, 3 },
/*2*/{ 2, 1, 0, 1, 2 },
/*3*/{ 3, 2, 1, 0, 1 },
/*4*/{ 4, 3, 2, 1, 0 }
};
    SymmetricMatrix<int> sm((int*)arr,sizeof(arr)/sizeof(arr[0]));
    cout << sm.Acess(4, 2) << endl;
    cout << sm.Acess(1, 4) << endl;
    cout << sm.Acess(2, 3) << endl;
    cout << sm << endl;

}

int main()
{
    FunTest();
    system("pause");
    return 0;
}
  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值