(六)数组和矩阵及其存储

来源:互联网 发布:在线算法 编辑:程序博客网 时间:2024/06/14 04:00
1.数组的概念和存储:
1.1.基本概念:
        1.可以将数组看成是线性表的推广。
1.2.数组的性质:
        1.数组元素固定;
        2.数组中每个元素数据类型相同;      
        3.数组中每个元素都有唯一的下标与之对应;
        4.数组是一种随机存储结构;
1.3.数组的存储结构:
        1.对于一个一维数组,每个数据占用k个存储单元,第一个数组元素a0的地址Loc(a0)确定,则一维数组中任一数组元素ai的存储地址Loc(ai)为:Loc(ai)=Loc(a0)+i*k;
        2.对于一个二维数组,也可以降至一维数组进行操作,有以主序为行的和以列为主序的两种存储方式,假设有m行n列的二维数组,则计算存储地址的方式对应如下:
        ①以行为主序,即行先变化:Loc(ai,j)=Loc((a0,0)+(i*n+j)*k;
        ②以列为主序,即列先变化:Loc(ai,j)=Loc((a0,0)+(j*m+i)*k;
        以上两式均是以行列下标为0开始,若以c1、c2分别表示行、列的下界,d1、d2分别表示行、列的上界
则有以下关系:
        ④以行为主序,即行先变化:Loc(ai,j)=Loc((ac1,c2)+[(i-c1)*(d2-c2+1)+(j-c2)]*k;
        ⑤以列为主序,即列先变化:Loc(ai,j)=Loc((ac1,c2)+[(j-c2)*(d1-c1+1)+(i-c1)]*k;
2.特殊矩阵的压缩存储:
特殊矩阵是指非零元素或零元素的分布有一定的规律的矩阵。为了节省存储空间,需要进行压缩存储,主要形式有对称矩阵、三角矩阵和对角矩阵;
2.1.对称矩阵:
        1.在一个n阶方阵中A中,若ai,j=aj,i(0<=i,j<n);则称A为n阶对称矩阵;
        2.由于对称矩阵中元素关于主对角线对称,因此存储时只需要存储矩阵中的上三角元素或者下三角元素即可,这样就可以将n*n个元素存储在n*(n+1)/2个元素的空间中了。
        3.存储时一般以行为主序进行存储,其存储的下标为0~n(n+1)/2 - 1;
        4.存储位置的计算
        对于对称矩阵A的下三角元素而言:若Ai,j前面共有i行,(行的下标为0~i-1),则:行下标为0的元素有1个,行下标为1的元素有两个,。。。,行下标为i的元素有i+1个。因此i行所有的元素为:1+2+...+(i+1)=i(i+1)/2,对应存储的下标为:i(i+1)/2 - 1;在加上Ai,j元素第i行的前j个元素,因此对称矩阵A下三角中任意元素Ai,j存储于一维数组的存储位置为:k=i(i+1)/2+j;
        ②由于上三角元素Ai,j等会下三角元素Aj,i,因此互换j和i就能得到上三角元素在一个一维数组中的存储位置:k=j(j+1)/2+i;
        ③综上所述,对称矩阵存储在一维数组时的存储位置为:k=i(i+1)/2+j,i>=j时;k=j(j+1)/2+i,i<j时;

2.2.三角矩阵:
        1.以主对角线来划分,三角矩阵分为上三角矩阵和下三角矩阵。上三角矩阵是指矩阵的下三角(不包括主对角线)中的元素全为一个常数或0;下三角矩阵反之;
        2.这样一来,使用一维数组进行存储时,只需将非零元素进行存储,再加上一个常数即可。即将n*n个元素存储在n*(n+1)/2+1个单元中。   
        3.结合对称矩阵和三角矩阵的特性,可以得到三角矩阵的存储位置:
        下三角矩阵:k=i(i+1)/2+j,i>=j时;k=n(n+1)/2,i<j时;
        在求上三角矩阵时,可以进行推导:假如有Ai,j,那么它前面共有i行元素,行下标为0的有n个,行下标为1的有n-1个,。。。,行下标为i-1的有n-(i-1)个,因此,i行之前共有元素为:n+(n-1)+...+n-(i-1)个,再加上i行中在Ai,j前面还有j-i个元素,因此得到下列式子;
        ②上三角矩阵:k=i(2n-1+i)/2+j-i;i>=j时;k=n(n+1)/2,i<j时;
2.3.对角矩阵(带状矩阵):
        1.对角矩阵的所有非零元素都集中在主对角线为中心的带状区域内,最常见的有三对角带状矩阵,下面主要以三对角带状矩阵讨论;
        2.存储方法:将带状矩阵的非零元素按行存储在一维数组中,其中,除了第一行和最后一行为2个元素外,其余行均为3个元素,因此所存储的元素个数有:2+2+3*(n-2)个;存储位置K和元素下标之间的关系为:
        ①主对角线上的元素,下标具有i=j的关系,此时k=3*i;(i=j时);
        ②主对角线上左下角的元素,下标具有i=j+1的关系,此时k=3*i-1;(i=j+1时);
        主对角线上右上角的元素,下标具有i=j-1的关系,此时k=3*i+1;(i=j-1时);
        综合三种情况,可得三对角带状矩阵的存储位置K和下标i,j的关系为:k=2i+j(0<=i,j<n且i-1<=j<=i+i);
3.稀疏矩阵
        稀疏矩阵不是特殊的矩阵,它是含有少量的非零元素和较多的零元素,并且非零元素的分布没有规律,这样的矩阵称为稀疏矩阵;
3.1.稀疏矩阵的三元组表示:
        1.对于一个m*n的稀疏矩阵,它的非零元素个数t<m*n,如果采用常规存储方式,肯定会造成很大浪费,因此稀疏矩阵的存储必须采用压缩存储方式;
        2.由于稀疏矩阵毫无规律,因此除了要存储矩阵中的元素外,还要存储具体位置,这时使用一个三元组(i,j,Ai,j)来表示,分别代表元素的行下标、列下标和元素值,而且一般将矩阵m*n的行数、列数、元素个数存放在三元组表的第一个位置;
3.2.三元组的存储:
        1.一般来说,三元组的存储是以行为主序的,三元组表的顺序存储结构定义如下:
#define MAXSIZE 100 typedef struct{    int i;//行号     int j;//列号     int v;//元素的值 }TNode;//三元组类型 typedef struct{    int m;//稀疏矩阵的行数     int n;//稀疏矩阵的列数     int t;//稀疏矩阵的非零元素个数     TNode data[MAXSIZE];//三元组表 }TSMatrix; //三元组表类型 
        以上这种定义方式,稀疏矩阵的行数、列数和元素个数并没有在三元组表中,而是专门设置了3个域;
        2.三元组表的建立:
        假设一个m*n的稀疏矩阵存储在二维数组中,则建立三元表进行存储的方法如下:
//参数分别表示:指向三元组表的指针,指向存储稀疏矩阵的二维数组的二级指针, // 矩阵行数,矩阵列数 void createMatrix(TSMatrix *p,int **a,int m,int n){    int i,j;    p->m=m;    p->n=n;    p->t=0;    for(i=0;i<m;i++){        for(j=0;j<n;j++){            if(a[i][j]!=0){                p->data[p->t].i=i;                p->data[p->t].j=j;                p->data[p->t].v=a[i][j];                p->t++;             }        }    }}
3.3.矩阵的转置:
        1.对于一个m*n的矩阵A,它的转置矩阵为n*m的矩阵B,则有:Ai,j=Bj,i(0<=i<m,0<=j<n);
        2.转置有两种方法:按列序递增转置法和快速转置法;
3.3.1.按列序递增转置法:由于转置后行和列进行了交换,因此以列为主序进行查找,然后将查到的非零元素的i和j交换放入转置后的三元表中,查找完毕,即完成转置;算法实现如下:
void revert(TSMatrix *a,TSMatrix *b){    int k,p,q;    b->n=a->m;    b->m=a->n;    b->t=a->t;    if(b->t!=0){        q=0;        for(k=0;k<a->n;k++){//控制列数             for(p=0;p<a->t;p++){//在三元组表中进行查找                 if(a->data[p].j==k){//三元组表中存在j=k的元素                     b->data[q].i=a->data[p].j;                     b->data[q].j=a->data[p].i;                     b->data[q].v=a->data[p].v;                     q++;                }            }        }        }    }
3.3.2.快速转置法:
        1.按列序递增转置法由于存在双重循环,因此效率不高。
        2.快速转置法基本思想:在三元组表a中依次取出每个三元组,并准确放入三元组表b中。使用快速转置法需要预先计算以下数据:
        ①三元组表a中的每一列中非零元素的个数;
        ②三元组表a中每一列的第一个非零元素在表b中的位置;
        3.步骤(以a表表示原矩阵三元组表,b表表示转置后的三元组表):
        ①首先,规定三元组表a中每一列k在三元组表b中的位置假设为pot[k],则有:
        pot[0]=0;(因为a中第0列第一个非零元素转置后也是第0行);    
        pot[k]=pot[k-1]+第k-1列中元素的个数。
        ②第二,求各列非零元素的个数,并存在pot数组中:第k-1列中元素的个数可以存放在一个数组中,但为了节省内存,继续存放在pot[]数组中,则会有:
        pot[0]继续存放第0列第一个非零元素的位置,即pot[]=0;pot[1]~pot[n]存放0~n-1列的非零元素的个数;
        ③第三,求每一列第一个元素在b中对应的位置:由① ②两式可得,k列第一个非零元素对应b表中的位置为:
         pot[k]=pot[k-1]+ pot[k];
         赋值号左边的pot[k]表示k列第一个非零元素对应b表中的位置,赋值号右边的pot[k]表示第k-1列中非零元素的个数;具体如下图所示:

        4.算法实现:
void revert_Matrix(TSMatrix *a,TSMatrix *b){    int i,j,k,m,pot[MAXSIZE];    b->m=a->n;    b->n=a->m;    b->t=a->t;    if(b->t!=0){        //初始化pot数组,此时k取值能取到a->n,因为pot[0]保存第0列第一个非0元素,pot[1]~pot[a->n]保存0~a->n-1列的元素个数         for(k=0;k<=a->n;k++){            pot[k]=0;        }        //求元素个数,pot[0]保存第0列第一个非0元素,pot[1]~pot[a->n]保存0~a->n-1列的元素个数         for(i=0;i<a->n;i++){            k=a->data[i].j;             pot[k+1]=pot[k+1]+1;        }        //求各列第一个非零元素的位置         for(k=1;k<a->n;k++){            //左边的pot[k]表示第k列的位置,右边的pot[k]表示第k-1列的元素个数;            //pot[k-1]表示已经算好的位置;             pot[k]=pot[k-1]+pot[k];        }        for(i=0;i<a->t;i++){            k=a->data[i].j;            b->data[pot[k]].i=a->data[i].j;            b->data[pot[k]].j=a->data[i].i;            b->data[pot[k]].v=a->data[i].v;            pot[k]=pot[k]+1;//存放第k列的下一个三元组         }     } }


0 0
原创粉丝点击