以一维数组存储的矩阵C++语言描述(包括稀疏矩阵三元组的表达),实现矩阵的基本运算(加减乘)及矩阵类的拷贝控制

行主映射:将矩阵依此从每一行的索引从左至右连续编号,将二维矩阵的索引映射为一维数组。
列主映射:将矩阵依此从每一列的索引从上至下连续编号,将二维矩阵的索引映射为一维数组。
本文的多种矩阵均采用行主映射,使用一维数组来表达多种矩阵。例如按照常规对二维矩阵进行描述的C++二维数组中,矩阵元素M(3,5)对应二维数组Array[2][4]。而采用一维映射后,该元素对应一维数组的Array[3*矩阵的列+5-1]。这样映射的好处在于节省内存空间,提高一些操作(如转置,矩阵乘法)的运行效率,虽然对于一般矩阵的提升不够明显,但是对于类似一些特殊矩阵(如对角矩阵、三对角矩阵、稀疏矩阵)所带来的效果极其明显。
对于特殊矩阵,需要重新定义数据结构进行存储。常见的一种方法是用三元组表达(row,col,value),以节省空间并提高效率。同时,因为与常规矩阵结构的不同,其加减乘除等操作需要重新编写算法。
本文中会用到以前编写的自定义异常类、数组线性表等程序。见线性表–数组/vecotr描述

一般矩阵(m*n):

实现矩阵的加/减/乘运算和矩阵类的各种拷贝控制成员以及其它一些读写操作。具体思想以及其他一些思考见注释。

#pragma once
#include <iostream>
#include <algorithm>
#include <iomanip>
#include "../../../ch06/ChainList/ChainList/illegalParameterValue.h"
using std::cin; using std::cout; using std::ends; using std::endl;

//前置声明
template <typename T> class myMatrix;
//将operator<<声明为模板 使用myMatrix的模板形参作为自己的模板实参 限定友好关系
template <typename T> std::ostream& operator<<(std::ostream& os,const myMatrix<T>& matrix);

template <typename T>
class myMatrix{ 
	friend std::ostream& operator<<<T>(std::ostream& os,const myMatrix<T>& matrix);
public:
	myMatrix()= default;
	myMatrix(size_t _row,size_t _col);
	myMatrix(const myMatrix<T>& rhs);//copy constructor
	myMatrix(myMatrix<T>&& rhs);//move construcotr
	~myMatrix() {  delete element; }

	size_t get_row()const {  return row; }
	size_t get_col()const {  return col; }
	T& operator()(size_t _row,size_t _col) const;//重载()来表示矩阵行列的索引 并完成矩阵数学逻辑上的索引到内存数组索引的映射
	void set(size_t _row,size_t _col,const T& _element);//改变矩阵(i,j)元素
	void reset();//将矩阵全部元素复位为0
	myMatrix<T>& tranpose();//将矩阵转置

	myMatrix<T>& operator=(const myMatrix<T>& rhs);//copy assignment
	myMatrix<T>& operator=(myMatrix<T>&& rhs);//move assignment
	myMatrix<T> operator+(const myMatrix<T>& rhs) const;//矩阵加法
	myMatrix<T> operator-(const myMatrix<T>& rhs) const;//矩阵减法
	myMatrix<T> operator*(const myMatrix<T>& rhs) const;//矩阵乘法
	myMatrix<T> operator+() const;//一元操作符+ 取正
	myMatrix<T> operator-() const;//一元操作符- 取负
	myMatrix<T>& operator+=(const myMatrix<T>& rhs);//复合赋值运算符
	myMatrix<T>& operator+=(size_t _unit);//复合赋值运算符 该矩阵加上一个数_unit(即_unit个单位矩阵)
	myMatrix<T>& operator-=(const myMatrix<T>& rhs);//复合赋值运算符
	myMatrix<T>& operator-=(size_t _unit);//复合赋值运算符 该矩阵减去一个数_unit(即_unit个单位矩阵)
	myMatrix<T>& operator*=(size_t _unit);//复合赋值运算符 该矩阵乘以一个数_unit(即_unit个单位矩阵)
private:
	size_t row, col;//矩阵的行列
	T* element;//数组element
	
	void check_if_same_size(size_t rhs_col,size_t rhs_row)const;//检查2个矩阵是否行可加减(行数列数相同)
	void check_if_multiplicable(size_t rhs_row) const;//检查2个矩阵是否可相乘(列数=行数)
};

