数组
1.数组的概述
- 数组也是用来存储“一对一”的逻辑关系数据的线性表。
- 顺序表、链表、栈和队列存储的都是不可再分的数据元素(如:int,char类型),但数组既可以用来存储不可再分的数据元素,也可以用来存储像顺序表、链表这样的数据结构。
- 按逻辑结构的不同,数组可细分:
- 无论数组的维数是多少,数组中的数据类型都必须一致,即是同一种数据类型。
2.数组的顺序存储结构
- 数组中数据的存储有两种先后存储方式:
- C 语言中,多维数组的存储采用的是以行序为主的顺序存储方式。
2.1数组中查找元素
- 查找元素之前,需要知道的信息如下:
- 多维数组的存储方式;————以行序为主?还是以列为主?
- 多维数组在内存中存放的起始地址;
- 该指定元素在原多维数组的坐标;
- 数组中元素的具体数据类型,即一个元素占用的字节。
- 实例:以行为序的2维的int数组a[n][m]中查找a[i]j。则关系有:
&a[i][j] = &a[0][0]+(i*m+j)*sizof(int);
3.矩阵的压缩存储
- 常见特殊矩阵,主要分为以下两类:
- 含有大量相同数据元素的矩阵,比如对称矩阵;
- 含有大量 0 元素的矩阵,比如稀疏矩阵、上(下)三角矩阵。
- 数据结构的压缩存储思想是:矩阵中的相同数据元素(包括元素0)只存储一个。
3.1 对称矩阵
- 定义:矩阵中,数据元素沿主对角线对应相等。
- 主对角线:对称线类似右斜杠\;
- 副对角线:对称线类似左斜杠/。
- 根据对称矩阵的特点,可以发现上三角和下三角的元素是一样的。我们可以用一个一维数组来存储上三角或者下三角。
- 具体实现步骤:
- 假设矩阵是a[i][j],压缩存储成b[k],则i,j,k符号如下关系:(其中矩阵中的行列下标均是从1开始,即i,j从1开始计数)
- (1)存储下三角
- k = i * (i - 1) / 2 + j - 1
- (2)存储上三角
- k = j * (j - 1) / 2 + i - 1
- 两种存储方式,最终的结果是一致,这样才方便从压缩后的数组去找数组。
- 代码实现:
#include<stdio.h>
void main()
{
int a[3][3] =
{
{1,2,3},
{2,4,5},
{3,5,6}
};
int b[10];
int c[10];
int k,m;
for (int i = 1; i <= 3; i++)
{
for (int j = 1; j <= i; j++)
{
k = i * (i - 1) / 2 + j - 1;
b[k] = a[i - 1][j - 1];
printf("下三角元素依次是:%d\n", b[k]);
}
}
printf("\n");
for (int j = 1; j <= 3; j++)
{
for (int i = 1; i <= j; i++)
{
m = j * (j - 1) / 2 + i - 1;
c[m] = a[i - 1][j - 1];
printf("上三角的元素依次是:%d\n", c[m]);
}
}
}
3.2 上(下)三角矩阵
- 主对角线下的数据元素全部相同的矩阵为上三角矩阵,主对角线上元素全部相同的矩阵为下三角矩阵。
- 压缩存储的方法和对称矩阵一样。
3.3 稀疏矩阵
- 定义:矩阵中分布有大量的相同的元素,比如 0,即非0元素非常少,这类矩阵叫稀疏矩阵。
- 压缩存储稀疏矩阵的思想是:
- 只存储矩阵中的非 0 元素。即稀疏矩阵非0元素的存储需同时存储该元素所在矩阵中的行标和列标。
- 矩阵总行数和列数。
- 压缩存储方法:
3.3.1 三元组顺序表
- 三元组:由 3 部分数据组成的集合,组中数据分别表示(行标,列标,元素值)。
#include<stdio.h>
typedef struct
{
int i, j;
int data;
}triple;
typedef struct
{
triple data[20];
int n, m, num;
}T_Matrix;
void DisPlay(T_Matrix M)
{
int i, j, k;
int Temp;
int flag;
for (i = 1; i <= M.n; i++)
{
for (j = 1; j <= M.m; j++)
{
flag = 0;
for (k = 0; k < M.num; k++)
{
if ((i == M.data[k].i) && (j == M.data[k].j))
{
Temp = M.data[k].data;
flag = 1;
break;
}
}
if (0 == flag)
{
Temp = 0;
}
printf("%d\t", Temp);
}
printf("\n");
}
}
void main()
{
T_Matrix M;
M.n = 3;
M.m = 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 = 2;
M.data[1].data = 3;
M.data[2].i = 3;
M.data[2].j = 3;
M.data[2].data = 5;
DisPlay(M);
}
- 该方法缺点:在访问矩阵中指定的某元素需要遍历整个压缩数组,因此效率比较低。为了提高效率,在压缩存储时,多用一个数组记录每行非0元素在新的压缩一未数组的索引位置,该方法即是行逻辑链接的顺序表。
3.3.2 行逻辑链接的顺序表
- 实现思想:
- 将矩阵M中非0元素的三元组(行标、列标和元素值)存储在一个维数组b[k]中;
- 再用另一个数组c[n](n <= k)记录矩阵中每行第一个非 0 元素在一维数组b[k]中的位置。
- 实例:
#include<stdio.h>
#define SIZE 6
typedef struct
{
int data;
int i, j;
}triple;
typedef struct
{
triple arr[SIZE];
int loc[4];
int m, n, num;
}T_Matrix;
void DisPlay(T_Matrix M)
{
for (int i = 1; i <= M.m; i++)
{
for (int j = 1; j <= M.n; j++)
{
int value = 0;
if (i + 1 <= M.m)
{
for (int k = M.loc[i]; k < M.loc[i+1]; k++)
{
if (i == M.arr[k].i && j == M.arr[k].j)
{
printf("%d ", M.arr[k].data);
value = 1;
break;
}
}
if (value == 0)
{
printf("0 ");
}
}
else
{
for (int k = M.loc[i]; k <= M.num; k++)
{
if (i == M.arr[k].i && j == M.arr[k].j)
{
printf("%d ", M.arr[k].data);
value = 1;
break;
}
}
if (value == 0)
{
printf("0 ");
}
}
}
printf("\n");
}
}
void main()
{
T_Matrix M;
M.m = 3;
M.n = 4;
M.num = 5;
M.loc[1] = 1;
M.loc[2] = 3;
M.loc[3] = 4;
M.arr[1].data = 1;
M.arr[1].i = 1;
M.arr[1].j = 1;
M.arr[2].data = 6;
M.arr[2].i = 1;
M.arr[2].j = 4;
M.arr[3].data = 3;
M.arr[3].i = 2;
M.arr[3].j = 2;
M.arr[4].data = 5;
M.arr[4].i = 3;
M.arr[4].j = 3;
M.arr[5].data = 8;
M.arr[5].i = 3;
M.arr[5].j = 4;
DisPlay(M);
}
- 前两种方法,本质上都是通过数组来存储稀疏矩阵,数组的缺点就是不方便插入或者删除元素。因此可以考虑采用链表的方式,即十字链表法。
3.3.3 十字链表法
- 实现思想:链表+数组
*元素用三元组表示,将每行每列的非0元素各存到一个链表中,然后将行链表的表头存在一个数组中,将列链表的表头存在另一个数组中。
3.4 矩阵的转置
- 定义:将矩阵中所有元素的行标和列标进行互换,得到新的矩阵。示意图如下:
- 实现步骤:
-
- 将矩阵的行数和列数互换;
-
- 将原矩阵元素的行列交换;
-
- 对原矩阵以列为序,重新排列三元组表中存储各三元组的先后顺序。
#include<stdio.h>
typedef struct
{
int data;
int i, j;
}triple;
typedef struct
{
triple arr[5];
int m, n, num;
}T_Mtriax;
T_Mtriax RePlaceMtriax(T_Mtriax M, T_Mtriax T) {
T.m = M.n;
T.n = M.m;
T.num = M.num;
if (T.num)
{
int q = 1;
for (int col = 1; col <= M.m; col++)
{
for (int p = 1; p <= M.num; p++)
{
if (M.arr[p].j == col)
{
T.arr[q].i = M.arr[p].j;
T.arr[q].j = M.arr[p].i;
T.arr[q].data = M.arr[p].data;
q++;
}
}
}
}
return T;
}
void main()
{
T_Mtriax M;
T_Mtriax NewM;
M.n = 3;
M.m = 2;
M.num = 4;
M.arr[1].data = 1;
M.arr[1].i = 1;
M.arr[1].j = 2;
M.arr[2].data = 3;
M.arr[2].i = 2;
M.arr[2].j = 2;
M.arr[3].data = 6;
M.arr[3].i = 3;
M.arr[3].j = 1;
M.arr[4].data = 5;
M.arr[4].i = 3;
M.arr[4].j = 2;
for (int i = 1; i <= M.num; i++)
{
NewM.arr[i].data = 0;
NewM.arr[i].i = 0;
NewM.arr[i].j = 0;
}
NewM = RePlaceMtriax(M, NewM);
for (int i = 1; i <= NewM.num; i++)
{
printf("(%d,%d,%d)\n", NewM.arr[i].i, NewM.arr[i].j, NewM.arr[i].data);
}
}