稀疏矩阵

稀疏矩阵的定义

  对于那些零元素数目远远多于非零元素数目,并且非零元素的分布没有规律的矩阵称为稀疏矩阵(sparse)。

  人们无法给出稀疏矩阵的确切定义,一般都只是凭个人的直觉来理解这个概念,即矩阵中非零元素的个数远远小于矩阵元素的总数,并且非零元素没有分布规律。

稀疏矩阵的压缩存储

  由于稀疏矩阵中非零元素较少,零元素较多,因此可以采用只存储非零元素的方法来进行压缩存储。
  由于非零元素分布没有任何规律,所以在进行压缩存储的时侯需要存储非零元素值的同时还要存储非零元素在矩阵中的位置,即非零元素所在的行号和列号,也就是在存储某个元素比如aij的值的同时,还需要存储该元素所在的行号i和它的列号j,这样就构成了一个三元组(i,j,a[i][j])的线性表
  三元组可以采用顺序表示方法,也可以采用链式表示方法,这样就产生了对稀疏矩阵的不同压缩存储方式。

稀疏矩阵的存储方式

  1.稀疏矩阵的三元组表示

      若把稀疏矩阵的三元组线性表按顺序存储结构存储,则称为稀疏矩阵的三元组顺序表。 
    顺序表中除了存储三元组外,还应该存储矩阵行数、列数和总的非零元素数目,这样才能唯一的确定一个矩阵。

typedef struct Triple
{
    int row,col,e;
}Triple;

typedef struct TSMarix
{
    Triple data[max+1];//data是非零元素三元组表,data[0]未用 
    int m,n,len;//m:矩阵的行数,n:矩阵的列数,len:非零元素的个数 
}TSMarix;
    稀疏矩阵的转置

      行列递增转置法---算法思想:

        采用按照被转置矩阵三元组表A的序列(即转置后三元组表B中的行序)递增的顺序进行转置,并以此送入转置后矩阵的三元组表B中,这样一来,转置矩阵的三元组表B恰好是以“行序为主序”的

        这种方法中,转置后的三元组表B仍按行序递增存放,必须多次扫描被转置矩阵的三元组表A,以保证被转置矩阵递增形式进行转置,因此要通过双重循环来完成

#include <stdio.h>
#include <stdlib.h>
#define max 3

typedef struct Triple
{
    int row,col,e;
}Triple;

typedef struct TSMarix
{
    Triple data[max+1];//data是非零元素三元组表,data[0]未用 
    int m,n,len;//m:矩阵的行数,n:矩阵的列数,len:非零元素的个数 
}TSMarix;

void CreateTSMarix(TSMarix *T)
{
    int i;
    printf("请输入矩阵的行数,列数,非零元素个数:\n") ;
    scanf("%d%d%d",&T->m,&T->n,&T->len);
    if(T->m<1||T->n<1||T->len<1||T->len>=max)
    {
        printf("输入的数据有误!\n");
        exit(1);
    }

    printf("请输入非零元素的行下标,列下标,值(空格分开输入):\n");
    for(i=1;i<T->len+1;i++)
        scanf("%d%d%d",&T->data[i].row,&T->data[i].col,&T->data[i].e);
}

void TransposeTSMarix(TSMarix T,TSMarix *B)//B保存转置后的矩阵 
{
    B->n=T.m;//转置后B的列存储的T的行 
    B->m=T.n;//转置后B的行存储的T的列 
    B->len=T.len;//B的元素的个数和T的元素的个数一样不变 
    int i,j,k;
    if(B->len>0)
    {
        j=1;//转置后B元素中的下标 
        for(k=1;k<=T.m;k++)//采用被转置矩阵的列序即转置后矩阵的行序增序排列 
        {
            for(i=1;i<=T.len;i++)
            {
                if(T.data[i].col==k)//从头到尾扫描表A,寻找col值为k的三元组进行转置 
                {
                    
                    B->data[j].row=T.data[i].col;
                    B->data[j].col=T.data[i].row;
                    B->data[j].e=T.data[i].e;
                    j++;
                }
            }
        }
    }
}