template <typename T>
myMatrix<T>::myMatrix(size_t _row,size_t _col):row(_row),col(_col) { 
	//检验矩阵行列参数的有效性
	if (row < 0 || col < 0) throw illegalParameterValue("Rows and columns must be>=0!");
	//行列不能只有其中一个为0(可以同时为0 生成0*0的矩阵)
	if ((row == 0 || col == 0) && (row != 0 || col != 0))
		throw illegalParameterValue("Either both or neither rows and columns should be 0!");
	element = new T[row*col];
}

template <typename T>
myMatrix<T>::myMatrix(const myMatrix<T>& rhs):row(rhs.row),col(rhs.col) { //拷贝构造函数
	element = new T[row*col];
	//利用std::copy拷贝rhs的元素
	//注意 copy(start,end,target)的拷贝范围是[start,end) 所以end要比元素个数多1
	std::copy(rhs.element, rhs.element + rhs.row*rhs.col, element);
}

template <typename T>
myMatrix<T>::myMatrix(myMatrix<T>&& rhs) :row(rhs.row),col(rhs.col),element(rhs.element){ //移动构造函数
	//直接接管资源 然后将rhs置于可析构状态
	cout << "move constructor" << endl;
	rhs.element = nullptr;
}

template <typename T>
T& myMatrix<T>::operator()(size_t _row,size_t _col) const{ 
	//检查序列(row,col)是否合法
	if (_row<1 || _col<1 || _row>row || _col>col)
		throw illegalParameterValue("i or j of matrix(i,j) out of range");
	return element[(_row-1)*col+(_col-1)];//数学逻辑到物理地址的映射
}

template <typename T>
void myMatrix<T>::set(size_t _row,size_t _col,const T& _element) { 
	operator()(_row,_col) = _element;
}

template <typename T>
void myMatrix<T>::reset() { //将所有元素置为0
	for (size_t i = 0; i < row*col; ++i)
		element[i] = 0;
}

template <typename T>
void myMatrix<T>::check_if_same_size(size_t rhs_row,size_t rhs_col)const { 
	if (row != rhs_row || col != rhs_col)
		throw illegalParameterValue("rows and columns of two matrixs must be the same!");
}

template <typename T>
void myMatrix<T>::check_if_multiplicable(size_t rhs_row)const { 
	if (col != rhs_row)
		throw illegalParameterValue("columns of left matrix and rows of right matrix must be the same to multiply!");
}
template <typename T>
myMatrix<T>& myMatrix<T>::operator=(const myMatrix<T>& rhs) { //拷贝赋值运算符
	//先拷贝再释放内存避免自赋值
	row = rhs.row;
	col = rhs.col;
	T* newdata = new T[row*col];
	std::copy(rhs.element, rhs.element + row*col, newdata);
	delete[] element;
	element = newdata;
}

template <typename T>
myMatrix<T>& myMatrix<T>::operator=(myMatrix<T>&& rhs) { //移动赋值运算符
	//直接接管资源
	if (this!=&rhs) { //避免自赋值
		row = rhs.row;	
		col = rhs.col;
		element = rhs.element;
	}
	//将rhs置于可析构状态
	rhs.col = rhs.row = 0;
	rhs.element = nullptr;
	cout << "move assignment" << endl;
	return *this;
}

template <typename T>
myMatrix<T>& myMatrix<T>::operator+=(const myMatrix<T>& rhs){ //复合赋值运算符+=
	check_if_same_size(rhs.row,rhs.col);
	for (size_t i = 0; i < row*col;++i) { 
		element[i] += rhs.element[i];
	}
	return *this;
}

template <typename T>
myMatrix<T> myMatrix<T>::operator+(const myMatrix<T>& rhs) const { //矩阵加法
	//检查是否两个矩阵行数列数相同
	//Q:是否可以使用+=来实现+? A:会影响效率.因为涉及到对数组的2次拷贝 考虑以下一种实现情况:
	//myMatrix<T> retMatrix(*this);
	//retMatrix += rhs;
	//return retMatrix;
	//可以看到在拷贝构造*this是对element数组进行了一次for遍历 在+=时又对element数组进行了一次for遍历
	//因此 其他一般用+=运算符定义+运算符的情况在这里不适用 -= *=下同
	check_if_same_size(rhs.row,rhs.col);
	myMatrix<T> retMatrix(row, col);//返回的矩阵
	for (size_t i = 0; i < row*col; ++i) { 
		retMatrix.element[i] = element[i] + rhs.element[i];
	}
	return retMatrix;
}

