【数据结构】数组与广义表

数组与广义表

一、数组

数组的顺序存储

一维数组:
A = a 1 , a 2 , . . . a i , . . . , a n A = a_1, a_2, ... a_i, ..., a_n A=a1,a2,...ai,...,an
L o c ( A [ i ] ) = L o c ( A [ 1 ] ) + ( i − 1 ) × s i z e Loc(A[i]) = Loc(A[1]) + (i - 1)\times size Loc(A[i])=Loc(A[1])+(i1)×size
二维数组:
以行为主,下标从 1 开始,首元素 a 11 的地址 L o c ( A [ 1 ] [ 1 ] ) , a i j 为第 i 行,第 j 列的元素 以行为主,下标从1开始,首元素a_{11}的地址Loc(A[1][1]),a_{ij} 为第i行,第j列的元素 以行为主,下标从1开始,首元素a11的地址Loc(A[1][1]),aij为第i行,第j列的元素
L o c ( A [ i ] [ j ] ) = L o c ( A [ 1 ] [ 1 ] ) + ( n × ( i − 1 ) + j − 1 ) × s i z e Loc(A[i][j]) = Loc(A[1][1]) + (n\times (i - 1) + j - 1)\times size Loc(A[i][j])=Loc(A[1][1])+(n×(i1)+j1)×size
三维数组:
以行为主序, r 个 m × n 数组 以行为主序,r个m\times n数组 以行为主序,rm×n数组
L o c ( A [ i ] [ j ] [ k ] ) = L o c ( A [ 1 ] [ 1 ] [ 1 ] ) + ( ( i − 1 ) × m × n + ( j − 1 ) × n + ( k − 1 ) ) × s i z e Loc(A[i][j][k]) = Loc(A[1][1][1]) + ((i - 1)\times m\times n + (j - 1)\times n + (k - 1))\times size Loc(A[i][j][k])=Loc(A[1][1][1])+((i1)×m×n+(j1)×n+(k1))×size
n维数组:
对于 n 维数组 A [ c 1 . . d 1 , c 2 . . d 2 , c n . . d n ] , a j 1 j 2 . . . j n 的地址为: L o c ( j 1 , j 2 , . . . , j n ) = L o c ( c 1 , c 2 , . . . , c n ) + ∑ i = 1 n α i × ( j i − c i ) 其中 α i = s i z e × ∏ k = i + 1 n ( d k − c k + 1 ) , 1 ≤ i ≤ n 对于n维数组A[c_1..d_1, c_2..d_2, c_n..d_n],a_{j_1j_2...j_n}的地址为:\\ Loc(j_1, j_2, ..., j_n) = Loc(c_1, c_2, ..., c_n) + \sum_{i=1}^{n}\alpha _ i\times (j_i-c_i)\\ 其中\alpha _ i = size\times\prod_{k=i+1}^{n}(d_k-c_k+1),1\le i\le n 对于n维数组A[c1..d1,c2..d2,cn..dn],aj1j2...jn的地址为:Loc(j1,j2,...,jn)=Loc(c1,c2,...,cn)+i=1nαi×(jici)其中αi=size×k=i+1n(dkck+1),1in

二、特殊矩阵的压缩存储

1.有特殊规律的矩阵,寻找规律公式,进行压缩存储。
2.稀疏矩阵(非0元素很少),只存储非0元素。

1.规律分布的特殊矩阵

(1)三角矩阵

( a 11 0 a 21 a 22 0 a 31 a 32 a 33 0 ⋮ ⋮ ⋮ ⋮ 0 a n 1 a n 2 a n 3 ⋮ a n n ) \begin{pmatrix} a_{11}&0\\ a_{21}&a_{22}&0\\ a_{31}&a_{32}&a_{33}&0\\ \vdots&\vdots&\vdots&\vdots&0\\ a_{n1}&a_{n2}&a_{n3}&\vdots&a_{nn}\\ \end{pmatrix} a11a21a31an10a22a32an20a33an300ann

