文章目录
- 什么是矩阵?
- 什么是压缩存储?
- 一.对称矩阵的压缩存储:
- 三. 对角矩阵的压缩存储:
- 四. 稀疏矩阵的压缩存储:
- **稀疏矩阵的链式存储结构 (十字链表)**
什么是矩阵?
什么是压缩存储?
一.对称矩阵的压缩存储:
- 样式:
- 储存方式图示:
- 对称矩阵的存储结构:
- 对称矩阵的压缩存储:
//对称矩阵的压缩存储
#include <stdio.h>
#define len 5
int main(){
//定义对称矩阵
int A[len][len] = {1,2,3,4,5,
2,3,4,5,6,
3,4,5,6,7,
4,5,6,7,8,
5,6,7,8,9};
//定义存储数组
int B[len*(len + 1) / 2];
//进行压缩存储
for(int i = 0;i < len ;i++){
for(int j = 0;j <= i;j++){
if (i >= j) {
B[i*(i+1)/2+j] = A[i][j]; // 二维转一维
} else break;
}
}
printf("压缩矩阵的元素是:\n");
//输出B中元素
for (int k = 0; k < len*(len + 1) / 2; ++k) {
printf("%d ",B[k]);
}
return 0;
}
- 对称矩阵的基本应用:
#include <stdio.h>
#include <stdlib.h>
#define M 10
#define N 4
int main()
{
int a[M]={1,2,3,4,5,6,7,8,9,10};
int b[M]={1,1,1,1,1,1,1,1,1,1};
int c[N][N],d[N][N];
int i,j,k=0,s;
for(i=0;i<N;i++)
for(j=0;j<=i;j++)
{
c[i][j]=a[k];
d[i][j]=b[k];
k++;
}
for(i=0;i<N-1;i++)
for(j=i+1;j<N;j++)
{
c[i][j]=c[j][i];
d[i][j]=d[j][i];
}
printf("1、输出对称矩阵M:\n");
for(i=0;i<N;i++)
{
for(j=0;j<N-1;j++)
printf("%d ",c[i][j]);
printf("%d\n",c[i][j]);
}
printf("2、输出对称矩阵N:\n");
for(i=0;i<N;i++)
{
for(j=0;j<N-1;j++)
printf("%d ",d[i][j]);
printf("%d\n",d[i][j]);
}
printf("3、两个对称矩阵M、N的和为:\n");
for(i=0;i<N;i++)
{
for(j=0;j<N-1;j++)
printf("%d ",c[i][j]+d[i][j]);
printf("%d\n",c[i][j]+d[i][j]);
}
printf("3、两个对称矩阵M、N的积为:\n");
for(i=0;i<N;i++)
{
for(j=0;j<N-1;j++)
{
s=0;
for(k=0;k<N;k++) s+=c[j][k]*d[k][j];
printf("%d ",s);
}
s=0; for(k=0;k<N;k++) s+=c[j][k]*d[k][j];
printf("%d\n",c[i][j]+d[i][j]);
}
return 0;
}
二.三角矩阵的压缩存储
以主对角线划分三角矩阵有下三角矩阵和上三角矩阵
下三角矩阵:矩阵(除主对角线)的上三角部分的值均为一个常数C或者0
上三角矩阵:与下三角矩阵相反
图示:(图中蓝色主对角线部分元素(一般情况)永远不都为一个常数或者0)
二.压缩原理:
根据上、下三角矩阵的特殊性(有一小半部分的元素都为一个常数C或者0)我们可以考虑将这一半的空间压缩到一个元素(多对一的映射),然后另一半的部分就类似对称矩阵一半部分的压缩存储了。
三.矩阵坐标[i,j]转换为一维数组下标K的方法:
1>下三角矩阵:
(1).首先考虑,非重复部分的存储,见图:
可以得出 k = i(i-1)/2+j-1
(2).重复部分只需一个元素即可,为不影响前面的存储,考虑放到所有非重复元素的后面即可, 由于前面部分总共的元素个数为:
n(1+n)/2(等差数列求和,每行元素逐级递增)又由于数组以0为起点,所以放到n(1+n)/2的位置即可。
总结:
k = i(i-1)/2+j-1 (i >=j)
k = n(n+1)/2 (i>j)
2>上三角矩阵
(1).先考虑非重复部分的存储,图示:
按行优先存储,矩阵中元素对应一维数组的元素规则为:
由图显而易见,某元素在一维数组中的下标就是它按行优先存储时在半个矩阵中的序号
(1-1)序号 = 所有在它前面元素的个数 = 它所在行前面行的所有元素+它所在行它前面的元素
由于每行元素个数逐级递减,构成一个等差数列,公差:d = 1,首项:a1 = n ,末项:an = n-(i-1) = n-i+1(经评论区提醒,已自纠)
(1-2)前i-1行元素个数为:sn = n(a1+an)/2 = (i-1){n+[n-(i-1)+1]}/2=(i-1)(2n-i+2)/2
(1-3)第i行中在它前面的元素个数为:j-1
(1-4)公式:K =(i-1)(2n-i+2)/2+j-1
(2).考虑重复部分元素和下三角一样:k = n(n+1)/2
总结:
K = (i-1)(2n-i+2)/2+j-i-1 (i >=j)
K = n(n+1)/2 (i>j)
- 下三角矩阵压缩存储代码实现:
#include<stdio.h>
int main() {
int a[5][5] = {
1,0, 0, 0, 0,
5,9, 0, 0, 0,
4,6, 8, 0, 0,
2,3, 44,55,0,
7,11,12,13,14
};
int len = sizeof(a[5])/sizeof(int);
int b[len*(len+1)/2], x, y, k;
printf("原二维数组:\n"); //输出原二维数组
for (x = 0; x < len; x++){
for (y = 0; y < len; y++){
if (a[x][y] < 10){ // 使得输出更加整齐
printf("%d ", a[x][y]);
} else {
printf("%d ", a[x][y]);
}
}
printf("\n");
}
printf("压缩后的一维数组:\n");
for (int i = 0; i < len; i++){ //将二维数组中非0值压缩至一维数组中
for (int j = 0; j < len; j++){
if (i >= j) { //特殊矩阵,只压下三角的值
k = i * (i + 1) / 2 + j; // 二维数组和一维数组中原值的对应关系
b[k] = a[i][j];
} else break; // 提升性能
}
}
for (int l = 0; l < len*(len+1)/2; l++){ //输出一维数组
printf("%d ", b[l]);
}
printf("\n");
printf("输入要查询的 行号&&列号 : "); //输出要查询的数据
scanf("%d%d", &x, &y);
printf("\n");
printf("您查询的数据是: ");
if (x < y) printf("0\n"); //如果上三角直接输出0
else printf("%d\n", b[(x - 1) * (x) / 2 + y - 1]); // 下三角输出一维数组中对应的值
return 0;
}
三. 对角矩阵的压缩存储:
存储方法:
二维数组存储
所有非主对角线元素全等于零的n阶矩阵,称为对角矩阵或称为对角方阵。
对角矩阵压缩存储代码实现:
#include <stdio.h>
#include <stdlib.h>
#define n 4
int d[n];
void Store(int x, int i, int j){
/* 把x存为D ( i , j ) */
if (i<0||j<0||i>=n||j>=n)
{
printf("数组出界!");
exit(1);
}
if (i != j && x != 0)
{
printf("非对角线上元素值必须为零");
exit(1);
}
if (i == j)
d[i] = x;
}
int main(){
int i,j;
int D[n][n] ={{2,0,0,0},{0,1,0,0},{0,0,4,0},{0,0,0,6}};
for(i=0;i<n;i++){
for(j=0;j<n;j++){
Store(D[i][j],i,j);
}
}
for(i=0;i<n;i++) {
printf("%d ",d[i]);
}
printf("\n");
return 0;
}
四. 稀疏矩阵的压缩存储:
三元组(i , j ,aij)唯一确定矩阵的一个非零元=
三元组顺序表:
三元顺序表的优缺点:
- 稀疏矩阵的三元组压缩存储以及简单应用:
// δ=t/(n?m) <= 0.05 则证明是稀疏矩阵
/* 稀疏矩阵的类型说明及转置算法 */
#include<stdio.h>
#include<stdlib.h>
#define MAX 12
typedef int datatype;
typedef struct{
int i, j; //行号、列号
datatype v; //元素值
} node;
typedef struct{
int m, n, t; //行数,列数,非零元素个数
node data[MAX]; //三元组表
}spMatrix;
spMatrix *Create(); //创建一个3行4列的稀疏矩阵
spMatrix *TransMat(spMatrix *a); //稀疏矩阵的转置
void Output(spMatrix *a); //在屏幕上以行列的形式输出矩阵
int main(){
printf("输入12个整数:\n");
spMatrix *a = Create();
spMatrix *b = TransMat(a);
printf("原矩阵:\n");
Output(a);
printf("转置矩阵:\n");
Output(b);
return 0;
}
spMatrix *Create(){ // 创建一个3行4列的稀疏矩阵
int m = 3, n = 4, k = 0, t = 0;
datatype element;
spMatrix *matrix;
matrix = (spMatrix *)malloc(sizeof(spMatrix)); //创建一个稀疏矩阵a
matrix->m = m;
matrix->n = n;
while (k < m*n){
scanf("%d", &element);
if (element != 0){
matrix->data[t].i = k / n;
matrix->data[t].j = k % n;
matrix->data[t].v = element;
t++;
}
k++;
}
matrix->t = t;
return matrix;
}
spMatrix *TransMat(spMatrix *a){ //稀疏矩阵的转置
int p, q, bno = 0;
spMatrix *b;
b = (spMatrix *)malloc(sizeof(spMatrix)); //为矩阵b分配内存空间
b->m = a->n;
b->n = a->m;
b->t = 0;
if (a->t == 0) //若b中元素全为零,则将b返回
return b;
for (p = 0; p < a->n; p++)
for (q = 0; q < a->t; q++)
if (a->data[q].j == p){
b->data[bno].i = a->data[q].j;
b->data[bno].j = a->data[q].i;
b->data[bno].v = a->data[q].v;
bno++;
}
b->t = bno;
return b;
}
void Output(spMatrix *a){
//输出:在屏幕上以行列的形式输出矩阵
int i = 0, j = 0, k = 0;
for (i = 0; i < a->m; i++){
for (j = 0; j < a->n; j++){
if (i == a->data[k].i && j == a->data[k].j){
printf("%d ", a->data[k].v);
k++;
}
else
printf("%d ", 0);
}
printf("\n");
}
}
稀疏矩阵的链式存储结构 (十字链表)
优点:它能够灵活地插入因运算而产生的新的非零元素,删除因运算而产生的新的零元素,实现矩阵的运算。
在十字链表中,矩阵的每一个非零元素用一个结点表示,该结点除了(row,col,value)外,还有两个域:
right: 用于链接同一行中的下一个非零元素;
down:用以链接同一列中的下一个非零元素。
十字链表中结点的结构示意图:
稀疏矩阵十字链表压缩存储实现:
// 压缩矩阵的十字矩阵压缩存储
#include<stdio.h>
#include<stdlib.h>
#define OK 1
#define ERROR 0
#define OVERFLOW -2
typedef int ElemType;
typedef struct OLNode{
int i,j; // 非零元的行下标和列下标
ElemType e; // 非零元的值
struct OLNode *right, *down; // 该非零元所在的行表和列表的后继链域
}OLNode, *OLink;
typedef struct{
OLink *rhead, *chead; // 行和链链表头指针向量基址
int mu,nu,tu; // 行数,列数,非零个数
}CrossList;
int CreateSMatrix_OL(CrossList *M){
if(M) free(M); // 如果十字链已存在,则先释放
int m,n,t=0;
scanf("%d%d",&m,&n); // 矩阵的行列
M->mu=m; M->nu=n, M->tu = t; // 行列数赋值
if(!(M->rhead=(OLink *)malloc((m+1)*sizeof(OLink)))) return ERROR; // 创建行指针,创建失败则返回 ERROR
if(!(M->chead=(OLink *)malloc((n+1)*sizeof(OLink)))) return ERROR; // 创建列指针,创建失败则返回 ERROR
for (int a = 1; a <= m; a++) M->rhead[a]=NULL; // 初始化置NULL
for (int b = 1; b <= n; b++) M->chead[b]=NULL; // 初始化置NULL
int e; // 定义输入元素
for(int i = 1;i <= m; i++){
for(int j = 1; j <= n; j++){
scanf("%d",&e);
if(e!=0){
t++; // 非零个数累加
OLNode *p,*q;
if(!(p = (OLNode *)malloc(sizeof(OLNode)))) exit(OVERFLOW); // 创建一个 p指针节点,用于储存输入的数据
p->i=i; p->j=j; p->e=e; p->down=NULL; p->right=NULL; // 节点赋值
if(M->rhead[i] == NULL || M->rhead[i]->j > j){
p->right=M->rhead[i];
M->rhead[i]=p;
}else{ // 寻找在行表中的插入位置
for(q = M->rhead[i]; (q->right) && q->right->j < j; q = q->right);
p->right=q->right;
q->right=p;
}
if(M->chead[j] == NULL || M->chead[j]->i > i){
p->down=M->chead[j];
M->chead[j]=p;
}else{ // 寻找在列表中的插入位置
for(q = M->chead[j]; (q->down) && q->down->i < i; q = q->down);
p->down=q->down;
q->down=p;
}
}
}
}
M->tu=t;
return OK;
}
void print(CrossList M){
OLNode *pTemp;
for(int p = 1; p <= M.mu; p++){
pTemp=M.rhead[p];
for(int q = 1; q <= M.nu; q++){
if(pTemp != NULL && pTemp->j == q){
printf("%d ",pTemp->e);
pTemp = pTemp->right;
}
else
printf("0 ");
}
printf("\n");
}
}
int main(){
CrossList M;
CreateSMatrix_OL(&M);
print(M);
return 0;
}