void Print(TSMarix T)
{
    int i;
    printf("元素的行下标,列下标,值为:\n");
    for(i=1;i<=T.len;i++)
    {
        printf("\t%d\t%d\t%d\n",T.data[i].row,T.data[i].col,T.data[i].e);
    }
}
int main() 
{
    TSMarix T,B;
    printf("创建三元组表:\n");
    CreateTSMarix(&T);
    
    printf("输出三元组表:\n");
    Print(T);
    
    TransposeTSMarix(T,&B);
    printf("转置后的矩阵B为:\n");
    Print(B);
    return 0; 
}

       一次快速定位转置法---算法思想

        1.待转置矩阵三元组表A每一列非零元素的总个数,即转置后矩阵三元组表B的每一行中非零元素的总个数

        2.待转置矩阵每一列中第一个非零元素在三元组表B中的正确位置,即转置后矩阵每一行中的第一个非零元素在三元组表B中的正确位置,需要增设两个数组,num[col]用来存放三元组表A第col列非零元素总个数(三元组表第col行非零元素总个数),position[col]用来存放转置前三元组表A中第col列(转置后三元组表B中第col行)中第一个非零元素在三元组表B中的存储位置(下标值)。num[col]:将三元组表A扫描一遍,对于其列号为col的元素,给相应的num数组下标为col的元素加一(下标计数法);position[col]:position[1]=1,表示三元组表A中,列值为1的第一个非零元素在三元组表B中的下标值;position[col]=position[col-1]+num[col-1],其中2<=col<=A.n

        通过position[col]的初始值为三元组表A中第col列(三元组表B第col行)中第一个非零元素正确的位置,当三元组表A第col列有一个元素加入到三元组表B时,则position[col]=position[col]+1,令position[col]始终指向三元组表A第col列中下一个非零元素在三元组表B中的正确存放位置。

#include <stdio.h>
#include <stdlib.h>
#define max 10
typedef struct
{
    int row,col;
    int e;
}Triple;

typedef struct
{
    Triple data[max+1];
    int m,n,len;
}TSMatrix;

void Create(TSMatrix *T)
{
    printf("请输入矩阵行数,列数,非零元素个数:\n");
    scanf("%d%d%d",&T->m,&T->n,&T->len);
    if(T->m<1||T->n<1||T->len<1||T->len>max)
    {
        printf("输入的数据有误!\n");
        exit(1);
    }
    
    printf("请输入元素的行下标,列下标,值:\n");
    for(int i=1;i<T->len+1;i++)
        scanf("%d%d%d",&T->data[i].row,&T->data[i].col,&T->data[i].e);
}

void FastTranspose(TSMatrix T,TSMatrix *B)
{
    int col,t,p,q;
    int num[max],position[max];
    B->len=T.len;
    B->m=T.n;
    B->n=T.m;
    if(B->len>0)
    {
        for(col=1;col<=T.n;col++)
            num[col]=0;
            
        for(t=1;t<=T.len;t++)//采用数组下标计数法计算每一列非零元素的个数 
            num[T.data[t].col]++;
            
        position[1]=1;
        for(col=2;col<=T.n;col++)//求col列中第一个非零元素在B.data[]中的正确位置 
            position[col]=position[col-1]+num[col-1];
            
        for(p=1;p<=T.len;p++)
        {
            col=T.data[p].col;
            q=position[col];
            B->data[q].row=T.data[p].col;
            B->data[q].col=T.data[p].row;
            B->data[q].e=T.data[p].e;
            position[col]++;
        }
        
    }
}

void Print(TSMatrix T)
{
    printf("\t行下标\t列下标\t值:\n");
    for(int i=1;i<T.len+1;i++)
        printf("\t%d\t%d\t%d\n",T.data[i].row,T.data[i].col,T.data[i].e);
}
int main() 
{
    TSMatrix T,B;
    printf("创建三元组:\n");
    Create(&T);
    
    printf("输入的三元组为:\n");
    Print(T);
    
    printf("快速排序后的三元组为:\n");
    FastTranspose(T,&B);
    Print(B);
    return 0;
}

    十字链表

    十字链表结点分为三类 : 
      表结点,它由五个域组成,其中i和j存储的是结点所在的行和列,right和down存储的是指向十字链表中该结点所有行和列的下一个结点的指针,v用于存放元素值。
    行头和列头结点,这类结点也有域组成,其中行和列的值均为零,没有实际意义,right和down的域用于在行方向和列方向上指向表结点,next用于指向下一个行或列的表头结点。
    总表头结点,这类结点与表头结点的结构和形式一样,只是它的i和j存放的是矩阵的行和列数。

    十字链表可以看作是由各个行链表和列链表共同搭建起来的一个综合链表,每个结点aij既是处在第i行链表的一个结点,同时也是处在第j列链表上的一个结点,就你是处在十字交叉路口上的一个结点一样,这就是十字链表的由来。
      十字链表中的每一行和每一列链表都是一个循环链表,都有一个表头结点。

转载于:https://www.cnblogs.com/tianzeng/p/9700272.html