template <typename T>
myMatrix<T>& myMatrix<T>::operator+=(size_t _unit) { //复合赋值运算符+= 矩阵加上一个数_unit(即_unit个单位矩阵)
	for (size_t i = 0; i < row*col;++i) { 
		element[i] += _unit;
	}
	return *this;
}

template <typename T>
myMatrix<T> myMatrix<T>::operator-(const myMatrix<T>& rhs) const{ //矩阵减法
	check_if_same_size(rhs.row,rhs.col);
	myMatrix<T> retMatrix(row,col);
	for (size_t i = 0; i < row*col;++i) { 
		retMatrix.element[i] = element[i] - rhs.element[i];
	}
	return retMatrix;
}

template <typename T>
myMatrix<T>& myMatrix<T>::operator-=(const myMatrix<T>& rhs) { //复合赋值运算符-=
	check_if_same_size(rhs.col,rhs.row);
	for (size_t i = 0; i < col*row;++i) { 
		element[i] -= rhs.element[i];
	}
	return *this;
}

template <typename T>
myMatrix<T>& myMatrix<T>::operator-=(size_t _unit) { //复合赋值运算符-= 矩阵减去一个数_unit(即_unit个单位矩阵)
	for (size_t i = 0; i < row*col; ++i) { 
		element[i] -= _unit;
	}
	return *this;
}

template <typename T>
myMatrix<T> myMatrix<T>::operator+() const { //矩阵所有元素取正
	myMatrix<T> retMatrix(*this);
	for (size_t i = 0; i < row*col; ++i) { 
		retMatrix.element[i] = +retMatrix.element[i];
	}
	return retMatrix;
}

template <typename T>
myMatrix<T> myMatrix<T>::operator-() const { //矩阵所有元素取负
	myMatrix<T> retMatrix(*this);
	for (size_t i = 0; i < row*col; ++i) { 
		retMatrix.element[i] = -retMatrix.element[i];
	}
	return retMatrix;
}

template <typename T>
myMatrix<T> myMatrix<T>::operator*(const myMatrix<T>& rhs) const { //矩阵乘法
	check_if_multiplicable(rhs.row);
	myMatrix<T> retMatrix(row,rhs.col);//结果矩阵是row行rhs.col列
	//注意 默认矩阵到一维数组是行主映射
	size_t row_ptr = 0;//用于遍历乘号左边矩阵的当前行的每一个元素
	size_t col_ptr = 0;//用于遍历乘号右边矩阵的当前列的每一个元素
	size_t arr_ptr = 0;//用于遍历被映射的一维数组
	for (size_t i = 0; i < row;++i) { //结果矩阵的每一行
		for (size_t j = 0; j < rhs.col;++j) { //结果矩阵的每一列
			T sum = 0;
			for (size_t k = 0; k < col;++k) { //结果矩阵的每一个元素matrix(i,j)=左边矩阵的第i行乘以右边矩阵的第j列
				sum += element[row_ptr] * rhs.element[col_ptr];
				++row_ptr;//移动到左边矩阵当前行的下一个元素
				col_ptr += rhs.col;//移动到右边矩阵当前列的下一个元素
			}
			retMatrix.element[arr_ptr++] = sum;
			row_ptr -= col;//后退到左边矩阵当前行的行首元素
			col_ptr = j+1;//移动到右边矩阵的下一列的首元素
		}
		row_ptr += col;//移动到左边矩阵的下一行的首元素
		col_ptr = 0;//后退到右边矩阵的第一列的首元素
	}
	return retMatrix;
}

template <typename T>
myMatrix<T>& myMatrix<T>::operator*=(size_t _unit) { //复合赋值运算符*= 矩阵乘以一个数_unit(即_unit个单位矩阵)
	for (size_t i = 0; i < row*col; ++i) { 
		element[i] *= _unit;
	}
	return *this;
}

template <typename T>
myMatrix<T>& myMatrix<T>::tranpose() { 
	for (size_t i = 1; i <= row;++i) { 
		for (size_t j = i; j <= row;++j) { 
			std::swap(operator()(i,j),operator()(j,i));
		}
	}
	return *this;
}


//重载友元operator<<操作符
template <typename T>
std::ostream& operator<<(std::ostream& os,const myMatrix<T>& matrix) { 
	for (size_t i = 1; i <= matrix.get_row(); ++i) { 
		cout << "| ";
		for (size_t j = 1; j <= matrix.get_col(); ++j) { 
			cout << std::setw(5) << std::left << matrix(i, j) << ends;
		}
		cout << " |" << endl;
	}
	return os;
}

对于矩阵类的测试:

