数据结构笔记——数组

目录

一、数组的类型定义

二、数组的顺序表示

三、特殊矩阵的压缩存储

1.对称矩阵

2.三角矩阵

3.对角矩阵

4.稀疏矩阵


一、数组的类型定义

数组是按一定格式排列起来的具有相同类型的数据元素的集合。

一维数组:由非结构的简单元素构成的线性表。因此其具有线性结构,并且是定长的线性表。

二维数组:在一维数组的基础上,每个元素都是线性结构的表。

这样的定义我们可以把它推广到 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语言中,用的都是以行序为主序。因此下文中均以以行序为主序的存储方式为例。

以二维数组为例,它的以行序为主序的存储方式如图所示:

其中A_i = (a_{i0}, a_{i1}, ... , a_{i, m - 1})A = (A_0, A_1, ... , A_{m - 1})

我们假设每个数据元素占 L 个存储单元,则二维数组 A 中任一元素a_{00}的存储位置可以由下式所确定:LOC(i, j) = LOC(0, 0) + (b_2 * i + j) * L,其中LOC(i, j)a_{ij}的存储地址,LOC(0, 0)a_{00}的存储位置,b_2为每行元素的个数。

可以以此推广得到 n 维数组的元素存储位置的计算公式:LOC(j_1, j_2, ... , j_n) = LOC(0, 0, ... , 0) + (\sum_{i = 1}^{n - 1}j_i\prod_{k = i + 1}^{n}b_k + j_n) * L,其中b_k为数组的各维长度

上式可以简化成

LOC(j_1, j_2, ... , j_n) = LOC(0, 0, ... , 0) + \sum_{i = 1}^{n}c_ij_i,其中c_n = L, \; \; c_{i - 1} = b_i * c_i \; \; (1 < i \leqslant 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的矩阵中,满足a_{ij} = a_{ji} \; (1 \leqslant i, j \leqslant n)

2)存储方法:只存储下(上)三角(包括主对角线)的数据元素,共占据\frac{n(n + 1)}{2}个元素空间

2.三角矩阵

1)特点:对角线以下(上)的数据元素(不包括对角线)全都为常数c

2)存储方法:重复元素c共享一个存储空间,共占据\frac{n(n + 1)}{2} + 1个元素空间

上三角数据元素存储位置:k = \begin{cases} (i - 1) * (2n - i + 2) / 2 + j - i + 1, & i \leqslant j \\ n(n + 1) / 2 + 1, & i > j \end{cases}

下三角数据元素存储位置:k = \begin{cases} i * (i - 1) / 2 + j, & i \geqslant j \\ n(n + 1) / 2 + 1, & i < j \end{cases}

3.对角矩阵

1)特点:在n * n的方阵中,所有非零元素都集中在以主对角线为中心的带状区域中,区域外的值全为0

2)存储方式:以对角线的方式存储,往上为负,往下为正

4.稀疏矩阵

假设在m * n的矩阵中,有 t 个元素不为零。令\delta = \frac{t}{m * n},称\delta为矩阵的稀疏因子

通常\delta \leqslant 0.05时称为稀疏矩阵

按照压缩存储的概念,只存储稀疏矩阵的非零元。

1.三元组顺序表

用三元组(i, j, a_{ij})惟一确定了矩阵 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
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
408数据结构笔记pdf整理是将408数据结构的课堂笔记整理成电子PDF格式的文件。整理这份笔记有以下几个目的和优点。 首先,整理这份笔记可以使学生更好地复习和回顾课堂内容。408数据结构是一门重要的计算机科学课程,课堂内容涉及许多复杂的数据结构和算法,学生在学习过程中可能会遇到各种各样的问题。通过整理这份笔记,学生可以更方便地查找和复习自己所学的内容,对于深化对于数据结构的理解和记忆起到了很大的帮助作用。 其次,整理这份笔记也可以作为学生之间的相互学习和交流的工具。由于408数据结构是一门独立的课程,学生之间的课堂笔记内容可能有所不同。通过整理这份笔记,可以将不同学生的观点和思考整合在一起,形成全面和多视角的学习资料。这样一方面能够帮助学生更好地理解和记忆课堂内容,另一方面也能促进学生之间的交流和合作,提高学习效率。 最后,整理这份笔记还可以方便老师的教学工作。作为授课教师,教师通常需要准备教案和课堂讲义。通过整理这份笔记,教师可以更轻松地准备教学材料,节省时间和精力。 综上所述,408数据结构笔记pdf整理是一项非常有益的工作。它可以帮助学生回顾和复习课堂内容,促进学生之间的合作和交流,也可以方便教师的教学工作。整理这份笔记是提高学生学习效率和质量的一个重要手段。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值