目录
一、数组的类型定义
数组是按一定格式排列起来的具有相同类型的数据元素的集合。
一维数组:由非结构的简单元素构成的线性表。因此其具有线性结构,并且是定长的线性表。
二维数组:在一维数组的基础上,每个元素都是线性结构的表。
这样的定义我们可以把它推广到 n 维,即n 维数组:在一维数组的基础上,每个元素都是 n - 1 维数组的表。
虽然除了一维数组之外,其它类型很明显都是非线性的,但我们仍然可以将它们看成是定长线性表进行操作。
注意:数组一旦被定义,它的维数和维界就不再改变。因此数组不进行插入和删除操作。
由于数组的结构是固定的,因此数组采用顺序存储方式。
数组的基本操作:
InitArray(Array &A, int dim, ...) // 构造数组A,"..."为各维的长度
DestroyArray(Array &A) // 销毁数组A
Value(Array A, ElemType &e, ...) // 求给定下标的元素的值,用e返回,"..."为各维下标
Locate(Array A, va_list ap, int &off) // 若ap指示的各下标值合法,则求出该元素在A中相对地址off
Assign(Array &A, ElemType e, ...) // 将给定下标的元素的值改为e,"..."为各维下标
二、数组的顺序表示
数组的顺序表示又有两种存储方式,一种是以行序为主序,另一种是以列序为主序。在C语言中,用的都是以行序为主序。因此下文中均以以行序为主序的存储方式为例。
以二维数组为例,它的以行序为主序的存储方式如图所示:
其中,
我们假设每个数据元素占 L 个存储单元,则二维数组 A 中任一元素的存储位置可以由下式所确定:
,其中
是
的存储地址,
是
的存储位置,
为每行元素的个数。
可以以此推广得到 n 维数组的元素存储位置的计算公式:,其中
为数组的各维长度
上式可以简化成
,其中
数组的代码实现:
1.存储结构:
#include <stdarg.h> // 标准头文件,提供va_start、va_arg和va_end,用于存取变长参数表
#define MAX_ARRAY_DIM 8 // 数组的维数最大值
typedef struct{
ElemType *base; // 数组元素基址,由InitArray分配
int dim; // 数组维数
int *bounds; // 数组维界基址,由InitArray分配
int *constants; // 数组映像函数常量基址,由InitArray分配
}Array;
2.初始化操作:
Status InitArray(Array &A, int dim, ...){
// 构造数组A,"..."为各维的长度
if(dim < 1 || dim > MAX_ARRAY_DIM) return ERROR;
A.dim = dim;
A.bounds = (int *)malloc(dim * sizeof(int));
if(!A.bounds) exit(OVERFLOW);
// 若各维长度合法,则存入A.bounds,并求出A的元素总数elemtotal
elemtotal = 1;
va_start(ap, dim); // ap为va_list类型,是存放变长参数表信息的数组
for(i = 0; i < dim; ++i){
A.bounds[i] = va_arg(ap, int);
if(A.bounds[i] < 0) return UNDERFLOW;
elemtotal *= A.bounds[i];
}
va_end(ap);
A.base = (ElemType *)malloc(elemtotal * sizeof(ElemType));
if(!base) exit(OVERFLOW);
// 求映像函数的常数ci,并存入A.constants[i - 1],i = 1, ... , dim
A.constants = (int *)malloc(dim * sizeof(int));
if(!A.constants) exit(OVERFLOW);
A.constants[dim - 1] = 1; // L = 1,指针的增减以元素的大小为单位
for(int i = dim - 2; i >= 0; --i){
A.constants[i] = A.bounds[i + 1] * A.constants[i + 1];
}
return OK;
}
3.销毁操作:
Status DestroyArray(Array &A){
// 销毁数组A
if(!base) return ERROR;
free(A.base);
A.base = NULL;
if(!bounds) return ERROR;
free(A.bounds);
A.bounds = NULL;
if(!constants) return ERROR;
free(A.constants);
A.constants = NULL;
}
4.求值操作:
Status Value(Array A, ElemType &e, ...){
// 求给定下标的元素的值,用e返回,"..."为各维下标
va_start(ap, e);
if((result = Locate(A, ap, off)) <= 0) return result;
e = *(A.base + off);
return OK;
}
5.求地址操作:
Status Locate(Array A, va_list ap, int &off){
// 若ap指示的各下标值合法,则求出该元素在A中相对地址off
off = 0;
for(i = 0; i < A.dim; ++i){
ind = va_arg(ap, int);
if(ind < 0 || ind >= A.bounds[i]) return OVERFLOW;
off += A.constants[i] * ind;
}
return OK;
}
6.改值操作:
Status assign(Array &A, ElemType e, ...){
// 将给定下标的元素的值改为e,"..."为各维下标
va_start(ap, e);
if((result = Locate(A, ap, off)) <= 0) return result;
*(A.base + off) = e;
return OK;
}
三、特殊矩阵的压缩存储
1.对称矩阵
1)特点:在n * n的矩阵中,满足
2)存储方法:只存储下(上)三角(包括主对角线)的数据元素,共占据个元素空间
2.三角矩阵
1)特点:对角线以下(上)的数据元素(不包括对角线)全都为常数c
(
)
2)存储方法:重复元素c共享一个存储空间,共占据个元素空间
上三角数据元素存储位置:
下三角数据元素存储位置:
3.对角矩阵
1)特点:在n * n的方阵中,所有非零元素都集中在以主对角线为中心的带状区域中,区域外的值全为0
2)存储方式:以对角线的方式存储,往上为负,往下为正
4.稀疏矩阵
假设在m * n的矩阵中,有 t 个元素不为零。令,称
为矩阵的稀疏因子。
通常时称为稀疏矩阵。
按照压缩存储的概念,只存储稀疏矩阵的非零元。
1.三元组顺序表
用三元组惟一确定了矩阵 A 的一个非零元,因此稀疏矩阵可以由表示非零元的三元组及其行列数惟一确定。
存储表示:
#define MAXSIZE // 假设非零元个数的最大值为12500
typedef struct{
int i, j; // 该非零元的行下标和列下标
ElemType e;
}Triple;
typedef struct{
Triple data[MAXSIZE + 1]; // 非零元三元组表,data[0]未用
int mu, nu, tu; // 矩阵的行数、列数和非零元个数
}TSMatrix;
2.十字链表
当矩阵的非零元个数和位置在操作过程中变化较大时,就不宜采用顺序存储结构来表示三元组的线性表。对于这类型的矩阵,可以采用链式存储结构来表示。
每个非零元用一个含5个域的结点表示,其中 i、j和 e 这三个域分别表示该非零元所在的行、列和非零元的值,right域指向同一行的下一个非零元,down域指向同一列的下一个非零元。这样整个矩阵就构成了一个十字交叉的链表,这就是十字链表。
存储表示:
typedef struct OLNode{
int i, j; // 该非零元的行和列下标
ElemType e;
struct OLNode *right, *down; // 该非零元所在行表和列表的后继链域
}OLNode, *OLink;
typedef struct{
OLink *rhead, *chead; // 行和列链表头指针向量基址由CreateSMatrix分配
int mu, nu, tu; // 稀疏矩阵的行数、列数和非零元个数
}CrossList;
建立十字链表:
Status CreateSMatrix_OL(CrossList &M){
// 创建稀疏矩阵M。采用十字链表存储表示
if(M) free(M);
scanf(&m, &n, &t);
M.mu := m; M.nu := n; M.tu := t;
if(!(M.rhead = (OLink *)malloc((m + 1) * sizeof(OLink)))) exit(OVERFLOW);
if(!(M.rhead = (OLink *)malloc((n + 1) * sizeof(OLink)))) exit(OVERFLOW);
M.rhead[] = M.chead[] = NULL;
for(scanf(&i, &j, &e); i != 0; scanf(&i, &j, &e)){
if(!(p = (OLNode *)malloc(sizeof(OLNode)))) exit(OVERFLOW);
p->i = i; p->j = j; p->e = e;
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;
}
}
return OK;
} // CreateSMatrix_OL