//======================普通矩阵myMatrix=======================
#include "myMatrix.h"

int main(){ 
	myMatrix<int> matrix1(5,5);//构造一个5*5的矩阵
	myMatrix<int> matrix2(5,5);//构造一个5*5的矩阵
	//初始化
	std::default_random_engine e;//不适用种子方便调试
	std::uniform_int_distribution<int> u(0,10);
	for (size_t i = 1; i <= 5;++i) { 
		for (size_t j = 1; j <= 5;++j) { 
			matrix1(i, j) = u(e);//随机赋值
		}
	}
	for (size_t i = 1; i <= 5; ++i) { 
		for (size_t j = 1; j <= 5; ++j) { 
			matrix2(i, j) = u(e);//随机赋值
		}
	}
	cout << matrix1 << endl;

	cout << "================tranpose==============" << endl;
	cout << matrix1.tranpose() << endl;
	cout << matrix2 << endl;
	
	cout << "=================operator+/move constructor==================" << endl;
	myMatrix<int> matrix3(matrix1+matrix2);//matrix3调用移动构造函数
	cout << matrix3 << endl;

	cout << "===================operator-/move construcotr/move assignment======================" << endl;
	matrix2=matrix3 - matrix1;//编译器对matrix3-matrix1的返回值进行优化 调用移动构造函数 matrix2=又调用了移动赋值运算符
	cout << matrix2 << endl;

	cout << "======================opearator+=/-=/*=/======================" << endl;
	matrix3 += matrix2;
	cout << matrix3 << endl;
	matrix3 += 3;
	cout << matrix3 << endl;
	matrix3 -= 3;
	cout << matrix3 << endl;
	matrix3 -= matrix2;
	cout << matrix3 << endl;
	matrix3 *= 2;
	cout << matrix3 << endl;

	cout << "======================get_col/get_row/set/reset======================" << endl;
	cout << "matrix3 row=" << matrix3.get_row() << " col=" << matrix3.get_col() << endl;
	matrix3.reset();
	matrix3.set(4,3,4);
	cout << matrix3 << endl;


	cout << "========================operator*=========================" << endl;
	myMatrix<int> matrix4(3,4);
	myMatrix<int> matrix5(4,5);
	for (size_t i = 1; i <= matrix4.get_row(); ++i) { 
		for (size_t j = 1; j <= matrix4.get_col(); ++j) { 
			matrix4(i, j) = u(e);//随机赋值
		}
	}
	for (size_t i = 1; i <= matrix5.get_row(); ++i) { 
		for (size_t j = 1; j <= matrix5.get_col(); ++j) { 
			matrix5(i, j) = u(e);//随机赋值
		}
	}
	cout << matrix4 << endl << matrix5 << endl;
	myMatrix<int> result_matrix(matrix4 * matrix5);
	cout << result_matrix << endl;
}

对角矩阵(n*n):

对角矩阵只有n个元素,然而使用一般矩阵类来描述不仅浪费空间,矩阵操作也是效率低下,因此只存储对角线上的元素,完成从矩阵的数学描述到计算机数组存储的物理映射。部分实现,其他思考见注释。

#pragma once
//===================对角矩阵的部分实现===================
//将对角矩阵设计为仅仅对n阶对角阵中对角线上n个元素的存储
#include "../../../ch06/ChainList/ChainList/illegalParameterValue.h"
template <typename T>
class diagonalMatrix { 
public:
	diagonalMatrix() = default;
	diagonalMatrix(size_t _n):n(_n),element(new T[n]()){  }
	~diagonalMatrix() {  delete element; }
	T& get_element(size_t i, size_t j)const { 
		check(i,j);
		if (i == j) return element[i - 1];//对角线上的元素
		else return 0;
	}
	void set(size_t i, size_t j,const T& new_value) { 
		check(i,j);
		//非对角元素只能为0
		if (i == j) element[i - 1] = new_value;
		else throw illegalParameterValue("nondiagonal elements must be 0");
	}
private:
	size_t n;//n阶对角矩阵
	T* element;//对角元素的存储数组
	
	void check() const{ //(i,j)索引是否有效
		if (i<1 || i>n || j<1 || j>n)
			throw illegalParameterValue("i or j must be >=1 and <=n!");
	}
};

三对角矩阵(n*n):

类似对角矩阵。部分实现和其他思考见代码和注释。

