数组
映射
- 行主映射: 先存储行号较小的, 行号相同, 先存储列号较小的
- 列主映射: 先存储列号较小的
不规则二维数组
即一个二维数组其每一行的元素个数不一样
#include<iostream>
using namespace std;
int main() {
// 一个二级指针, 表示有 5 行的二维数组
int** irregularArray = new int* [5];
// 给每一行分配一个数组, 长度不一
int len[5] = { 4, 3, 2, 5, 6 };
for (int i = 0; i < 5; i++) {
irregularArray[i] = new int[len[i]];
}
// 像普通二维数组一样使用
irregularArray[1][0] = 1;
cout << irregularArray[1][0];
return 0;
}
自定义一维数组
解决普通数组下标越界问题
构造函数
template<class T>
Array1D<T>::Array1D(int size) {
if (size < 0) throw "数组初始化错误";
this->size = size;
element = new T[size];
}
拷贝构造
这里要使用
(const xx&)否则后面使用=时可能会出现Class has no suitable copy constructor的报错
template<class T>
Array1D<T>::Array1D(const Array1D<T>& v) {
size = v.Size();
element = new T[size];
for (int i = 0; i < size; i++) {
element[i] = v[i];
}
}
实现 [] 访问
template<class T>
T& Array1D<T>::operator[](int i) const {
if (i < 0 || i >= size) throw "下标越界";
return element[i];
}
重载 =
为支持连等而返回引用, 同理这里也要使用 const
template<class T>
Array1D<T>& Array1D<T>::operator=(const Array1D<T>& v) {
if (&v == this) {
return *this;
}
if (element != NULL) {
delete[] element;
size = 0;
}
size = v.Size();
element = new int[size];
for (int i = 0; i < size; i++) {
element[i] = v[i]; // 这里重载了 []
}
return *this;
}
重载 ==
template<class T>
bool Array1D<T>::operator==(const Array1D<T>& v) {
if (size != v.Size()) { // 判断长度是否相等
return false;
}
// 判断每个元素是否相等
for (int i = 0; i < size; i++) {
if (element[i] != v[i]) {
return false;
}
}
return true;
}
🎉 完整代码
#include<iostream>
using namespace std;
template<class T>
class Array1D {
public:
// 这个具体实现要写在里面, 不能写在外面
friend ostream& operator << (ostream&os, Array1D<T>& v) {
os << '[';
for (int i = 0; i < v.Size() - 1; i++) {
os << v[i] << ',';
}
os << v[v.Size() - 1] << ']';
return os;
};
Array1D(int size = 0);
Array1D(const Array1D<T>& v);
~Array1D() { delete[] element; }
T& operator[](int i) const;
int Size() const { return size; }
void setData(int index, T val) {
element[index] = val;
};
Array1D<T>& operator = (const Array1D<T>& v);
bool operator == (const Array1D<T>& v);
void ReSize(int sz);
Array1D<T> operator - (const Array1D<T>& v) const;
private:
int size;
T* element; // 一维数组
};
// 构造函数
template<class T>
Array1D<T>::Array1D(int size) {
if (size < 0) throw "数组初始化错误";
this->size = size;
element = new T[size];
}
// 拷贝构造函数
template<class T>
Array1D<T>::Array1D(const Array1D<T>& v) {
size = v.Size();
element = new T[size];
for (int i = 0; i < size; i++) {
element[i] = v[i];
}
}
template<class T>
T& Array1D<T>::operator [](int i) const {
if (i < 0 || i >= size) throw "下标越界";
return element[i];
}
// 为支持连等而返回引用
template<class T>
Array1D<T>& Array1D<T>::operator=(const Array1D<T>& v) {
if (&v == this) {
return *this;
}
if (element != NULL) {
delete[] element;
size = 0;
}
size = v.Size();
element = new int[size];
for (int i = 0; i < size; i++) {
element[i] = v[i]; // 这里重载了 []
}
return *this;
}
template<class T>
bool Array1D<T>::operator ==(const Array1D<T>& v) {
if (size != v.Size()) { // 判断长度是否相等
return false;
}
// 判断每个元素是否相等
for (int i = 0; i < size; i++) {
if (element[i] != v[i]) {
return false;
}
}
return true;
}
template<class T>
void Array1D<T>::ReSize(int sz) {
delete[] element;
element = new T[sz];
size = sz;
}
template<class T>
Array1D<T> Array1D<T>::operator-(const Array1D<T>& v) const {
if (size != v.Size()) throw "无法相减";
Array1D<T> a(size);
for (int i = 0; i < size; i++) {
a[i] = element[i] - v[i];
}
return a;
}
int main() {
Array1D<int> a1(3);
// 也可以 a1[0] = 1;
a1.setData(0, 1);
a1.setData(1, 2);
a1.setData(2, 3);
// 测试 []
for (int i = 0; i < a1.Size(); i++) {
cout << a1[i] << " "; // 1 2 3
}
// 测试复制构造函数
Array1D<int> a2(a1);
cout << a2[1] << endl; // 2
// 测试 =
// 这里不能使用 Array1D<int> a3 = a2;
// 因为这会调用复制构造函数
Array1D<int> a3;
a3 = a2;
cout << a3[1] << endl; // 2
// 测试 ==
cout << (a3 == a2) << endl; // 1
return 0;
}
自定义二维数组
在 Array1D 的基础上实现 Array2D
🌟完整代码
// 忽略 Array1D 代码...
template<class T>
class Array2D {
public:
int Rows() const { return rows; }
int Columns() const { return cols; }
Array2D<T>(int, int);
Array2D(const Array2D<T>& m);
~Array2D() {
delete[] row;
}
Array1D<T>& operator[](int i) const;
Array2D<T> operator - (const Array2D<T>& m) const;
friend ostream& operator << (ostream& os, const Array2D<T>& v) {
for (int i = 0; i < v.Rows(); i++) {
os << v[i] << endl;
}
return os;
};
private:
int rows, cols;
Array1D<T>* row;
};
// 构造函数
template<class T>
Array2D<T>::Array2D(int r, int c) {
if (r <= 0 && c <= 0) throw "数组初始化错误";
rows = r;
cols = c;
row = new Array1D<T>[r];
for (int i = 0; i < r; i++) {
row[i].ReSize(c);
}
}
// 拷贝构造函数
template<class T>
Array2D<T>::Array2D(const Array2D<T>& m) {
rows = m.rows;
cols = m.cols;
row = new Array1D<T>[rows];
for (int i = 0; i < rows; i++) {
// 一维数组已经重载了 =
// 这里可以直接使用 =
row[i] = m.row[i];
}
}
// 重载 []
template<class T>
Array1D<T>& Array2D<T>::operator[](int i) const {
if (i < 0 || i >= rows) throw "下标越界";
return row[i];
}
// 重载 -
template<class T>
Array2D<T> Array2D<T>::operator - (const Array2D<T>& m) const {
if (rows != m.rows || cols != m.cols) throw "数组大小不匹配";
Array2D<T> w(rows, cols);
for (int i = 0; i < rows; i++) {
w.row[i] = row[i] - m.row[i];
}
return w;
}
int main() {
Array2D<int> a(3, 3);
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
a[i][j] = j;
}
}
cout << a << endl;
// [0, 1, 2]
// [0, 1, 2]
// [0, 1, 2]
Array2D<int> a2 = a;
Array2D<int> a3 = a2 - a;
cout << a3;
// [0, 0, 0]
// [0, 0, 0]
// [0, 0, 0]
return 0;
}
矩阵
与二维数组不同在于, 矩阵的行列索引应该从 1 开始
构造函数
生成一个 r×c 的矩阵或者 0×0 的
template<class T>
Matrix<T>::Matrix(int r, int c) {
if (r < 0 || c < 0) throw "行列必须非负";
if ((!r || !c) && (r || c)) throw "要么都是0, 要么都不等于0";
rows = r; cols = c;
element = new T[r * c];
}
复制构造函数
template<class T>
Matrix<T>::Matrix(const Matrix<T>& m) {
rows = m.Rows();
cols = m.Columns();
element = new T[rows * cols];
// 复制元素到 element
copy(m.element, m.element + rows * cols, element);
}
重载 =
template<class T>
Matrix<T>& Matrix<T>::operator=(const Matrix<T>& m) {
if (this != &m) { // 不能自己复制自己
delete[] element;
rows = m.Rows();
cols = m.Columns();
element = new T[rows * cols];
copy(m.element, m.element + rows * cols, element);
}
return *this;
}
() 操作符
template<class T>
T& Matrix<T>::operator()(int i, int j) const {
if (i < 1 || i > rows || j < 1 || j > cols) {
throw "错误索引!";
}
// 使用行主次序将一维数组映射到一维数组中
return element[(i - 1) * cols + j - 1];
}
矩阵乘法
时间复杂度
template<class T>
Matrix<T> Matrix<T>::operator*(const Matrix<T>& m) const {
if (cols != m.Rows()) {
throw "矩阵右乘要求左矩阵列数与右矩阵行数相等!";
}
// m×n 矩阵与 a×b 矩阵相乘, 结果为 m×b
Matrix<T> w(rows, m.Columns());
int ct = 0, cm = 0, cw = 0;
for (int i = 1; i <= rows; i++) {
for (int j = 1; j <= m.Columns(); j++) {
T sum = element[ct] * m.element[cm];
for (int k = 2; k <= cols; k++) {
ct++;
cm += m.Columns();
sum += element[ct] * m.element[cm];
}
w.element[cw++] = sum;
ct -= cols - 1;
cm = j;
}
ct += cols;
cm = 0;
}
return w;
}
🐼 完整代码
#include<iostream>
using namespace std;
template<class T>
class Matrix {
public:
friend ostream& operator << (ostream&os, const Matrix<T>&m) {
for (int i = 1; i <= m.Rows(); i++) {
for (int j = 1; j <= m.Columns(); j++) {
os << m(i,j) << " ";
}
os << "\n";
}
return os;
};
Matrix(int r = 0, int c = 0);
Matrix(const Matrix<T>& m); // copy constructor
~Matrix() { delete[] element; }
int Rows() const { return rows; }
int Columns() const { return cols; }
T& operator()(int i, int j) const;
Matrix<T>& operator=(const Matrix<T>& m);
Matrix<T> operator+(const Matrix<T>& m) const;
Matrix<T> operator-(const Matrix<T>& m) const;
Matrix<T> operator*(const Matrix<T>& m) const;
private:
int rows, cols;
T* element;
};
// 构造函数
template<class T>
Matrix<T>::Matrix(int r, int c) {
if (r < 0 || c < 0) throw "行列必须非负";
if ((!r || !c) && (r || c)) {
throw "要么都是0, 要么都不等于0";
}
rows = r; cols = c;
element = new T[r * c];
}
// 复制构造函数
template<class T>
Matrix<T>::Matrix(const Matrix<T>& m) {
rows = m.Rows();
cols = m.Columns();
element = new T[rows * cols];
// 复制元素到 element
copy(m.element, m.element + rows * cols, element);
}
// () 操作符
template<class T>
T& Matrix<T>::operator()(int i, int j) const {
if (i < 1 || i > rows || j < 1 || j > cols) {
throw "错误索引!";
}
// 使用行主次序将一维数组映射到一维数组中
return element[(i - 1) * cols + j - 1];
// | 1 2 3 4 ... cols |
// | cols+1 cols+2 ... |
}
// 重载 =
template<class T>
Matrix<T>& Matrix<T>::operator=(const Matrix<T>& m) {
if (this != &m) { // 不能自己复制自己
delete[] element;
rows = m.Rows();
cols = m.Columns();
element = new T[rows * cols];
copy(m.element, m.element + rows * cols, element);
}
return *this;
}
// 矩阵加法
template<class T>
Matrix<T> Matrix<T>::operator+(const Matrix<T>& m) const {
if (rows != m.Rows() || cols != m.Columns()) {
throw "非同型矩阵无法相加!";
}
Matrix<T> w(rows, cols);
// 矩阵加法, 就是对应位置元素相加
for (int i = 0; i < rows * cols; i++) {
w.element[i] = element[i] + m.element[i];
}
return w;
}
template<class T>
Matrix<T> Matrix<T>::operator-(const Matrix<T>& m) const {
if (rows != m.Rows() || cols != m.Columns()) {
throw "非同型矩阵无法相减!";
}
Matrix<T> w(rows, cols);
// 矩阵减法, 就是对应位置元素相减
for (int i = 0; i < rows * cols; i++) {
w.element[i] = element[i] - m.element[i];
}
return w;
}
// 矩阵乘法
template<class T>
Matrix<T> Matrix<T>::operator*(const Matrix<T>& m) const {
if (cols != m.Rows()) {
throw "矩阵右乘要求左矩阵列数与右矩阵行数相等!";
}
// m×n 矩阵与 a×b 矩阵相乘, 结果为 m×b
Matrix<T> w(rows, m.Columns());
int ct = 0, cm = 0, cw = 0;
for (int i = 1; i <= rows; i++) {
for (int j = 1; j <= m.Columns(); j++) {
T sum = element[ct] * m.element[cm];
for (int k = 2; k <= cols; k++) {
ct++;
cm += m.Columns();
sum += element[ct] * m.element[cm];
}
w.element[cw++] = sum;
ct -= cols - 1;
cm = j;
}
ct += cols;
cm = 0;
}
return w;
}
int main() {
Matrix<int> m(10, 10);
// 这里下标从 1 开始
for (int i = 1; i <= 10; i++) {
for (int j = 1; j <= 10; j++) {
m(i, j) = j;
}
}
cout << m << endl << endl;
Matrix<int> m2 = m * m;
cout << m2 << endl << endl;
Matrix<int> m3 = m - m;
cout << m3 << endl;
Matrix<int> m4 = m + m;
cout << m4 << endl;
return 0;
}
特殊矩阵
对角矩阵
只有对角线上可以是非零, 因此至多有 rows 个非零元素, 只需要长度为 rows 的一维数组即可表示
#include<iostream>
using namespace std;
template<class T>
class DiagonalMatrix {
public:
DiagonalMatrix(int size = 10) {
if (size < 0) throw "初始化对角矩阵错误!";
n = size;
element = new T[n];
}
~DiagonalMatrix() { delete[] element; }
T get(int, int) const;
void set(int, int, const T&);
private:
int n;
T* element; // 一维数组
};
// 获取 (i, j) 值
template<class T>
T DiagonalMatrix<T>::get(int i, int j) const {
if (i < 1 || i > n || j < 1 || j > n) {
throw "错误索引!";
}
if (i == j) {
return element[i - 1];
}
else {
return 0;
}
}
// 设置 (i, j) 值
template<class T>
void DiagonalMatrix<T>::set(int i, int j, const T& value) {
if (i < 1 || i > n || j < 1 || j > n) {
throw "错误索引!";
}
if (i == j) {
element[i - 1] = value;
}
else {
if (value != 0) throw "非对角元素必须为0!";
}
}
int main() {
DiagonalMatrix<int> dm;
for (int i = 1; i <= 10; i++) {
dm.set(i, i, i);
}
for (int i = 1; i <= 10; i++) {
for (int j = 1; j <= 10; j++) {
cout << dm.get(i, j) << " ";
}
cout << "\n";
}
return 0;
}
三对角矩阵
只有 |i-j|<=1 是非零的, 因此只需要长度为 3×rows-2 的一维数组
映射关系
(1) 采用 对角线映射 时:
- 下对角
(i, j)对应a[i-2] - 对角对应
a[n-1+i-1]=a[n+i-2] - 上对角对应于
a[2n-1+i-1]=a[2n+i-2]
(2) 采用 逐行映射 时:
t[0:9]=[2, 1, 3, 1, 3, 5, 2, 7, 9, 0]
(3) 采用 逐列映射 时:
t=[2, 3, 1, 1, 5, 3, 2, 9, 7, 0]
📢 完整代码
#include<iostream>
using namespace std;
template<class T>
class TridiagonalMatrix {
public:
TridiagonalMatrix(int size = 10) {
n = size;
element = new T[3 * n - 2];
}
~TridiagonalMatrix() { delete[] element; }
T get(int, int);
void set(int, int, const T&);
private:
int n;
T* element;
};
template<class T>
T TridiagonalMatrix<T>::get(int i, int j) {
if (i < 1 || j < 1 || i > n || j > n) throw "下标越界";
switch (i - j) {
case 1:
return element[i - 2];
case 0:
return element[n + i - 2];
case -1:
return element[2 * n + i - 2];
default: return 0;
}
}
template<class T>
void TridiagonalMatrix<T>::set(int i, int j, const T&value) {
if (i < 1 || i > n || j < 1 || j > n) {
throw "错误索引!";
}
switch (i - j) {
case 1:
element[i - 2] = value;
break;
case 0:
element[n + i - 2] = value;
break;
case -1:
element[2 * n + i - 2] = value;
break;
default: if (value != 0) throw "非三对角元素必须为0!";
}
}
int main() {
TridiagonalMatrix<int> tm;
for (int i = 1; i <= 10; i++) {
for (int j = 1; j <= 10; j++) {
if (i - j < -1 || i - j > 1) {
tm.set(i, j, 0);
}
else {
tm.set(i, j, j);
}
}
}
for (int i = 1; i <= 10; i++) {
for (int j = 1; j <= 10; j++) {
cout << tm.get(i, j) << " ";
}
cout << "\n";
}
return 0;
}
三角矩阵
需要长度为 的一维数组
采用逐行映射时:
对应 a[i×(i-1)/2+j-1] (下三角)
☃️ 完整代码
#include<iostream>
using namespace std;
template<class T>
class lowerTriangularMatrix {
public:
lowerTriangularMatrix(int size = 10) {
n = size;
element = new T[n * (n + 1) / 2];
}
~lowerTriangularMatrix() { delete[] element; }
T get(int, int);
void set(int, int, const T&);
private:
int n;
T* element;
};
template<class T>
T lowerTriangularMatrix<T>::get(int i, int j) {
if (i< 1 || j < 1 || i > n || j > n) throw "错误索引!";
if (i >= j) {
return element[i * (i - 1) / 2 + j - 1];
}
else {
return 0;
}
}
// 采用逐行映射
template<class T>
void lowerTriangularMatrix<T>::set(int i, int j, const T& value) {
if (i< 1 || j < 1 || i > n || j > n) throw "错误索引!";
if (i >= j) {
element[i * (i - 1) / 2 + j - 1] = value;
}
else {
if (value != 0) {
throw "非下三角元素必须为 0!";
}
}
}
int main() {
lowerTriangularMatrix<int> ltm;
for (int i = 1; i <= 10; i++) {
for (int j = 1; j <= 10; j++) {
if (i < j) {
ltm.set(i, j, 0);
}
else {
ltm.set(i, j, j);
}
}
}
for (int i = 1; i <= 10; i++) {
for (int j = 1; j <= 10; j++) {
cout << ltm.get(i, j) << " ";
}
cout << "\n";
}
return 0;
}
对称矩阵
与下(上)三角一致, 只需要 个存储空间
🌈 完整代码
template<class T>
class symmetricalMatrix {
public:
symmetricalMatrix(int size = 10) {
n = size;
element = new T[n * (n + 1) / 2];
}
~symmetricalMatrix() { delete[] element; }
T get(int, int);
void set(int, int, const T&);
private:
int n;
T* element;
};
template<class T>
T symmetricalMatrix<T>::get(int i, int j) {
if (i< 1 || j < 1 || i > n || j > n) throw "错误索引!";
if (i >= j) {
return element[i * (i - 1) / 2 + j - 1];
}
else {
return element[j * (j - 1) / 2 + i - 1];
}
}
// 采用逐行映射
template<class T>
void symmetricalMatrix<T>::set(int i, int j, const T& value) {
if (i< 1 || j < 1 || i > n || j > n) throw "错误索引!";
if (i >= j) {
element[i * (i - 1) / 2 + j - 1] = value;
}
else {
element[j * (j - 1) / 2 + i - 1] = value;
}
}
稀疏矩阵
矩阵有许多 0 元素, 且位置不确定
比如记录 1000 个顾客购买 10000 个商品的情况, 需要一个 1000 × 10000 的 purchases 矩阵来记录, 假设平均每个顾客只购买 20 个商品, 即只有 20000 个位置非空, 非空率为 0.2%, 浪费存储空间! 为了计算每个顾客的花费 spent 矩阵, 需要运算 运算效率低
压缩存储
即只记录非零元素的值, 利用一个 三元组 保存 值 行 列
template <class T>
class Term {
public:
int row, col;
T value;
};
不能提高 get set 的效率, 二维数组表示仅需要 O(1), 如果折半查找, get 需要 O(log(非0元素个数)), set 要移动元素, 因此需要 O(非0元素个数)
稀疏矩阵类
template<class T>
class SparseMatrix {
public:
SparseMatrix(int maxTerms = 10);
~SparseMatrix() { delete[] a; };
void transpose(SparseMatrix<T>& b) const; // 转置
void add(const SparseMatrix<T>& b, SparseMatrix<T>& c) const; // 矩阵加法
void reSize(int); // 重置矩阵大小
private:
void append(const Term<T>& t);
int rows, cols; // 矩阵行和列
int terms; // 非零元素个数
Term<T>* a; // 存放非零元素三元组的数组
int MaxTerms; // 非零元素数组最大长度
};
转置
步骤
- 首先我们先统计每一列有多少非零元素, 因为转置后就变成行, 也就是转置矩阵每一行有多少个非零元素
- 对每一列非零元素做累加, 就得到转置矩阵每一行的起始位置
- 可以直接根据原矩阵
列值通过rownext获取转置矩阵的索引进行复制
template<class T>
void SparseMatrix<T>::transpose(SparseMatrix<T>& b) const {
if (terms > b.MaxTerms) {
b.reSize(terms);
};
b.cols = rows;
b.rows = cols;
b.terms = terms;
// 统计每一列非零元素个数
// 这里为方便列与索引一一对应, 创建 cols + 1 大小的数组
int* ColSize = new int[cols + 1];
for (int i = 1; i <= cols; i++) {
ColSize[i] = 0;
}
for (int i = 0; i < terms; i++) {
ColSize[a[i].col]++;
}
// 做累加
int* RowNext = new int[cols + 1];
RowNext[1] = 0;
for (int i = 2; i <= cols; i++) {
RowNext[i] = RowNext[i - 1] + ColSize[i - 1];
}
// 复制元素
for (int i = 0; i < terms; i++) {
int j = RowNext[a[i].col]++; // 放了一个元素后后移一位
b.a[j].row = a[i].col;
b.a[j].col = a[i].row;
b.a[j].value = a[i].value;
}
}
时间复杂度:
矩阵相加
类似于两个有序链表的相加, 一个存放结果的数组 c, 再遍历两个数组, 谁元素小的加进 c, 然后指针后移, 直到其中一个遍历结束
// 矩阵相加
// b: 要加的矩阵, c: 保存结果的矩阵
template<class T>
void SparseMatrix<T>::add(const SparseMatrix<T>& b, SparseMatrix<T>& c) const {
if (rows != b.rows || cols != b.cols) {
throw "非同型矩阵不能相加";
}
// c 矩阵初始化
c.rows = rows;
c.cols = cols;
c.terms = 0;
// 双"指针", 分别遍历当前矩阵和 b 矩阵
int ct = 0, cb = 0;
// 因为行是有序的, 所以一个矩阵遍历结束后就可以退出循环
while (ct < terms && cb < b.terms) {
// 计算两个元素的行主次序编号
int indt = a[ct].row * cols + a[ct].col;
int indb = b.a[cb].row * cols + b.a[cb].col;
// b 的元素在后
if (indt < indb) {
// 那么就往 c 中添加 a 的元素
c.append(a[ct]);
ct++;
}
else if (indt == indb) { // 两个元素次序相同, 则相加
if (a[ct].value + b.a[cb].value) {
// 仅非零元素需要加进去
Term<T> t;
t.row = a[ct].row;
t.col = a[ct].col;
t.value = a[ct].value + b.a[cb].value;
c.append(t);
}
ct++; cb++;
}
else {
// b 的次序在前, 把 b 加进 c
c.append(b.a[cb]);
cb++;
}
}
// 将剩下的元素都是按顺序的, 直接加进去
for (; ct < terms; ct++) {
c.append(a[ct]);
}
for (; cb < b.terms; cb++) {
c.append(b.a[cb]);
}
}
时间复杂度(最多):
矩阵相乘
由于结果的非零项不确定, 因此只能先使用一个二维数组来保存计算结果
假设矩阵 a 和矩阵 b 相乘得矩阵 c, 则:
也就是说, a 矩阵中 列 为 i 的只能和 b 矩阵中 行 为 i 的相乘
值得注意的是, (1,1) 这个位置不止两个数相加(根据数学公式可以明显得出), 比如下面的 a(1,7) 与 b(7,1)
对于稀疏矩阵三元组的相加办法呼之欲出了
遍历两个稀疏矩阵, 找到左矩阵 列 与右矩阵 行 相等的, 其值相乘, += 到左矩阵 行, 右矩阵 列 对应的二维数组中, 最后遍历二维数组, 抽出值不为 0 的加到 c 矩阵中即可
核心代码:
// 开始遍历稀疏矩阵
for (int i = 0; i < terms; i++) {
for (int j = 0; j < b.terms; j++) {
// 左矩阵元素的列要和右矩阵元素的行相对应才能相乘
if (a[i].col == b.a[j].row) {
res[a[i].row][b.a[j].col] += a[i].value * b.a[j].value;
}
}
}
完整代码:
template<class T>
void SparseMatrix<T>::multiply(const SparseMatrix<T>& b, SparseMatrix<T>& c) const {
if (cols != b.rows) throw "矩阵不兼容, 无法相乘";
// 初始化结果矩阵(rows × b.cols 矩阵)
// 0 位置不要了
T** res = new T* [rows + 1];
for (int i = 1; i <= rows; i++) {
res[i] = new T[b.cols + 1];
}
// 结果全部初始化为 0
for (int i = 1; i <= rows; i++) {
for (int j = 1; j <= b.cols; j++) {
res[i][j] = 0;
}
}
c.rows = rows;
c.cols = b.cols;
// 开始遍历稀疏矩阵
for (int i = 0; i < terms; i++) {
for (int j = 0; j < b.terms; j++) {
// 左矩阵元素的列要和右矩阵元素的行相对应才能相乘
if (a[i].col == b.a[j].row) {
res[a[i].row][b.a[j].col] += a[i].value * b.a[j].value;
}
}
}
// 遍历结果数组, 值不为 0 的存进 c 中
for (int i = 1; i <= rows; i++) {
for (int j = 1; j <= b.cols; j++) {
if (res[i][j] != 0){
Term<T> t(i, j, res[i][j]);
c.append(t);
}
}
}
for (int i = 1; i <= rows; i++) {
delete[] res[i];
}
delete[] res;
}
🐣 完整代码
#include<iostream>
using namespace std;
// 三元组
template <class T>
class Term {
public:
Term() {
row = col = value = 0;
};
Term(int r, int c, T v) : row(r), col(c), value(v) {};
int row, col;
T value;
};
template<class T>
class SparseMatrix {
friend ostream& operator << (ostream& out, const SparseMatrix<T>& x) {
out << "行 = " << x.rows << " 列 = " << x.cols << endl;
out << "非零元素个数 = " << x.terms << endl;
for (int i = 0; i < x.terms; i++) {
out << "(" << x.a[i].row << ',' << x.a[i].col
<< ") = " << x.a[i].value << endl;
}
return out;
};
friend istream& operator >> (istream& in, SparseMatrix<T>& x) {
cout << "输入矩阵的行, 列以及总共要输入多少数据:";
in >> x.rows >> x.cols >> x.terms;
if (x.terms > x.MaxTerms) { // 确保数组不会越界
x.reSize(x.terms);
};
for (int i = 0; i < x.terms; i++) {
cout << "输入第" << (i + 1) << "个元素的行, 列以及值: ";
in >> x.a[i].row >> x.a[i].col >> x.a[i].value;
}
return in;
};
public:
SparseMatrix(int maxTerms = 10);
~SparseMatrix() { delete[] a; };
void transpose(SparseMatrix<T>& b) const;
void add(const SparseMatrix<T>& b, SparseMatrix<T>& c) const;
void multiply(const SparseMatrix<T>& b, SparseMatrix<T>& c) const;
int Columns() { return cols; };
int Rows() { return rows; };
void reSize(int);
private:
void append(const Term<T>& t);
int rows, cols; // 矩阵行和列
int terms; // 非零元素个数
Term<T>* a; // 存放非零元素三元组的数组
int MaxTerms; // 非零元素数组最大长度
};
// 构造函数
template<class T>
SparseMatrix<T>::SparseMatrix(int maxTerms) {
if (maxTerms < 1) throw "初始化错误";
MaxTerms = maxTerms;
a = new Term<T>[MaxTerms];
terms = rows = cols = 0;
}
// 矩阵转置
template<class T>
void SparseMatrix<T>::transpose(SparseMatrix<T>& b) const {
if (terms > b.MaxTerms) {
b.reSize(terms);
};
b.cols = rows;
b.rows = cols;
b.terms = terms;
// 统计每一列非零元素个数
// 这里为方便列与索引一一对应, 创建 cols + 1 大小的数组
int* ColSize = new int[cols + 1];
for (int i = 1; i <= cols; i++) {
ColSize[i] = 0;
}
for (int i = 0; i < terms; i++) {
ColSize[a[i].col]++;
}
// 做累加
int* RowNext = new int[cols + 1];
RowNext[1] = 0;
for (int i = 2; i <= cols; i++) {
RowNext[i] = RowNext[i - 1] + ColSize[i - 1];
}
for (int i = 0; i < terms; i++) {
int j = RowNext[a[i].col]++; // 放了一个元素后后移一位
b.a[j].row = a[i].col;
b.a[j].col = a[i].row;
b.a[j].value = a[i].value;
}
}
// 矩阵相加
// b: 要加的矩阵, c: 保存结果的矩阵
template<class T>
void SparseMatrix<T>::add(const SparseMatrix<T>& b, SparseMatrix<T>& c) const {
if (rows != b.rows || cols != b.cols) {
throw "非同型矩阵不能相加";
}
// c 矩阵初始化
c.rows = rows;
c.cols = cols;
c.terms = 0;
// 双"指针", 分别遍历当前矩阵和 b 矩阵
int ct = 0, cb = 0;
// 因为行是有序的, 所以一个矩阵遍历结束后就可以退出循环
while (ct < terms && cb < b.terms) {
// 计算两个元素的行主次序编号
int indt = a[ct].row * cols + a[ct].col;
int indb = b.a[cb].row * cols + b.a[cb].col;
// b 的元素在后
if (indt < indb) {
// 那么就往 c 中添加 a 的元素
c.append(a[ct]);
ct++;
}
else if (indt == indb) { // 两个元素次序相同, 则相加
if (a[ct].value + b.a[cb].value) {
// 仅非零元素需要加进去
Term<T> t(a[ct].row, a[ct].col, a[ct].value + b.a[cb].value);
c.append(t);
}
ct++; cb++;
}
else {
c.append(b.a[cb]);
cb++;
}
}
// 将剩下的元素直接加进去
for (; ct < terms; ct++) {
c.append(a[ct]);
}
for (; cb < b.terms; cb++) {
c.append(b.a[cb]);
}
}
// 矩阵相乘
template<class T>
void SparseMatrix<T>::multiply(const SparseMatrix<T>& b, SparseMatrix<T>& c) const {
if (cols != b.rows) throw "矩阵不兼容, 无法相乘";
// 初始化结果矩阵(rows × b.cols 矩阵)
// 0 位置不要了
T** res = new T* [rows + 1];
for (int i = 1; i <= rows; i++) {
res[i] = new T[b.cols + 1];
}
// 结果全部初始化为 0
for (int i = 1; i <= rows; i++) {
for (int j = 1; j <= b.cols; j++) {
res[i][j] = 0;
}
}
c.rows = rows;
c.cols = b.cols;
// 开始遍历稀疏矩阵
for (int i = 0; i < terms; i++) {
for (int j = 0; j < b.terms; j++) {
// 左矩阵元素的列要和右矩阵元素的行相对应才能相乘
if (a[i].col == b.a[j].row) {
res[a[i].row][b.a[j].col] += a[i].value * b.a[j].value;
}
}
}
// 遍历结果数组, 值不为 0 的存进 c 中
for (int i = 1; i <= rows; i++) {
for (int j = 1; j <= b.cols; j++) {
if (res[i][j] != 0){
Term<T> t(i, j, res[i][j]);
c.append(t);
}
}
}
for (int i = 1; i <= rows; i++) {
delete[] res[i];
}
delete[] res;
}
// 矩阵重新设置大小
template<class T>
void SparseMatrix<T>::reSize(int sz) {
Term<T>* temp = new Term<T>[sz];
copy(a, a + terms, temp);
delete[] a;
a = temp;
MaxTerms = sz;
}
// 尾部添加新元素
template<class T>
void SparseMatrix<T>::append(const Term<T>& t) {
if (terms >= MaxTerms) {
this->reSize(MaxTerms * 2);
};
a[terms] = t;
terms++;
}
int main() {
SparseMatrix<int> a;
cin >> a;
cout << a << endl;
SparseMatrix<int> b;
cin >> b;
cout << b << endl;
SparseMatrix<int> c;
a.multiply(b, c);
cout << c;
return 0;
}