多维数组和广义表(C++)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Davincdada/article/details/80021965

线性表、栈、队列和串都是线性表的数据结构,他们的逻辑结构特征是:每个数据元素之多有一个直接前趋和直接后继。对于多维数组和广义表是一种复杂的非线性结构,它们的逻辑特征是:一个元素可能有多个直接前趋和多个直接后继。

一、数组概念

  1. 一维数组
    可以看成是一个线性表或一个向量,在计算机中是一段连续的存储单元,适用于随机查找。

  2. 二维数组
    二维数组
    由图可以知道,每个元素最多由两个直接前驱和两个直接后继(边界除外),故是一种典型的非线性结构。

多维数组最多有多个直接前驱和直接后继,也是非线性结构。

  1. 多维数组在计算机内的存储
    由于内存结构是一维的,故必须按照某种次序将数组元素排成一个线性序列。

二、多维数组的存储结构

数组是先定义后使用,且为静态分配存储单元,所以采用顺序存储。有行优先顺序存储、列优先顺序存储。

对于数组 A(nxm)
1. 行优先 顺序存储

(a11->a12->a13->...a1n)->(a21->a22->a23->...a2n)->...(am1->...->amn)

最左边下标变化慢,做for外层循环;右边变化快,做for内层循环。

aij地址:前面有i行(i×n),改行前面有j个元素

LOC(aij) = LOC(a00) + (i*n+j)*d  // d为每个元素所占空间大小
// 三维数组
LOC(aijk) = LOC(a000) + (i*n*p+j*p+k)*d

2 . 列优先 顺序存储

(a11->a21->a31->..an1)->(a12->a22->a32->...an2)->(a1m->a2m->...->anm)

最又边下标变化慢,做for外层循环;左边变化快,做for内层循环。

aij地址:前面有j列(j×m),改行前面有i个元素

LOC(aij) = LOC(a00) + (j*m+i)*d  // d为每个元素所占空间大小
// 三维数组
LOC(aijk) = LOC(a000) + (j*m*p+i*p+k)*d

三、 特殊矩阵的压缩存储

所谓压缩,即当矩阵元素分布呈某种规律时,可以多个元素共用一个存储单元,以实现节约存储单元。简单的说,就是多个值相同的元素只分配一个存储单元,值为0的不分配空间。

可以理解为将二维数组(矩阵)压缩到一个存储单元数目少的一维数组中。而为了方便存储后直接找到某元素,还必须给出压缩前下标和压缩后下标之间的变换公式

1. 对称矩阵

aij = aji  // A(nxn)

这里写图片描述

以矩阵的对角线为分隔,分为上三角和下三角 。压缩存储对称矩阵存储时只需要存储上三角/下三角的数据,所以最多存储n(n+1)/2个数据(相当于1+2+…+n,即等差数列求和)。

对称矩阵和压缩存储的对应关系:
(1)下三角存储

// 二维数组为压缩前的元素,一维数组放压缩后的元素
    int s[k], a[i][j];
    if (i >= j) 
        a[i][j] = s[ i*(i+1)/2 + j ]; 
    else(i < j)
        a[i][j] = s[ j*(j+1)/2 + i ];

(2)上三角存储

    int s[k], a[i][j];
    if (i <= j) 
        a[i][j] = s[ i*n-i(i-1)/2 + j-i ]; 
    else(i > j)
        a[i][j] = s[ j*n-j(j-1)/2 + i-j ];

2、 三角矩阵
(1)下三角
它的主对角线以上(不包括主对角线)的元素均为常数0(或相同的常数)
其压缩存储与对称矩阵的类似,但必须多一个存储单元存放上三角部分元素,使用的存储单元为n(n+1)/2 + 1

假设仍按行优先存放,有

if (i >= j)
    a[i][j] = s[ i(i+1)/2 + j ];
else (i < j)
    a[i][j] = s[ n(n+1)/2 ];

(2)上三角
它的主对角线以下(不包括主对角线)的元素均为常数0(或相同的常数)

if (i <= j)
    a[i][j] = s[ i*n-i(i-1)/2 + j-i ];
else (i > j)
    a[i][j] = s[ n(n+1)/2 ];

3、 对角矩阵
若矩阵中所有非零元素都集中在以主对角线为中心的带状区域中,则为对角矩阵,常见的有三对角矩阵,五对角矩阵,七对角矩阵等。这里写图片描述
以三对角矩阵为例:
在一个 n*n 的三对角矩阵中,只有 n+n-1+n-1 个非零元,共需要3n-2 个存储单元,零元不占存储单元。

    int s[3n-2], a[i][j];
    if (i = j+1)
        a[i][j] = s[3i-1];
    else if (i = j)
        a[i][j] = s[3i];
    else (i = i-1)
        a[i][j] = s[3i+1];

4、稀疏矩阵

矩阵阶数大,非零元数目少,零元多,非零元的排列没有规律的矩阵。
由于没有规律,除了存放非零元的值外,还需要适当的辅助信息(行列号)才能迅速确定一个元素。有以下这些存储方法:

(1)三元数组
所谓三元:元素值、行号、列号
整个稀疏矩阵中非零元的三元组合起来称为三元组表。
这里写图片描述
数据类型描述:

#include <iostream>
using namespace stdl;

const int maxsize = 100;
class node
{
public:
    int i,j;  // 非零元行、列号
    int v;    // 非零元值
};
class sparmatrix  // 定义稀疏矩阵
{
public:
    int rows,cols;       // 行、列数 
    int terms;           // 非零元个数
    node data[maxsize];  // 三元组表 
} 

(2)带行指针的链表

把具有相同行号的非零元用一个单链表连接起来,稀疏矩阵的N行组成N个单链表,合起来称为带行指针的链表。
这里写图片描述

(3)十字链表

当系数矩阵中的非零元位置或个数经常变动时,三元组就不适合采用了,用链表作为存储结构更合适。

这里写图片描述
这里写图片描述
每个非零元既是第 i 行循环的一个节点,又是第 j 列的节点,相当于处在十字交叉路口,故称为十字链表。

class linknode
{
public:
    int i,j;  // 行、列号
    linknode *cptr, *rptr;  // 行、列指针
    union vnext   // 定义一个共同体
    {
        int v;   // 表节点使用v域,表示非零元值
        linknode *next; // 表头节点使用next指向下一个表头节点
    } k;
    ...
};

5、 广义表

广义表是n个元素的有限序列,其中的每一项可以使原子项,也可以是一个子表。一般用LS表示广义表,ls表示子表。

广义表举例:

 F = ( ); // F 为空表,长度为0
 G = (a, (b, c)); // 长度为2,第一项为原子,第二项为子表
 H = (x,y,z); // 长度为3,都是原子
 D = (B,C); // 都是子表
 E = (a,E); // 长度为2,一个原子一个子表

这里写图片描述

广义表的深度 = 括号层数

分类:
(1)线性表:元素都是原子
(2)纯表:元素都是原子或都是子表,与树对应,如图(a)(d)
(3)再入表:与图对应,允许节点共享,如 (d)
(4)递归表:如图(e) D = (a, D);

猜你喜欢

转载自blog.csdn.net/Davincdada/article/details/80021965