#pragma once
//=================三对角矩阵的不完全实现=======================
//对于n阶三对角矩阵,可以只存储三条对角线的元素,共3n-2个
//采用逐条对角线映射 
//也就是在数组element[]中先存储n-1个下对角线的元素,再存储n个主对角线的元素,最后存储n-1个上对角线的元素
#include "../../../ch06/ChainList/ChainList/illegalParameterValue.h"
template <typename T>
class tridiagonalMatrix { 
public:
	tridiagonalMatrix() = default;
	tridiagonalMatrix(size_t _n):n(_n),element(new T[3*n-2]){  }
	~tridiagonalMatrix() {  delete element; }

	T& get_element(size_t i,size_t j) const{ 
		check(i,j);
		switch (i-j){ 
		case 1://下对角线
			return element[i-2];//Q:i-2 ? A:下对角线的i是从2开始的,即(2,j) 而下对角线首先存储在element[0,n-1)中
		case 0://主对角线
			return element[n+i-2];//Q:n+i-2 ? A:(n-1)+(i-1)
		case -1://上对角线
			return element[2*n+i-2];//Q:2*n+i-2 ? A:(n-1)+(n)+(i-1)
		default://非三对角线元素
			return 0;
		}
	}

	void set(size_t i,size_t k,const T& new_val) { 
		check(i,j);
		switch (i-j){ 
		case 1://下对角线
			element[i - 2] = new_val;
			break;
		case 0://主对角线
			element[n + i - 2] = new_val;
			break;
		case -1://上对角线
			element[2 * n + i - 2] = new_val;
			break;
		default:
			throw illegalParameterValue("nontridiagonal element must be 0!");//非三对角元素必须为0
		}
	}
private:
	size_t n;//n阶矩阵
	T* element;//存储三对角的元素

	void check()const { //检查(i,j)索引是否有效
		if (i<1 || i>n || j<1 || j>n)
			throw illegalParameterValue("i or j must be >=1 and <=n!");
	}
};

下三角矩阵(n*n):

类似地,一个set方法的实现,思考见代码和注释。

//===================类似地,下三角矩阵的映射公式========================
//构造函数中 element=new T[n*(n+1)/2]; 三角矩阵一共有(n+1)n/2个元素
template <typename>
void lowerTriangularMatrix<T>::set(size_t i,size_t j,const T& new_value) { 
	check(i,j);
	if (i >= j)	//在下三角的位置
		element[n*(n-1)/2+j-1] = new_value;//第i行上面一共有i(i-1)/2个元素
	else throw illegalParameterValue("elements not in lower triangle must be 0!");//非下三角元素必须为0
}

稀疏矩阵(m*n):

稀疏矩阵和稠密矩阵之间没有明确的界限。这里我们规律稀疏矩阵的非0元素要小于n*n/2,甚至有时要求n*n/5。对角阵和三对角矩阵的非0元素是O(n),因此算稀疏矩阵,而三角矩阵的非0元素最多可达(n+1)n/2,因此算入稠密矩阵。
实现了稀疏矩阵的加法、乘法、转置操作,其他思考见代码和注释。

#pragma once
//=====================稀疏矩阵=============================
//稀疏矩阵和稠密矩阵之间没有明确的界限 这里我们规律稀疏矩阵的非0元素要小于n*n/2 甚至有时要求n*n/5
//对角阵和三对角矩阵的非0元素是O(n) 因此算稀疏矩阵 而三角矩阵的非0元素最多可达(n+1)n/2 因此算入稠密矩阵

#include "../../../ch05/ArrayList/ArrayList/myArrayList.h"


//本实现中 对稀疏矩阵的每个元素进行行主次序映射
//======================稀疏矩阵每个元素的数据结构========================
template <typename T>
struct matrixTerm{ 
	size_t row;
	size_t col;
	T value;
	//因为arrayList<T>中的index_of/output等函数要求T实现==符号 <<符号
	bool operator==(const matrixTerm<T>& rhs)const { 
		return row == rhs.row && col == rhs.col && value == rhs.value;
	}
};

template <typename T>
std::ostream& operator<<(std::ostream& os,const matrixTerm<T>& term) { 
	os << "a(" << term.row << "," << term.col << ")=" << term.value << endl;
	return os;
}