一、选择 1.若结点的存储地址其关键字之间存在某种函数关系,则称这种存储结构为( ) A.顺序存储结构 B.链式存储结构 C.线性存储结构 D.散列存储结构 2.在长度为100的顺序表的第10个位置上插入一个素,素的移动次数为( ) A.90 B.91 C.89 D.92 3.对于只在表的首、尾两端进行插入操作的线性表,宜采用的存储结构为( ) A.顺序表 B.用头指针表示的单循环链表C.用尾指针表示的单循环链表D.单链表 4.若进栈序列为c, a,b,则通过入出栈操作可能得到的a,b,c的不同排列个数为( ) A.4 B.5 C.3 D.2 5.为查找某一特定单词在文本中出现的位置,可应用的串运算是( ) A.插入 B.删除 C.串联接 D.子串定位 7.三维数组A[4][5][6]按行优先存储方法存储在内存中,若每个素占2个存储单,且数组中第一个素的存储地址为120,则素A[2][4][5]的存储地址为( ) A.296 B.298 C.300 D.302 6.下列陈述中正确的是( ) A.二叉树是度为2的有序树 B.二叉树中结点只有一个孩子时无左右之分 C.二叉树中必有度为2的结点D.二叉树中最多只有两棵子树,并且有左右之分 7.n个叶子的霍夫曼树结点总数为( ) A.2n B.2n-1 C.2n+1 D.不能确定 8.有向图的邻接矩阵中,每列素之和为该顶点的( ) A.度 B.入度 C.出度 D.权值 9.100个结点完全二叉树高度是( ) A.9 B.10 C.11 D.12 10.n个顶点的无向完全图中含有向边的数目最多为( ) A.n-1 B.n C.n(n-1)/2 D.n(n-1) 11.已知一个有向图如右所示,则从顶点a出发进行深度优先偏历,不可能得到的DFS序列为( ) A.a d b e f c B.a d c e f b C.a d c b f e D.a d e f c b 12.在最好和最坏情况下的时间复杂度均为O(nlogn)且稳定的排序方法是( ) A.快速排序 O(n*n) B.堆排序 C.归并排序 D.基数排序 13.不可能生成右图所示二叉排序树的关键字序列是( ) A.4 5 3 1 2 B.4 2 5 3 1 C.4 5 2 1 3 D.4 2 3 1 5 14.平衡二叉树中任一结点的( ) A.左、右子树的高度均相同 B.左、右子树高度差的绝对值不超过1 C.左子树的高度均大于右子树的高度 D.左子树的高度均小于右子树的高度 15.在VSAM文件的控制区间中,记录的存储方式为( ) A.无序顺序 B.有序顺序 C.无序链接 D.有序链接 二、填空题 16.若一个算法中的语句频度为T(n)=3720n3/2+4nlogn,则算法的时间复杂度为________。 17.单链表中若在指针p所指的结点之后插入指针s所指结点,则可用下列两个语句实现该操作,是_s->next=p->next和_______ _。 18.假设以S和X分别表示进栈和退栈操作,则对输入序列a,b,c,d,e进行一系列栈操作SSXSXXSSXX之后,得到的输出序列为________。 19.串S=″I am a worker″的长度是________。 20.假设一10阶下三角矩阵按列优顺序压缩存储一维数组C中,则C数组的大小为________。 21.在n个结点的二叉链表中,有________个空指针。 22.对关键字序列(52,80,63,44,48,21)进行一趟简单选择排序之后得到的结果为________。 23.由10个结点构成的二叉排序树,在等概率查找的假设下,查找成功时的平均查找长度的最大值可能达到________,最小为 。 24头指针为f,尾指针为r的循环队列判断空的条件是 (r+1)%max==f 。 三、问答题 1.已知一个6行5列的稀疏矩阵非零的值分别为:9,41,6,8,-54,5和-8,它们在矩阵中的列号依次为:1,4,5,1,2,4和5,其行值依次为1,1,2,2,3和5。请写出该稀疏矩阵。 2.已知二叉树树T的先序遍历序列为ABCDE,中序遍历序列为CBDEA。请画出二叉树T。 3.对关键字序列(72,87,61,23,94,16,05,58)进行堆排序,使之按关键字递减次序排列。请写出排序过程中得到的初始堆和前三趟的序列状态。 初始堆:_94,87,。。。_______ 第1趟:{94},87,72,61,58,23,16,5 第2趟:{94,87},72,58,61,5, 23,16, 4.在关键字序列(07,12,15,18,27,32,41,92)中用二分查找法查找和给定值41相等的关键字,请写出查找过程中依次和给定值“41”比较的关键字。平均查找长度。 ASL=(1+2+3)/8= 霍夫曼树:给定若干关键字在某次通信中出现频率,要求构造霍夫曼树并求每个结点编码。a,b,c,d,e,f:2,5,7,8,12,30 关键字序列:4,7,11,14,49,54,散列函数H(key)=key%7,散列表长0。。6,线性探测法处理冲突,构造该散列表。 关键字序列: 14,7,11,4,49,54,写出直接插入排序,起泡排序,简单选择排序(快速排序)每趟结果。 初始序列:{14,7,11,4,49,54} 第一趟{4,7,11},14,{49,54} 第二趟:4,{7,11},14, 49,{54} 第三趟4,7,{11},14, 49,{54} 五、算法设计 .假设二叉树T采用如下定义的存储结构: typedef struct node { DataType data; struct node *lchild,*rchild; }BinTree;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值