参考:数据结构(c++版)(第二版)清华大学出版社 陈宝平等主编
核心:
1.用一个三元组保存不为0的部分(本质是定义一个类,存放不为0元素的 值 , 行 列坐标)
2.定义一个稀疏矩阵类,里面有三元组类的一维数组,本解法用的是静态
(可以用指针实现动态,先分配一个小空间,如果空间不够,就重新声明一个指针开一个大小为 (原空间大小+1) 的空间,把值都复制进去,再释放原指针空间,最后把原指针指向重新声明的指针)
3相加,相减,相乘都是把他扩展成二维数组,对应到代码就是两层循环 在循环中判断该行列坐标下的元素的值,对应到代码就是循环遍历存放三元组类的数组,看下标是否吻合
(这种方法时间复杂度很高,但思路直接,对于加减,可以把要相加的两个三元组的数组合并到结果变量的数组中,再在结果变量的数组中合并行列相同的,去除值为0的,最后排序。对于相乘(假如是A*B),可以只看A中列数和B中行数相等的元素,因为其他的相乘都为0)
4.由于矩阵相乘有个累加的过程,所以比相加减多一层循环
5.转置可以普通转置,即直接更换行列坐标,也可快速转置(见文末)
快速转置主要是避免排序(按列从小到大排序)的时间损耗,但好像不排序也没什么大问题
6.拓展:还可以链式结构 :
1>把具有相同行号的三元组结点按照列号从小到大的顺序连成单链表
所以稀疏矩阵里对应地将一维数组改为存放每行链表头指针的一维数组
2>不仅同行连,同列也连成一个链表,形成十字链表,稀疏矩阵里对应
地增加一个放每列头指针的数组
细节:
1.循环遍历存放三元组类的数组时,需要一个标志flag表示是否找到,同时由于需要用到找到的三元组,所以找到后要马上break出来以保存下标
2.重新声明一个稀疏矩阵的变量来存放加减或乘的结果,注意返回值
3.注意下标,尤其是相乘时,见代码注释
4.加减乘的结果可能为0,为0时不应将他放入三元组的数组中
5.本解法是从0开始
#include<bits/stdc++.h>
using namespace std;
const int maxsize = 100;
class node
{
int row, col;
int value;
public:
friend class sparse_matrix;
};
class sparse_matrix
{
node data[maxsize];
int row_sum, col_sum, num;
public:
sparse_matrix() {}
sparse_matrix(int** a, int b, int c)
{
num = 0;
row_sum = b;
col_sum = c;
int i, j;
for (i = 0; i < b; i++)
{
for (j = 0; j < c; j++)
{
if (a[i][j] != 0)
{
data[num].value = a[i][j];
data[num].row = i;
data[num].col = j;
num++;
}
}
}
}
void print()
{
int i, j, k, flag = 0;
for (i = 0; i < row_sum; i++)
{
for (j = 0; j < col_sum; j++)
{
for (k = 0; k < num; k++)
{
if (i == data[k].row && j == data[k].col)
{
flag = 1;
break;
}
}
if (flag == 1)
{
cout << data[k].value << " ";
flag = 0;
}
else
{
cout << "0" << " ";
}
}
cout << endl;
}
}
sparse_matrix addmatrix(sparse_matrix a)//矩阵相加
{
int i, j, k, flag = 0;
sparse_matrix c;
c.col_sum = a.col_sum;
c.row_sum = a.row_sum;
c.num = 0;
for (i = 0; i < maxsize; i++)
{
c.data[i].value = 0;
}
for (i = 0; i < row_sum; i++)
{
for (j = 0; j < col_sum; j++)
{//两层循环相当于在遍历由稀疏矩阵拓展开的二维数组
for (k = 0; k < num; k++)//看该二维数组中哪些元素不为0,循环两次,一次是在this里找
{
if (i == data[k].row && j == data[k].col)
{
c.data[c.num].value += data[k].value;
}
}
for (k = 0; k < a.num; k++)//一次是在传入的a里找
{
if (i == a.data[k].row && j == a.data[k].col)
{
c.data[c.num].value += a.data[k].value;
}
}
if (c.data[c.num].value != 0)//因为可能加和后结果为0,故加一句判断
{
c.data[c.num].row = i;
c.data[c.num].col = j;
c.num++;//如果结果为0,c.data[]的下标(即c.num)不会增加
//,下一次动作还是对该下标,且其为0,不用再额外清零
}
}
}
return c;
}
sparse_matrix submatrix(sparse_matrix a)
{
int i, j, k, flag = 0;
sparse_matrix c;
c.col_sum = a.col_sum;
c.row_sum = a.row_sum;
c.num = 0;
for (i = 0; i < maxsize; i++)
{
c.data[i].value = 0;
}
for (i = 0; i < row_sum; i++)
{
for (j = 0; j < col_sum; j++)
{
for (k = 0; k < num; k++)
{
if (i == data[k].row && j == data[k].col)
{
c.data[c.num].value += data[k].value;
}
}
for (k = 0; k < a.num; k++)
{
if (i == a.data[k].row && j == a.data[k].col)
{
c.data[c.num].value -= a.data[k].value;
}
}
if (c.data[c.num].value != 0)
{
c.data[c.num].row = i;
c.data[c.num].col = j;
c.num++;
}
}
}
return c;
}
sparse_matrix mulmatirx(sparse_matrix a)
{
int i, j, k, flag = 0, l;//这里是this的矩阵*a的矩阵
sparse_matrix c;
c.col_sum = a.col_sum;
c.row_sum = row_sum;
c.num = 0;
for (i = 0; i < maxsize; i++)
{
c.data[i].value = 0;
}
int m, n;
for (i = 0; i < row_sum; i++)//遍历this矩阵的每一行
{
for (j = 0; j < a.col_sum; j++)//遍历a矩阵的每一列
{
for (k = 0; k < col_sum; k++)//当this矩阵的对应行*a矩阵对应列时,遍历每一个元素
{//k既为this矩阵的列,又为a矩阵的行
for (l = 0; l < num; l++)//判断对应位置元素是否为0
{
if (i == data[l].row && k == data[l].col)
{
flag = 1;
break;//注意找到了就要跳出去,因为后面有用到l,要保证l的值没变
}
}
if (flag == 1)
{
m = data[l].value;
flag = 0;
}
else
{
m = 0;
}
for (l = 0; l < a.num; l++)//判断对应位置元素是否为0
{
if (k == a.data[l].row && j == a.data[l].col)
{
flag = 1;
break;
}
}
if (flag == 1)
{
n = a.data[l].value;
flag = 0;
}
else
{
n = 0;
}
c.data[c.num].value += m * n;
}
if (c.data[c.num].value != 0)
{
c.data[c.num].row = i;
c.data[c.num].col = j;
c.num++;
}
}
}
return c;
}
sparse_matrix tranmatirx()
{
int i, j, k, flag = 0;
sparse_matrix c;
c.col_sum = row_sum;
c.row_sum = col_sum;//互换行数,列数的值
c.num = num;
for (i = 0; i < num; i++)
{
c.data[i].col = data[i].row;
c.data[i].row = data[i].col;//交换行列下标
c.data[i].value = data[i].value;
}
return c;
}
};
int main()
{
int** a;
int m, n, i, j;
while (1)
{
cin >> m >> n;
a = new int* [m];
for (i = 0; i < m; i++)
{
a[i] = new int[n];
}
for (i = 0; i < m; i++)
{
for (j = 0; j < n; j++)
{
cin >> a[i][j];
}
}
sparse_matrix p(a, m, n);
p.print();
cout << endl;
p.tranmatirx().print();
}
/*cin >> m >> n;
a = new int* [m];
for (i = 0; i < m; i++)
{
a[i] = new int[n];
}
for (i = 0; i < m; i++)
{
for (j = 0; j < n; j++)
{
cin >> a[i][j];
}
}
sparse_matrix p1(a, m, n);
p.mulmatirx(p1).print();
p.submatrix(p).print();
p.submatrix(p1).print();*/
}
/*1 0 0 0
0 1 0 0
0 0 1 0
0 0 0 1*/
/*1 2 3
*/
/*
2 3
1 2 3
4 5 6
3 2
1 4
2 5
3 6
*/
快速转置算法
思想:(这里的矩阵是从1开始)
1.引入两个数组,num,pot,用来确认转置后所处位置,避免排序的时间损耗
2.num[i]表示原矩阵中第i列(即转置后的矩阵的第i行)中非0元素的个数
3pot[i]表示原矩阵中第i列的第一个非0元素在转置后的位置
4.pot[i]=pot[i-1]+num[i-1],i>1;(转置后的矩阵的上一行开头的所在位置+转置后的矩阵的上一行的元素数量)
i==1时,pot[1]=1;
5.假设原矩阵是this,转置矩阵用T表示
int q=pot[i]; //q存放位置
T.data[q]. //直接[q]
…
pot[i]++; //++后就可以表示后续的位置,如第二个非0元素的位置