//======================稀疏矩阵的类spareMatrix========================
//前置声明 将<< >>运算符定义为模板T 与spareMatrix<T>形成一对一友好关系
template <typename T> class spareMatrix;
template <typename T> std::ostream& operator<<(std::ostream& os,const spareMatrix<T>& matrix);
template <typename T> std::istream& operator>>(std::istream& is,spareMatrix<T>& matrix);
template <typename T>
class spareMatrix{ 
	friend std::ostream& operator<<<T>(std::ostream& os, const spareMatrix<T>& matrix);
	friend std::istream& operator>><T>(std::istream& is, spareMatrix<T>& matrix);
public:
	//实际上 我们的spareMatrix类不需要自定义任何拷贝控制成员 (默认/非默认/拷贝/移动构造函数/析构函数)
	//每一个拷贝构造成员都可以使用编译器合成的版本 它们也是安全合理的
	//原因在于我们的数据成员中自定义类型成员myArrayList<T>它已经定义了自己相应(所有)的拷贝控制成员
	//编译器在合成spareMatrix类会自动调用对应的myArrayList拷贝控制成员 以一个移动构造函数为例进行说明 其它成员同理:
	//用于说明 operator+中编译器的优化:对于返回临时对象的移动构造
	//spareMatrix(spareMatrix<T>&& rhs) :rows(rhs.rows),cols(rhs.cols),terms(std::move(rhs.terms)){ 
		移动构造函数直接接管资源 调用myArrayList的移动构造函数
		对于size_t这些内置类型不用移动构造(也没必要移动构造)
		//cout << "move construcotr" << endl;
	//}

	spareMatrix<T> tranpose() const;
	spareMatrix<T> add(const spareMatrix<T>& rhs) const;
	spareMatrix<T> multiply(const spareMatrix<T>& rhs)const;

	spareMatrix<T> operator+(const spareMatrix<T>& rhs)const { 
		//按照常理来说 我们这样编写代码会产生多个临时的中间变量对象的拷贝 然而实际上会被编译器优化掉
		//编译器会对add()返回的临时对象进行优化 并不会直接拷贝 而是移动构造 可以见移动构造函数中的调试语句
		return add(rhs);
	}
	spareMatrix<T> operator*(const spareMatrix<T>& rhs) const { 
		return multiply(rhs);
	}
private:
	size_t rows;//矩阵的行数
	size_t cols;//矩阵的列数
	myArrayList<matrixTerm<T>> terms;//非0项表
};

template <typename T>
std::ostream& operator<<(std::ostream& os,const spareMatrix<T>& matrix) { 
	//输出稀疏矩阵的基本信息
	os << "Matrix rows=" << matrix.rows << " columns=" << matrix.cols << endl;
	os << "nonzero terms=" << matrix.terms.get_size() << endl;
	//每个项单独一行输出
	for (auto iter = matrix.terms.begin(); iter != matrix.terms.end();++iter) { 
		os << *iter << endl;
	}
	return os;
}

template <typename T>
std::istream& operator>>(std::istream& is,spareMatrix<T>& matrix) { 
	size_t numbers_of_terms;
	cout << "please enter number of rows,columns and terms:" << endl;
	is >> matrix.rows >> matrix.cols >> numbers_of_terms;
	if (numbers_of_terms > matrix.rows*matrix.cols / 2)	//非0元素多余1/2 则不算入稀疏矩阵
		throw illegalParameterValue("spare matrix requires number of nonzero elements being less than half!");
	matrix.terms.resize(numbers_of_terms);//使用arraylist的resize 保证set(size)合法并且有足够的容量(设定为2*number)
	//输入项
	matrixTerm<T> perTerm;
	for (size_t i = 0; i < numbers_of_terms;++i) { 
		cout << "Enter row,column and value of term(" << (i + 1) << ") in matrix:" << endl;
		is >> perTerm.row >> perTerm.col >> perTerm.value;
		if (perTerm.row<1 || perTerm.row>matrix.rows || perTerm.col<1 || perTerm.col>matrix.cols)
			throw illegalParameterValue("terms index (i,j) out of matrix(row,col)");
		if (perTerm.value == 0) throw illegalParameterValue("spare matrix save nonzero element1");
		matrix.terms.set(i, perTerm);
	}
	return is;
}

