继续是《数据结构算法与应用:C++语言描述》,第四章数组和矩阵的笔记。本小节介绍矩阵的基本概念以及自定义一个类Matrix实现基本的矩阵操作。
矩阵
定义和操作
一个$m\times n$的矩阵(matrix)是一个m行、n列的表,如下图所示,其中m和n是矩阵的维数。

对于矩阵来说,最常见的操作就是矩阵转置、矩阵加和矩阵乘。
一个$m\times n$矩阵的转置矩阵是一个$n\times m$的矩阵$M^T$,它与$M$之间存在以下关系:
?
M^T(i,j) = M(j,i)\quad 1\le i\le n,\; 1\le j\le m
?
仅当两个矩阵的维数相同时,即具有相同的行数和列数,才可以对两个矩阵求和。两个$m\times n$矩阵$A和B$相加所得到的$m\times n$矩阵$C$如下:
?
C(i,j) =A(i,j)+B(i,j)\quad 1\le i\le n,\; 1\le j\le m
?
仅当一个$m\times n$矩阵$A$的列数与另一个$q\times p$矩阵$B$的行数相同,即n=q,才可以执行矩阵乘法$AB$。其得到的$m\times p$矩阵$C$满足以下关系:
?
C(i,j) = \sum_{k=1}^nA(i,k)\ \ B(k,j)\quad 1\le i\le m,\; 1\le j\le p
?
矩阵加的代码实现如下
template
void Add( T **a, T **b, T **c, int rows, int cols)
{
// 矩阵 a 和 b 相加得到矩阵 c .
for (int i = 0; i < rows; i++)
for (int j = 0; j < cols; j++)
c[i][j] = a[i][j] + b[i][j];
}
矩阵转置代码实现如下:
template
void Transpose(T **a, int rows)
{
// 对矩阵 a[0:rows-1][0:rows-1] 进行转置
for (int i = 0; i < rows; i++)
for (int j = i+1; j < rows; j++)
Swap(a[i][j], a[j][i]);
}
矩阵乘法的代码实现如下:
template
void Mult(T **a, T **b, T **c, int m, int n, int p)
{
// m x n 矩阵 a 与 n x p 矩阵 b相乘得到c
for (int i = 0; i < m; i++)
for (int j = 0; j < p; j++) {
T sum = 0;
for (int k = 0; k < n; k++)
sum += a[i][k] * b[k][j];
c[i][j] = sum;
}
}
类Matrix
这里自定义一个类Matrix来实现矩阵的功能,在该类中,使用()来指定每个元素,并且各行和各列的索引值都是从1开始的。
其类定义如下:
#ifndef MATRIX_H_
#define MATRIX_H_
#include
using std::ostream;
template
class Matrix{
private:
int rows, cols;
// 元素数组
T *element;
public:
Matrix(int r = 0, int c = 0);
// 复制构造函数
Matrix(const Matrix& m);
~Matrix(){ delete[] element; }
int Rows() const{ return rows; }
int Columns() const{ return cols; }
T& operator()(int i, int j)const;
Matrix& operator=(const Matrix& m);
Matrix operator+() const;
Matrix operator+(const Matrix& m)const;
Matrix operator-() const;
Matrix operator-(const Matrix& m)const;
Matrix operator*(const Matrix& m)const;
Matrix& operator+=(const T& m);
void Output(ostream& out) const;
};
#endif
构造函数,复制构造函数以及赋值运算符的代码如下:
template
Matrix::Matrix(int r, int c){
if (r < 0 || c < 0)
throw BadInitializers();
if ((!r || !c) && (r || c))
throw BadInitializers();
rows = r;
cols = c;
element = new T[r*c];
}
template
Matrix::Matrix(const Matrix& m){
// 复制构造函数
rows = m.rows;
cols = m.cols;
element = new T[rows*cols];
int size = rows * cols;
for (int i = 0; i < size; i++)
element[i] = m.element[i];
}
template
Matrix& Matrix::operator=(const Matrix& m){
// 重载赋值运算符=
if (*this != &m){
rows = m.rows;
cols = m.cols;
delete[] element;
element = new T[rows*cols];
int size = rows * cols;
for (int i = 0; i < size; i++)
element[i] = m.element[i];
}
return *this;
}
为了重载矩阵下标操作法(),使用了C++的函数操作符(),与数组的下标操作法[]不同的是,该操作符可以带任意数量的参数。对于一个矩阵来说,需要两个整数参数。如下所示
template
T& Matrix::operator()(int i, int j)const{
// 返回一个指向元素(i,j)的引用
if (i<1 || i>rows || j<1 || j>cols)
throw OutOfBounds();
return element[(i - 1)*cols + j - 1];
}
二元减法的代码如下,矩阵加法操作符,一元减法操作符,增值操作符和输出操作符的代码都比较类似。
template
Matrix Matrix::operator-(const Matrix& m)const{
if (rows != m.rows || cols != m.cols)
throw SizeMismatch();
Matrix w(rows, cols);
for (int i = 0; i < rows * cols; i++){
w.element[i] = element[i] - m.element[i];
}
return w;
}
矩阵乘法实现如下所示
template
Matrix Matrix::operator*(const Matrix& m)const{
// 矩阵乘法 返回w = (*this) * m
if (cols != m.rows)
throw SizeMismatch();
Matrix w(rows, m.cols);
int ct = 0, cm = 0, cw = 0;
for (int i = 1; i
更完整的例子可以查看我的Github
复杂性
当T是一个内部数据类型时,矩阵构造函数复杂性是$O(1)$,当T是一个用户自定义类时,构造函数的复杂性是$O(rowscols)$,下标操作符的复杂性是$\theta(1)$,乘法操作符的复杂性是$O(rowscols*m.cols)$。