L o c [ i , j ] = L o c [ 1 , 1 ] + ( i × ( i − 1 ) 2 + j − 1 ) Loc[i,j]=Loc[1,1]+(\frac{i\times(i-1)}{2}+j-1) Loc[i,j]=Loc[1,1]+(2i×(i1)+j1)
将 A 压缩到 B 中 A [ i ] [ j ] = { 0 , i < j B [ i ( i − 1 ) 2 + j ] , i ≥ j 将A压缩到B中 A[i][j]=\left\{\begin{matrix} 0,i<j\\ B[\frac{i(i-1)}{2}+j],i\ge j \end{matrix}\right. A压缩到BA[i][j]={0,i<jB[2i(i1)+j],ij

(2)带状矩阵

A = ( a 11 a 12 0 ⋯ 0 a 21 a 22 a 23 ⋱ ⋮ 0 a 32 a 33 ⋱ 0 ⋮ ⋱ ⋱ ⋱ a n − 1 , n 0 ⋯ 0 a n , n − 1 a n n ) n × n A = \begin{pmatrix} a_{11} & a_{12} & 0 & \cdots & 0 \\ a_{21} & a_{22} & a_{23} & \ddots & \vdots \\ 0 & a_{32} & a_{33} & \ddots & 0 \\ \vdots & \ddots & \ddots & \ddots & a_{n-1,n} \\ 0 & \cdots & 0 & a_{n,n-1} & a_{nn} \\ \end{pmatrix}_{n\times n} A= a11a2100a12a22a320a23a330an,n100an1,nann n×n
{ 当 i = 1 时, j = 1 , 2 当 1 < i < n 时, j = i − 1 , i , i + 1 当 i = n 时, j = n − 1 , n \left\{\begin{matrix} 当i=1时,j=1,2\\ 当1<i<n时,j=i-1,i,i+1\\ 当i=n时,j=n-1,n\\ \end{matrix}\right. i=1时,j=121<i<n时,j=i1,i,i+1i=n时,j=n1,n
i.存储原则:将带状区域上的非零元素按行序存储。

ii…确定存储空间大小:假设每个非零元素占用一个存储单元,那么所需的一维数组空间大小为 3n-2,其中 n 是矩阵的行数(因为除了第一行和最后一行只有两个非零元素外,其余各行均有三个非零元素)。

iii.确定非零元素地址:非零元素在一维数组中的地址可以通过计算得出。例如,对于三对角带状矩阵中的元素 a_ij,其在一维数组中的位置 Loc(i,j) :
L o c ( A [ i ] [ j ] ) = L o c ( A [ 1 ] [ 1 ] ) + ( ( 3 ( i − 1 ) − 1 ) + ( j − i + 1 ) ) × s i z e Loc(A[i][j])=Loc(A[1][1])+((3(i-1)-1)+(j-i+1))\times size Loc(A[i][j])=Loc(A[1][1])+((3(i1)1)+(ji+1))×size

2. 稀疏矩阵

非0元素占比低于30%

(1)三元组表示法

行号row,列号col,非0元素值e

1.三元组的类型定义
#define MAXSIZE 1000
typedef struct
{
    int row,col;
    ElementType e;
}Triple;

typedef struct
{
    Triplle data[MAXSIZE+1];
    int m, n, len;
}TSMatrix;//Triple Sparse Matrix
2.矩阵转置的经典算法
void TransMatrix(ElementType source[m][n], ElementType dest[n][m])
{
    int i, j;
    for(i = 0; i < m; i ++)
       for(j = 0; j < n; j ++)
          dest[j][i] = source[i][j];//行列互换
}//O(m * n)
3.用三元组实现稀疏矩阵的转置

A转置为B,按col递增的顺序扫描A中元素,依次转置

/*列序递增转置法*/
void TransTSMatrix(TSMatrix A, TSMatrix * B)
{
    int i, j, k;
    B->m = A.n; B->n = A.m; B->len = A.len;
    if(B->len > 0)
    {
        j = 1;//j为辅助计数器,记录转置后的三元组在B中的总下标值
        for(k = 1; k <= A.n; k ++)
           for(i = 1; i <= A.len; i ++)
              if(A.data[i].col == k)
              {
                B->data[j].row = A.data[i].col;
                B->data[j].col = A.data[i].row;
                B->data[j].e = A.data[i].e;
                j ++;
              }
    }
}//O(A.n * A.len)
4.一次定位快速算法

空间换时间,多个单重循环记录下num,position,以避免多重循环

void FastTransposeTSMatrix(TSMatrix A, TSMatrix * B)
{
    int col, t, p, q;
    int num[MAXSIZE];//num[j]记录转置前A中第j列非零元素的个数
    int position[MAXSIZE];//position[j]记录转置前A中第j列第一个非零元素在B.data中的下标,position[col] = position[col-1] + num[col-1]
    B->m = A.n; B->n = A.m; B->len = A.len;
    if(B->len)
    {
        for(col = 1; col <= A.n; col ++)
            num[col] = 0;
        for(t = 1; t <= A.len; t ++)//统计A中每列非零元素的个数
            num[A.data[t].col] ++;
        position[1] = 1;
        for(col = 2; col <= A.n; col ++)//计算每列第一个非零元素在B.data中的下标
            position[col] =position[col - 1] + num[col - 1];
        for(p =1; p <= A.len; p++)//转置
        {
            col = A.data[p].col;
            q = position[col];
            B->data[q].row = A.data[p].col;
            B->data[q].col = A.data[p].row;
            B->data[q].e = A.data[p].e;
            position[col] ++;//下一个非零元素的位置
        }
    }
}//O(A.len + A.n)
(2)稀疏矩阵的链式存储结构:十字链表

为了避免大量移动元素,十字链表能够灵活地插入因运算而产生的新的非0元素,删除因运算而产生的新的非0元素,实现矩阵的各种运算。

每个结点除了row,col,e,加入right,down链域。同一行的非0元素链接成一个单链表,同一列的非0元素链接成一个单链表,各个结点好像一个个十字交叉路口。

1.十字链表的类型定义
typedef struct OLNode
{
    int row, col;
    ElementType e;
    struct OLNode * right, * down;
}OLNode, * OLink;//Orthogonal

typedef struct
{
    OLink * row_head[m + 1], * col_head[n +1];
    int m, n, len;
}CrossList;
2.十字链表的创建
void CreateCrossList(CrossList * M)
{
    scanf(&m, &n, &t);//输入矩阵的行数、列数和非零元素个数(伪代码)
    M->m = m; M->n = n; M->len = t;
    if(!(M->row_head = (OLink *)malloc((m+1)*sizeof(OLink)))) exit(OVERFLOW);
    if(!(M->col_head = (OLink *)malloc((n+1)*sizeof(OLink)))) exit(OVERFLOW);
    M->row_head[] = M->col_head[] = NULL;//初始化行、列头指针
    for(scanf(&i, &j, &e); i != 0; scanf(&i, &j, &e))//输入非零元素的行、列和值
    {
        if(!(p = (OLNode *)malloc(sizeof(OLNode)))) exit(OVERFLOW);//生成新的结点
        p->row = i; p->col = j; p->e =e;
        if(M->row_head[i] == NULL)
            M->row_head[i] == p;
        else
        {
            q = M->row_head[i];
            while(q->right != NULL&& q->right->col < j)//往右移动,找到插入位置
                q = q->right;
            p->right = q->right;
            q->right = p;//按列递增插入
        }
        if(M->col_head[j] == NULL) 
            M->col_head[j] = p;
        else
        {
            q = M->col_head[j];
            while(q->down != NULL && q->down->row < i)//往下移动,找到插入位置
                q = q->down;
            p->down = q->down;
            q->down = p;//按行递增插入
        }

    }
}//O(t*s),t为非零元素个数,s为max(m,n)

三、广义表

广义表(Generalized List)是一种非线性的数据结构,它可以包含原子(基本的数据元素)和子表(其他广义表)。广义表是线性表的推广,可以用来表示具有层次结构的数据。

1.基本概念

原子:广义表中的基本数据元素,不可再分。
子表:广义表中的元素也可以是另一个广义表。
表头:广义表的第一个元素,可以是原子或子表。
表尾:除了表头之外的其余部分,总是另一个广义表。

下面给出几个广义表的例子:
D = ( ) 空表,其长度为零
A = ( a , ( b , c ) )
B = ( A , A , D)
C = ( a , C ) 按递归定义

以A为例:
head(A) = a
tail(A) = ( ( b , c ) ) 注意表尾也是一个广义表

2.广义表的存储结构

(1)广义表的头尾链表存储结构

广义表中的每个元素用一个节点来表示,表中有两类节点:i.原子结点;ii.子表结点。用标志域tag来区分两种节点。

typedef enum {ATOM,LIST} ElemTag;
typedef struct GLNode
{
    ElemTag tag;//标志域,区分原子结点和表结点
    union
    {
        AtomType atom;//原子结点的值域
        struct {struct GLNdoe * hp, * tp;} htp;//表结点的指针域htp,hp指向表头,tp指向表尾
    } atom_htp;//原子结点的值域atom和表结点的指针域htp的联合体域
} GLNode, * GList;//General List
(2)广义表的同层结点链存储结构

表结点:tag,hp,tp
原子结点:tag,atom,tp

typedef struct GLNode
{
    ElemTag tag;
    union
    {
        AtomType atom;
        struct GLNode * hp;//表头指针域
    }atom_hp;//原子结点的值域和表结点的指针域的联合体域
    struct GLNode * tp;
}GLNode, * GList;

3.广义表的操作实现(以头尾链表存储结构为例)

(1)求广义表的表头
GList Head(GList L)
{//求广义表L的表头,并返回指向表头的指针
    if(L == NULL) return NULL;
    if(L->tag == ATOM) exit(0); //原子不是表,没有指向它的指针
    else return (L->atom_htp.htp.hp);
}
(2)求广义表的表尾
GList Tail(GList L)
{
    if(L == NULL) return NULL;
    if(L->tag == ATOM) exit(0);
    else return (L->atom_htp.htp.tp);
}
(3)求广义表的长度
int Length(GList L)
{
    int k = 0;
    GLNode * s;
    if(L == NULL) return 0;
    if(L->tag == ATOM) exit(0);
    s = L;
    while(s != NULL)
    {
        k ++;
        s = s->atom_htp.htp.tp;
    }
    return k;
}
(4)求广义表的深度
int Depth(GList L)
{
    int d, max = 0;
    GLNode * s;
    if(L == NULL) return 1;//空表深度为1
    if(L->tag == ATOM) return 0;//原子深度为0
    s = L;
    while(s != NULL)
    {
        d = Depth(s->aotm_htp.htp.hp);//递归求子表的深度
        if(d > max) max = d;
        s = s->atom_htp.htp.tp;
    }
    return max + 1;//表的深度为其最深子表的深度加1
}
(5)统计广义表中的原子数目
int CountAtom(GList L)
{
    int n1, n2;
    if(L == NULL) return 0;//空表无原子
    if(L->tag == ATOM) return 1;//指向单个原子
    n1 = CountAtom(L->atom_htp.htp.hp);
    n2 = CountAtom(L->atom_htp.htp.tp);//神奇的递归~
    return n1 + n2;
}
(6)复制广义表
int CopyGList(GList S, GList * T)
{
    if(S == NULL)
    {
        * T = NULL;
        return OK;
    }
    * T = (GLNode *)malloc(sizeof(GLNode));
    if(* T == NULL) return ERROR;
    (* T)->tag = S->tag;
    if(S->tag == ATOM)  (* T)->atom = S->atom;//S为原子结点,复制到T
    else
    {
        CopyGList(S->atom_htp.htp.hp,&((* T)->atom_htp.htp.hp));
        CopyGList(S->atom_htp.htp.tp,&((* T)->atom_htp.htp.tp));//还是递归,广义表天然的属性适合递归
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值