template <typename T>
spareMatrix<T> spareMatrix<T>::tranpose() const { //返回稀疏矩阵的转置矩阵
	//设置转置矩阵的特征
	spareMatrix<T> tranMatrix;
	tranMatrix.rows = cols;
	tranMatrix.cols = rows;
	tranMatrix.terms.resize(terms.get_size());
	
	//计算转置前后元素的位置i 用于set(i,term) (注意 是按照行主次序映射的)
	size_t* colSize = new size_t[cols];//记录转置前矩阵每一列的元素个数
	size_t* newStart = new size_t[cols];//转置后每一行(原每一列)的元素的起始位置
	//初始化
	for(size_t i = 0; i < cols;++i) { 
		colSize[i] = 0;
		newStart[i] = 0;
	}
	//遍历非0元素点 根据它们的列信息递增colSize[i]
	for (auto iter = terms.begin(); iter != terms.end();++iter) { 
		++colSize[(*iter).col-1];//数学逻辑row,col需要减去1才能映射到数组
	}
	//寻找转置后矩阵每一行(原每一列)中各个元素的起始点
	newStart[0] = 0;//转置后第一行(原第一列)的元素一定是从0开始的
	for (size_t i = 1; i < cols;++i) { 
		newStart[i] = colSize[i - 1] + newStart[i - 1];
	}

	//开始转置
	matrixTerm<T> perTerm;
	for (auto iter = terms.begin(); iter != terms.end();++iter) { 
		size_t pos = newStart[(*iter).col-1]++;//当前行(原矩阵列)已经存储了一个元素了 下一个元素的起始点索引应该+1
		perTerm.col = (*iter).row;//行列转置
		perTerm.row = (*iter).col;
		perTerm.value = (*iter).value;
		//cout << "tranpose pos:" << pos;//调试语句 注意pos从数组取值时要对(*iter).colcol-1
		tranMatrix.terms.set(pos,perTerm);
	}
	return tranMatrix;
}

template <typename T>
spareMatrix<T> spareMatrix<T>::add(const spareMatrix<T>& rhs)const { 
	if (cols != rhs.cols || rows != rhs.rows)
		throw illegalParameterValue("rows and columns of two matrixs being added must be the same!");
	spareMatrix<T> resMatrix;//结果矩阵
	resMatrix.rows = rows;
	resMatrix.cols = cols;
	size_t res_size=0;//结果矩阵中元素的数量 也是将term insert到结果矩阵时的索引_index
	auto lhs_iter = terms.begin();
	auto rhs_iter = rhs.terms.begin();
	//遍历*this和rhs 相同的项相加 不同的项直接插入结果矩阵 直到其中一个完成遍历
	while (lhs_iter != terms.end() && rhs_iter!=rhs.terms.end()) { 
		//按照行主次序得到2个迭代器指代元素的相对位置索引
		size_t lhs_index = (*lhs_iter).row*cols + (*lhs_iter).col;
		size_t rhs_index = (*rhs_iter).row*cols + (*rhs_iter).col;
		if (lhs_index<rhs_index) { //*this矩阵的项在前
			resMatrix.terms.insert(*lhs_iter,res_size++);
			++lhs_iter;
		}
		else if (lhs_index==rhs_index) { //两个项在同一位置
			T add_sum = (*lhs_iter).value + (*rhs_iter).value;
			if (add_sum != 0) { //两项相加后结果不为0则插入到结果矩阵
				matrixTerm<T> term(*lhs_iter);//随便解引用lhs/rhs其中之一都行
				term.value = add_sum;
				resMatrix.terms.insert(term,res_size++);
			}
			++lhs_iter;
			++rhs_iter;
		}
		else { //*this的项在rhs的后面
			resMatrix.terms.insert(*rhs_iter,res_size++);
			++rhs_iter;
		}
	}//其中一个(可能两个)矩阵遍历完成
	//把剩下的另外一个矩阵的元素直接插入到结果矩阵
	for (; lhs_iter != terms.end(); ++lhs_iter)
		resMatrix.terms.insert(*lhs_iter,res_size++);
	for (; rhs_iter != rhs.terms.end(); ++rhs_iter)
		resMatrix.terms.insert(*rhs_iter,res_size++);
	return resMatrix;
}

