文章目录
7. 数组 - 数组存储结构、矩阵压缩存储方式
7.1 数组存储结构基本概念
- 从本质上讲,
数组
与顺序表、链表、栈和队列一样,都用来存储具有"一对一" 逻辑关系
数据的线性存储结构
。 - 根据数组中存储数据之间逻辑结构的不同,数组可细分为
一维数组
、二维数组
、…、n 维数组
。 - 数组中数据的存储有
两种先后存储方式
:以列序为主(先列后行)
:按照行号从小到大的顺序,依次存储每一列的元素以行序为主(先行后序)
:按照列号从小到大的顺序,依次存储每一行的元素。多维数组查找指定元素
时,需知道以下信息:
- 多维数组的
存储方式
;- 多维数组在内存中存放的
起始地址
; - 该指定元素在原多维数组的
坐标
(比如说,二维数组中是通过行标和列标来表明数据元素的具体位置的); - 数组中
数组的具体类型
,即数组中单个数据元素所占内存的大小,通常用字母 L 表示;
- 多维数组在内存中存放的
- 例如 :
二维数组采用以行序为主
的方式,则在二维数组 a n m a_{nm} anm中查找 a i j a_{ij} aij存放位置的公式为:
L O C ( i , j ) = L O C ( 0 , 0 ) + ( i ∗ m + j ) ∗ L LOC(i,j) = LOC(0,0) + (i*m + j) * L LOC(i,j)=LOC(0,0)+(i∗m+j)∗L
如果采用以列存储
的方式,在 a n m a_{nm} anm中查找 a i j a_{ij} aij的方式为:
L O C ( i , j ) = L O C ( 0 , 0 ) + ( i ∗ n + j ) ∗ L LOC(i,j) = LOC(0,0) + (i*n + j) * L LOC(i,j)=LOC(0,0)+(i∗n+j)∗L - 数据常用来存储矩阵,对矩阵进行操作
7.2 特殊矩阵概念
7.2.1 对称矩阵
对称矩阵
:数据元素沿主对角线对应相等
的矩阵
压缩存储思想
:由于矩阵中沿对角线两侧的数据相等,因此数组中只需存储对角线一侧(包含对角线)的数据即可,即可以用一维数组存储对称矩阵
。- 压缩公式 :
存储下三角
的元素,只需将各元素所在的行标 i
和列标 j
代入如下的公式:
k = i ∗ ( i − 1 ) 2 + j − 1 k = \frac {i*(i-1)}{2 } + j -1 k=2i∗(i−1)+j−1
存储上三角
的元素,只需将各元素所在的行标 i
和列标 j
代入如下的公式:
k = j ∗ ( j − 1 ) 2 + i − 1 k = \frac {j*(j-1)}{2 } + i -1 k=2j∗(j−1)+i−1
最终求得的k 值
即为该元素存储到数组中的位置(矩阵中元素的行标和列标都从 1 开始),例如上图所示的对称矩阵压缩后为:skr[6] = { 1, 2, 4, 3, 5, 6}
7.2.2 上(下)三角矩阵
- 主对角线下的数据元素全部相同的矩阵为
上三角矩阵
(图 a)),主对角线上元素全部相同的矩阵为下三角矩阵
(图 b))。
- 压缩存储的方式是:
上(下)三角矩阵采用对称矩阵的方式存储上(下)三角的数据
(元素 0 不用存储),上(下)三角矩阵存储元素和提取元素的过程和对称矩阵相同。
7.2.3 稀疏矩阵
稀疏矩阵
:矩阵中分布有大量的元素 0,即非 0 元素非常少
- 稀疏矩阵的压缩存储思想为:存储矩阵中的
非 0 元素和该元素所在矩阵中的行标和列标
,加上矩阵的行数和列数
。
7.3 稀疏矩阵压缩算法
7.3.1 三元组顺序表
三元组顺序表
压缩存储方法的思想为 :按照压缩思想,用一个三元素结构体存储每个非0元素的(行标、列标、元素值)
,每个三元组顺序存储到一个数组中(该数组为结构体数组),然后存储矩阵的行数、列数、非0元素总个数
。
// 三元组存储结构的定义
typedef struct {
int i; // i : 数据的行标,从1开始
int j; // j : 数据的列标,从1开始
int data; // data : 非0元素值
}triple;
typedef struct {
triple data[number]; // 数据段存储
int n; // 矩阵的行数
int m; // 矩阵的列数
int num; // 非0元素的总个数
}TSMatrix;
- 示例代码如下:
#include <stdio.h>
#include <stdlib.h>
#define number 3
// 三元组存储结构的定义
typedef struct {
int i; // i : 数据的行标,从1开始
int j; // j : 数据的列标,从1开始
int data; // data : 非0元素值
}triple;
typedef struct {
triple data[number]; // 数据段存储
int n; // 矩阵的行数
int m; // 矩阵的列数
int num; // 非0元素的总个数
}TSMatrix;
// 输出存储的稀疏矩阵
void display(TSMatrix M) {
for (int i = 1; i <= M.n; i++) {
for (int j = 1; j <= M.m; j++) {
int value = 0;
for (int k = 0; k < M.num; k++) {
if (i == M.data[k].i && j == M.data[k].j) {
printf("%d ", M.data[k].data);
value = 1;
break;
}
}
if (value == 0)
printf("0 ");
}
printf("\n");
}
}
int main() {
TSMatrix M;
M.m = 3;
M.n = 3;
M.num = 3;
M.data[0].i = 1;
M.data[0].j = 1;
M.data[0].data = 1;
M.data[1].i = 2;
M.data[1].j = 3;
M.data[1].data = 5;
M.data[2].i = 3;
M.data[2].j = 1;
M.data[2].data = 3;
display(M);
return 0;
}
7.3.2 行逻辑链接的顺序表
行逻辑链接的顺序表
可以看作是三元组顺序表的升级版,解决了三元组顺序表每次提取指定元素都需要遍历整个数组,运行效率低的问题。- 思想为 :在三元组顺序表的基础上,多使用了一个数组,专门记录矩阵中每行第一个非 0 元素在一维数组(三元组结构体数组)中的位置。
// 行逻辑链接的顺序表结构体定义
typedef struct {
int i; // i : 数据的行标,从1开始
int j; // j : 数据的列标,从1开始
int data; // data : 非0元素值
}triple;
typedef struct {
triple data[number]; // 数据段存储
int rpos[num]; // 存储矩阵每行第一个非零元素在data数组中的位置
int n; // 矩阵的行数
int m; // 矩阵的列数
int num; // 非0元素的总个数
}RLSMatrix;
- 示例代码如下:
#include <stdio.h>
#include <stdlib.h>
#define number 5
// 行逻辑链接的顺序表结构体定义
typedef struct {
int i; // i : 数据的行标,从1开始
int j; // j : 数据的列标,从1开始
int data; // data : 非0元素值
}triple;
typedef struct {
triple data[number]; // 数据段存储
int rpos[number]; // 存储矩阵每行第一个非零元素在data数组中的位置
int n; // 矩阵的行数
int m; // 矩阵的列数
int num; // 非0元素的总个数
}RLSMatrix;
// 输出存储的稀疏矩阵
void display(RLSMatrix M) {
for (int i = 1; i <= M.n; i++) {
for (int j = 1; j <= M.m; j++) {
int value = 0;
if (i + 1 <= M.n) {
for (int k = M.rpos[i]; k < M.rpos[i + 1]; k++) {
if (i == M.data[k].i && j == M.data[k].j) {
printf("%d ", M.data[k].data);
value = 1;
break;
}
}
if (value == 0) {
printf("0 ");
}
}
else {
for (int k = M.rpos[i]; k <= M.num; k++) {
if (i == M.data[k].i && j == M.data[k].j) {
printf("%d ", M.data[k].data);
value = 1;
break;
}
}
if (value == 0) {
printf("0 ");
}
}
}
printf("\n");
}
}
int main(int argc, char* argv[])
{
RLSMatrix M;
M.num = 4;
M.n = 3;
M.m = 4;
M.rpos[1] = 1;
M.rpos[2] = 3;
M.rpos[3] = 4;
M.data[1].data = 3;
M.data[1].i = 1;
M.data[1].j = 2;
M.data[2].data = 5;
M.data[2].i = 1;
M.data[2].j = 4;
M.data[3].data = 1;
M.data[3].i = 2;
M.data[3].j = 3;
M.data[4].data = 2;
M.data[4].i = 3;
M.data[4].j = 1;
display(M);
return 0;
}
7.3.3 十字链表
- 十字链表存储稀疏矩阵思想为 :采用的是
"链表+数组"
结构,链表的每个节点存储(行标,列表,元素值,指向下一个列节点的指针,指向下一个行节点的指针)
;与此同时,所有行链表的表头存储到一个数组(rhead)中,所有列链表的表头存储到另一个数组(chead)中。 - 每个节点示意图:
- 十字链表存储示意图:
- 链表定义:
typedef struct OLNode
{
int i, j, e; // 矩阵三元组 : i代表行, j代表列, e代表当前位置的数据
struct OLNode* right; // 指针域 : 右指针
struct OLNode* down; // 指针域 : 下指针
}OLNode, * OLink;
typedef struct
{
OLink* rhead, * chead; // 行和列链表头指针
int mu, nu, tu; // 矩阵的行数,列数和非零元的个数
}CrossList;
- 操作示例代码如下:
#include<stdio.h>
#include<stdlib.h>
typedef struct OLNode
{
int i, j, e; // 矩阵三元组 : i代表行, j代表列, e代表当前位置的数据
struct OLNode* right; // 指针域 : 右指针
struct OLNode* down; // 指针域 : 下指针
}OLNode, * OLink;
typedef struct
{
OLink* rhead, * chead; // 行和列链表头指针
int mu, nu, tu; // 矩阵的行数,列数和非零元的个数
}CrossList;
CrossList CreateMatrix_OL(CrossList M)
{
int m, n, t;
int i, j, e;
OLNode* p, * q;
printf("输入矩阵的行数、列数和非0元素个数:");
scanf("%d %d %d", &m, &n, &t);
M.mu = m;
M.nu = n;
M.tu = t;
if (!(M.rhead = (OLink*)malloc((m + 1) * sizeof(OLink))) || !(M.chead = (OLink*)malloc((n + 1) * sizeof(OLink))))
{
printf("初始化矩阵失败");
exit(0);
}
for (i = 1; i <= m; i++)
{
M.rhead[i] = NULL;
}
for (j = 1; j <= n; j++)
{
M.chead[j] = NULL;
}
for (scanf("%d%d%d", &i, &j, &e); 0 != i; scanf("%d%d%d", &i, &j, &e)) {
if (!(p = (OLNode*)malloc(sizeof(OLNode))))
{
printf("初始化三元组失败");
exit(0);
}
p->i = i;
p->j = j;
p->e = e;
//链接到行的指定位置
if (NULL == M.rhead[i] || 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 (NULL == M.chead[j] || 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;
}
}
return M;
}
void display(CrossList M) {
for (int i = 1; i <= M.nu; i++)
{
if (NULL != M.chead[i])
{
OLink p = M.chead[i];
while (NULL != p)
{
printf("%d\t%d\t%d\n", p->i, p->j, p->e);
p = p->down;
}
}
}
}
int main()
{
CrossList M;
M.rhead = NULL;
M.chead = NULL;
M = CreateMatrix_OL(M);
printf("输出矩阵M:\n");
display(M);
return 0;
}
感谢阅读 若有错误 敬请见谅!!!