一、概念
严版数据结构:特殊矩阵:相同元素或者零元素在矩阵中的分布存在一定规律的矩阵,反之称为稀疏矩阵。
国外:矩阵中绝大多数元素都为0的矩阵称为稀疏矩阵。
二、特殊矩阵存储
一般的特殊矩阵是:对称矩阵、上三角矩阵、下三角矩阵、对角矩阵。
特殊矩阵存储这里列,上、下三角矩阵的存储。对称矩阵是按角对角线对称。
由对称矩阵A[i][j] = A[j][i],所以我们只需要保存一份即可,所需储存空间为(1+n)*n/2, 需要保存的元素为:
A(0,0)
A(1,0) A(1,1)
A(2,0) A(2,1) A(2,2)
....
A(n,0) A(n,1) A(n,2) .... A(n,n)
按照行优先来存储,保存在一维数组中。
分析:矩阵A中要储存元素个数是n(n+1)/2个,因为每行成等差数列。
结果如下:每一个元素对应一维数组的位置 i*(i+1)/2+j , i 表示行数,j 表示列数。
保存元素:A(0,0) A(1,0) A(1,1) ... A(n-1,0) A(n-1,1) A(n-1,2) .... A(n-1,n-1)
数组下标: 0 1 2 ... n*(n-1)/2 n*(n-1)/2+1 n*(n-1)/2+2 n*(n+1)/2-1
#include<stdio.h>
int MaxSize;
int A[10][10];
int saveArr[100];
//初始化数组
void init() {
for(int i=0; i<MaxSize; ++i) {
for(int j=0; j<MaxSize; ++j) {
if(i>j) {
A[i][j]=i;
} else if(i<j) {
A[i][j]=j;
} else {
A[i][j]=0;
}
}
}
printf("很明显这是一个对称矩阵\nA:\n");
for(int i=0; i<MaxSize; i++) {
for(int j=0; j<MaxSize; j++) {
printf("%d ",A[i][j]);
}
printf("\n");
}
printf("\n");
}
//存储对称矩阵的下三角。
void save() {
int i,j,temp;
int k=0;
for(i=0; i<MaxSize; ++i) {
for(j=0; j<MaxSize; ++j) {
if(i>=j) {
saveArr[i*(i+1)/2+j]=A[i][j];
printf("%d ",A[i][j]);
}
}
printf("\n");
}
printf("\n一维数组中存入的数据\n");
for(i=0; i<(MaxSize*(MaxSize+1)/2); i++) { //如果是n维矩阵,则为n*(n+1)/2
printf("%d ",saveArr[i]);
}
}
int main() {
scanf("%d",&MaxSize);
init();
save();
}
三、三元组法表示稀疏矩阵
稀疏矩阵中的相同元素C,假设C是0,在矩阵中的分布不像在特殊矩阵中那么有规律,所以我们用数据结构将C存起来,并保存原始C在稀疏矩阵的位置信息。
三元组数据结构为一个长度为n,表内每个元素都有3个分量的线性表,其中3个分量分别为值、行下标、列下标。元素结构体定义如下:
typedef struct Node{
int data; //储存数据
int line; //行下标
int row; //列下标
}Trimat;
一般我们可以用二维数组代替,即int A[maxterm+1][3],注意如果data为float类型,则 line或row 使用时,需要使用int进行强制转换。
例子:A矩阵如下:
0 0 0 1
0 0 3 2
1 0 0 0
0 2 0 0
则三元组表示为:
元素值 行下标 列下标
0 5 4 4 //第一行,元素值表示该矩阵中稀疏元素的个数,行下标值该矩阵的行数,列下标指该矩阵的列数。
1 1 0 3
2 3 1 2
3 2 1 3
4 1 2 0
5 2 3 1
创建代码,就不写了,直接一个循环遍历矩阵,看那个不是0就按上述格式加入到结构体数组中。
四、伪地址表示稀疏矩阵
伪地址即元素在矩阵中按照行优先或者列优先储存相对位置。伪地址法每一行只有两个储存单元,一个用来存放矩阵元素值,另一个用来存放伪地址。这种方法需要2N个储存单元,N为非0元素的个数,对于一个m*n的稀疏矩阵A,元素A[i][j]的伪地址计算方法伪n*i+j。
0 0 0 1
0 0 3 2
1 0 0 0
0 2 0 0
伪地址分别是:3,6,7,8,13
五、稀疏矩阵的链式储存
1.邻接表表示法(节约空间)
将矩阵种每一行非零元素串成一个链表,链表结点中有两个分量,分别表示该结点对应的元素值和列下标。
typedef struct Node{
int data; //储存数据
int row; //列下标
struct Node *next;
}Trimat[100];
具体实现过于简单,循环遍历矩阵,为每一行的非零元素在 Trimat[i] 中创建单链表即可,这里没有专门的头结点。
2.十字链表表示法
矩阵的每一行用一个带头结点的链表表示,每一列用一个带头结点的链表表示,这种储存结构中有5个分量:行分量,列分量、数据分量、以及指向两头结点的指针。
普通结点定义:
typedef struct node{
int row,col;
struct node *right,*down;
int data;
}Node;
头结点定义:
typedef struct{
Node *rhead,*chead;
int m , n , k;
}CrossList;