template <typename T>
spareMatrix<T> spareMatrix<T>::multiply(const spareMatrix<T>& rhs)const { 
	//基本思路是 首先构建lhs/rhs的位置矩阵 记录每一行第一个元素开始的索引(多个元素记得将索引++得到后面的元素)
	//然后根据这个矩阵 对lhs每一行的每一个元素进行遍历 每一次遍历找到这个元素的列号(同时也是rhs中的行号)
	//然后对rhs的这一行(注意是行!不按照常规的a[i][k]*b[k][j]来乘 因为我们可以将结果临时保存 每次遍历进行累加)
	//依此乘以rhs行中每个元素 将每个乘积累加到一个大小为lhs.row/rhs.col的矩阵对应的位置中 lhs每行循环时记得将这个临时数组清空
	//最重要的一点就是相乘时 并不是常规的lhs的行乘以rhs的列 而是lhs的行乘以对应rhs中不同的行 进行累加最后存入结果矩阵
	if (cols != rhs.rows)
		throw illegalParameterValue("columns of left matrix must be the same to rows of right matrix to multiply!");
	spareMatrix<T> resMatrix;//结果矩阵
	
	//设置结果矩阵的特征
	resMatrix.rows = rows;
	resMatrix.cols = rhs.cols;
	size_t res_size = 0;//结果矩阵的非零元素个数 同时也是插入arraylist时的索引
	if (terms.get_size() == 0 || rhs.terms.get_size() == 0)//保证都不是非零矩阵
		return resMatrix;//全部元素为0

	//构造lhs/rhs的位置矩阵 保存其每行元素在矩阵行主映射下起始的索引
	size_t* lhs_row_start = new size_t[rows+1]();	//创建时初始化
	size_t* rhs_row_start = new size_t[rhs.rows+1](); //创建时初始化
	//遍历 先得到每一行的元素个数
	for (auto iter = terms.begin(); iter != terms.end(); ++iter) { 
		//第一行起始位置一定为0
		++lhs_row_start[(*iter).row];
	}
	for (auto iter = rhs.terms.begin();iter!=rhs.terms.end();++iter) { 
		++rhs_row_start[(*iter).row];
	}
	//将每行大小数组转为每行首元素开始位置的索引
	for (size_t s = 1; s < rows + 1;++s) { 
		lhs_row_start[s] += lhs_row_start[s - 1];
	}
	for (size_t s = 1; s < rhs.rows + 1; ++s) { 
		rhs_row_start[s] += rhs_row_start[s - 1];
	}

	//进行矩阵乘法
	T* temArr = new T[rhs.cols]();//临时数组 用来保存每次乘积的累积和
	for (size_t i = 0; i < rows;++i) { //对lhs的每一行
		//将上一行的临时矩阵清空
		for (size_t tem_col = 0; tem_col < rhs.cols;++tem_col) { 
			temArr[tem_col] = 0;
		}
		//构造临时矩阵
		for (size_t j = lhs_row_start[i]; j < lhs_row_start[i + 1];++j) { //对lhs当前i行的每一个元素j
			size_t rhs_row = terms.get_element(j).col;//找到该元素j的列数 也就是对应的rhs的行数
			//对rhs矩阵中rhs_row行的起始位置到下一行的起始位置间的元素 注意col到数组的映射需要-1
			for (size_t k = rhs_row_start[rhs_row - 1]; k < rhs_row_start[rhs_row];++k) { 
				//lhs的元素j与rhs中rhs_row行的每一个元素k相乘 累加在元素k对应的临时数组的列中
				temArr[rhs.terms.get_element(k).col-1] += terms.get_element(j).value * rhs.terms.get_element(k).value;
			}
		}

		//将临时矩阵的非零乘积结果存入结果矩阵
		for (size_t tem_col = 0; tem_col < rhs.cols; ++tem_col) { 
			if (temArr[tem_col] != 0) { 
				matrixTerm<T> term;
				term.row = i + 1;
				term.col = tem_col + 1;
				term.value = temArr[tem_col];
				resMatrix.terms.insert(term,res_size++);//存入
			}
		}
	}
	return resMatrix;
}

对于稀疏矩阵的测试:

//======================稀疏矩阵=======================
#include "spareMatrix.h"
int main() { 
	//手动输入可能比较麻烦 建议做成文件输入 手动或文件输入矩阵特征后 每项格式都为 row col value
	//测试合成的拷贝控制成员/输入运算符>>/输出运算符<<
	//spareMatrix<int> m1;
	//cin >> m1;
	//cout << "======================================" << endl << m1 << endl;

	//测试矩阵的转置
	//spareMatrix<int> m2(m1.tranpose());
	//cout << "======================================" << endl << m2 << endl;

	//测试两个矩阵相加
	//spareMatrix<int> m3;
	//cin >> m3;
	//spareMatrix<int> m4;
	//cin >> m4;
	//spareMatrix<int> m5 = m3 + m4;
	//cout << "======================================" << endl << m5 << endl;
	
	//spareMatrix<int>m6;
	//cin >> m6;
	//spareMatrix<int>m7(m6+m6);
	//cout << m7;
	
	//测试两个矩阵相乘
	spareMatrix<int> m8;
	spareMatrix<int> m9;
	cin >> m8;
	cin >> m9;
	spareMatrix<int> m10(m8*m9);
	cout << m10;
	return 0;
}
    原文作者:Zacky_ran
    原文地址: https://blog.csdn.net/sinat_40408114/article/details/91047185
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