目录

前言:

需求分析:

难点分析:

代码和思路详解:

三元组表的头文件:

建立索引思路:

什么是三元组表的索引:

结合题意实现索引表:

建立索引表的代码实现:

索引表的结构体定义:

索引表的创建思路:

索引表创建的代码实现:

打印索引表:

求鞍点的思路:

基本思路:

注意事项:

求鞍点的代码实现:

程序测试:

main()函数:

行优先三元组表:

列优先三元组表:

三元组表tup的源文件:

总结:


前言:

两个实验14——三元组、广义表,个人写下来,要写好还是这个实验更难一些(也可能是因为我递归写的比较熟悉),这个实验完善后的代码量比广义表复制那个实验多了一倍。

需求分析:

如果矩阵A中存在这样的一个元素A[i][j]满足以下条件:A[i][j]是第i行中值最小的元素,且又是第j列中值最大的元素,则称A[i][j]为矩阵A的一个鞍点。

假设稀疏矩阵A(大小是m×n)已经用三元组表存放。要求建立行索引和列索引实现求鞍点的算法,如果没有鞍点,也给出相应信息。

难点分析:

1.如果是二维数组存储的矩阵实现起来非常简单,但是已经用三元组存储好了,我们并不知道是行优先存储还是列优先存储,需要分情况。

2.行优先或者列优先建立索引和遍历都有区别。

3.三元组表存储的稀疏矩阵,得考虑到某一行若全是大于0的数时,行最小大于0;若某一列全是负数,列最大小于0。

代码和思路详解:

三元组表的头文件:

我在头文件定义了三元组表示稀疏矩阵的结构体、行优先建表、列优先建表和打印三元组;初始化三元组是在建表是内部调用的,方便后续的测试。源文件我放在最后,我们主要讲索引和求鞍点。

/*
**作者:李宗霖    日期:2023/5/10
**三元组表示稀疏矩阵算法库2
*/
#include <stdio.h>
#include <stdlib.h>typedef struct {int r;         //行 int c;          //列 int v;          //非零元素
}TraidNode, *Traid; //三元组 typedef struct {int rows;     //行数 int cols;      //列数 int nums;      //非零元素数 Traid data;     //用指针可以根据nums开空间
}TSmat, *TSMatrix;  //稀疏矩阵的三元组表 TSMatrix InitTSM(int *MAT, int rows, int cols);     //初始化三元组表
TSMatrix RFCreat(int *MAT, int rows, int cols);     //行优先建表
TSMatrix CFCreat(int *MAT, int rows, int cols);     //列优先建表
void DispTraid(TSMatrix t);                         //打印三元组表 

建立索引思路:

什么是三元组表的索引:

稀疏矩阵在转化成三元组表存储后,大大节省了空间,但是失去了随机存取的性质,需要通过遍历寻找某一元素,而遍历最坏情况的时间复杂度为O(mn),所以我们可以通过建立一个索引(相当于目录)来节省时间,最坏情况时间复杂度为O(m)或O(n),这取决于这个三元组是行优先还是列优先,所以索引也分为行索引和列索引。

如图1是带行索引的三元组表,没有索引要查找第6行元素需要将前面所有元素都遍历一遍,建了索引之后可以立马找到第六行的第一个元素,从第六行第一个元素开始遍历;如果没有索引要查找是否有第4行元素,需要将前面的所有元素遍历,建立索引,行为4的索引指向行3结束后的第一个元素,行为5,大于4说明没有行4元素。综上索引表可以帮助我们快速定位到目标行/列所在第一个元素,大大加快查找速度。

结合题意实现索引表:

题目需要查找第i行最小元素,和第j列的最大元素,所以我们的索引表除了可以存储查找起始位置外,我们还可以行索引存当前行最小值,列索引存当前列最大值如图2:

图上可以看到,当三元组表行优先时,列无序,列索引存储的位置没法确定,用数组的话,可以一个二元数组存放行索引,一个一元数组存放列索引,这里我定义了一个Index结构体,因为不知道行列大小所以用三个指针动态开空间:*loca存放遍历起始位置,*rvmin存放行最小元素,*cvmax存放列最大元素;r:row行,c:col列,v:value元素。

建立索引表的代码实现:

索引表的结构体定义:

如果是行优先*loca存放行索引,列优先则是存放列索引,为了区分更方便,定义了一个int tag,tag==0时行优先,tag==1时列优先:

typedef struct {int tag;     //标志:tag==0,行优先索引;tag==1,列优先索引 int *loca;       //行优先则存储对应行非0元素在三元组中出现位置,列优先亦然 int *rvmin;       //存储对应行的最小元素值(r:行,v:元素)int *cvmax;       //存储对应列的最大元素值(c:列,v:元素)
}Index;

索引表的创建思路:

1.判断行优先还是列优先:

三元组表t是已经存储好的,我们并不知道到底是行优先还是列优先,所以我们需要在遍历的过程中去判断三原组表的优先级。

我这里的思路是定义一个lastrow存储遍历过程中访问的上一个行标,如果lastrow大于现在正在访问的行标row,说明行标是无序的,那就是列优先,将lastrow置为-1。如果从头到尾row一直>=lastrow,那说明row是有序的,就是行优先,行和列都有序默认为行优先。

2.对rvmin数组和cvmax数组的初始化:

在初始化方面如果第一次写思路很顺的话下意识就给全部初始化为0了,我最开始写就写成这样了,后面测试发现有问题修改的,这个是很容易被忽略的问题。

之前说了行的最小值可能不为0,即整个行元素都大于0,如果初始化为0,0永远小于正整数,而0不被记录在三元组内,则rvmin的值永远小于等于0,很显然有问题。很多人也很容易想到,赋值0不行,那我遍历一遍获取整个矩阵的最大值再赋值给rvmin不就行了?那当然,没有那么简单;如果全部赋值为最大值,0不被记录,解决了某行全部元素大于0的问题,但是其他不是所有元素大于0的行不九出问题了,明明最小值为0,0没被记录,所以0不是最小值。下面我给出我能想到的最简单的方法:

思路就是全部赋值为矩阵最大值,不过最后还需要加上判断语句,如果行非0元素的数量小于矩阵的列数,并且,当前rvmin[i]>0的时候rvmin[i]=0。计算非0元素数量我们后续操作也有用。

解决rvmin,还有cvmax,当某一列元素全为负数的时候0恒大于负数,存储的最大值为0,实际最大值是小于0的数,解决思路和上面一样,如果非0元素数量小于矩阵行数,并且,当前cvmax[i]<0的时候cvmax[i]=0。

3.对rvmin,cvmax,loca的赋值

rvmin和cvmax的赋值非常简单,初始化完成之后,在遍历三元组表的过程中进行比较,符合条件的赋值。

loca的赋值和三元组的快速转置思路相同:我们先确定标号为1的访问位置为0,我们需要计算出每行/每列非0元素的个数,上一行/列的首元素位置+上一行/列元素数量=当前行/列首元素的位置。

索引表创建的代码实现:

为了更符合我们的阅读习惯(从第1行开始而不是第0行)rvmin[0],cvmax[0],loca[0],我们可以不存放数据,但是为了我们后续输出索引表方便遍历,我们可以分别存放,矩阵的行数,矩阵的列数,和非0元素个数。

代码分为几个部分:Index的初始化(开空间)->计算矩阵最大最小值并初始化rvmin,cvmax,rvnum,cvnum ->遍历三元组表求出各行最小元素值,各列最大元素值,各行、列非0元素数量,并判断是行优先还是列优先 ->根据优先级*loca开空间并赋值(上一行/列位置+上一行/列的数量)。

Index *CreatIndex(TSMatrix t) {//rvmin,cvmax开空间 Index *I = (Index *)malloc(sizeof(Index));I->rvmin = (int *)malloc((t->rows+1)*sizeof(int));I->cvmax = (int *)malloc((t->cols+1)*sizeof(int));//rvnum存各行的非0元素数量,cvnum存各列的非0元素数量 int i, max = 0, min = 0;int rvnum[t->rows+1], cvnum[t->cols+1];//max获取矩阵最大值,min获取矩阵最小值 for(i = 0; i < t->nums; i++) {if(max < t->data[i].v) max = t->data[i].v;else if(min > t->data[i].v) min = t->data[i].v;}//rvmin初始化为矩阵最大值,cvmax初始为举证最小值,num初始化为0 for(i = -1; i < t->rows && i < t->cols; i++) {if(i < t->rows) {I->rvmin[i+1] = max;rvnum[i+1] = 0;}if(i < t->cols) {I->cvmax[i+1] = min;cvnum[i+1] = 0;}}I->rvmin[0] = t->rows;I->cvmax[0] = t->cols;//遍历三元组表,lastrow记录上一个三元组的行标,若行标无序判断为列优先 int lastrow = t->data[0].r;for(i = 0; i < t->nums; i++) {//遍历求出各行最小元素值,各列最大元素值,各行、列非0元素数量 int row = t->data[i].r;rvnum[row]++;int col = t->data[i].c;cvnum[col]++;if(t->data[i].v < I->rvmin[row]) I->rvmin[row] = t->data[i].v;if(t->data[i].v > I->cvmax[col]) I->cvmax[col] = t->data[i].v;if(lastrow != -1) {if(lastrow <= row) lastrow = row;else lastrow = -1;}}//某行不全是非0元素且最小值不小于0,置为0 for(i = 1; i < t->rows + 1 && i < t->cols + 1; i++) {if(i < t->rows + 1 && rvnum[i] != t->cols && I->rvmin[i] > 0)I->rvmin[i] = 0;if(i < t->cols + 1 && cvnum[i] != t->rows && I->cvmax[i] < 0)I->cvmax[i] = 0;}//行优先三元组表,位置索引为行索引,记录每一行第一个元素在三元组表中出现的位置 if(lastrow != -1) {I->tag = 0;I->loca = (int *)malloc((t->rows+1)*sizeof(int));I->loca[0] = t->nums;I->loca[1] = 0;for(i = 1; i < t->rows; i++) {I->loca[i+1] = I->loca[i] + rvnum[i];}}//列优先三元组表 else {I->tag = 1;I->loca = (int *)malloc((t->cols+1)*sizeof(int));I->loca[0] = t->nums;I->loca[1] = 0;for(i = 1; i < t->cols; i++) {I->loca[i+1] = I->loca[i] + cvnum[i];}}return I;
}

打印索引表:

void DispIndex(Index *I) {int i;if(I->tag == 0) {printf("\n行优先三元组表索引:");printf("\n行号:\t\t");for(i = 1; i < I->rvmin[0] + 1; i++)printf("%d\t", i);printf("\n遍历起始位:\t");for(i = 1; i < I->rvmin[0] + 1; i++)printf("%d\t", I->loca[i]);printf("\n行最小元素:\t");for(i = 1; i < I->rvmin[0] + 1; i++)printf("%d\t", I->rvmin[i]);printf("\n\n列号:\t\t");for(i = 1; i < I->cvmax[0] + 1; i++)printf("%d\t", i);printf("\n列最大元素:\t");for(i = 1; i < I->cvmax[0] + 1; i++)printf("%d\t", I->cvmax[i]); }else {printf("\n列优先三元组表索引:");printf("\n行号:\t\t");for(i = 1; i < I->rvmin[0] + 1; i++)printf("%d\t", i);printf("\n行最小元素:\t");for(i = 1; i < I->rvmin[0] + 1; i++)printf("%d\t", I->rvmin[i]);printf("\n\n列号:\t\t");for(i = 1; i < I->cvmax[0] + 1; i++)printf("%d\t", i);printf("\n遍历起始位:\t");for(i = 1; i < I->cvmax[0] + 1; i++)printf("%d\t", I->loca[i]);printf("\n列最大元素:\t");for(i = 1; i < I->cvmax[0] + 1; i++)printf("%d\t", I->cvmax[i]); }printf("\n");
}

求鞍点的思路:

基本思路:

我们建立的索引表中包含了每一行的最小值*rvmin,和每一列的最大值*cvmax,只需要i遍历行,j遍历列,判断rvmin[i]==cvmax[j],那么点A[i][j]就是第i行最小值,第j列最大值,即鞍点。

注意事项:

1.我们在遍历过程中需要判断是否有鞍点,我的思路是建立一个int have=0,如果判断有鞍点have=1,遍历完成have=0,则没有鞍点。

2.如果判断有鞍点后,我们需要获取鞍点的值,如果遍历整个三元组表那就是置索引于枉然。我们在这里需要使用三元组表的索引查找来节省时间,快速判断三元组表是否存在点i,j,存在赋值,不存在则鞍点值为0,注意遍历也需要区分是行优先还是列优先。

求鞍点的代码实现:

上面建索引比较难,建成索引,求鞍点就洒洒水啦。

void GetSaddle(Index *I, TSMatrix t) {int i, j, val;int have = 0;           //have作为标志判断是否存在鞍点,存在have=1 for(i = 1; i < t->rows + 1; i++) {for(j = 1; j < t->cols + 1; j++) {if(I->rvmin[i] == I->cvmax[j]) {        //判断点(i,j)是否是鞍点 val = 0;           //鞍点的值默认为0,与三元组表成功匹配则赋值 have = 1;           //鞍点存在 int k;               //k作为访问三元组表的指针 if(I->tag == 0) {   //行优先 for(k = I->loca[i]; t->data[k].r == i; k++) {if(t->data[k].c == j) {val = t->data[k].v;break; //匹配成功直接退出节约时间 }}}else {                //列优先 for(k = I->loca[j]; t->data[k].c == j; k++) {if(t->data[k].r == i) {val = t->data[k].v;break;}}}printf("\n鞍点:(%d,%d) = %d", i, j, val);}}}printf("\n");//鞍点不存在输出提示信息 if(have == 0) printf("在这个稀疏矩阵中没找到鞍点啦!!!\n");
}

程序测试:

main()函数:

int MAT[M][N] = {{1, 0, 0, 5, 0},{0, 0, 0, 0, 0},{0, 0, 0, 0, 0},{7, 6, 8, 5, 9},{0, 0, 0, 0, 0},{0, 0, 3, 0, 0}};int i, j;for(i = 0; i < M; i++) {for(j = 0; j < N; j++)printf("%d\t", MAT[i][j]);printf("\n");}TSMatrix rft = RFCreat(*MAT, M, N);Index *rfI = CreatIndex(rft);TSMatrix cft = CFCreat(*MAT, M, N);Index *cfI = CreatIndex(cft);DispTraid(rft);DispIndex(rfI);GetSaddle(rfI, rft);DispTraid(cft);DispIndex(cfI);GetSaddle(cfI, cft);return 0;
}

行优先三元组表:

列优先三元组表:

大家可以多测试几组一些矩阵试试,我测试下来没有任何问题

三元组表tup的源文件:

/*
**作者:李宗霖    日期:2023/5/10
**三元组表示稀疏矩阵算法库2
*/
#include "tup.h"//初始化
TSMatrix InitTSM(int *MAT, int rows, int cols) {//计算非0元素个数nums int i, j, nums = 0;for(i = 0; i < rows; i++) {for(j = 0; j < cols; j++) {if(MAT[i*cols+j] != 0) nums++;}}TSMatrix t = (TSMatrix)malloc(sizeof(TSmat));t->rows = rows;t->cols = cols;t->nums = nums;t->data = (Traid)malloc(nums * sizeof(TraidNode));return t;
}//行优先建表
TSMatrix RFCreat(int *MAT, int rows, int cols) {TSMatrix t = InitTSM(MAT, rows, cols);int i, j, k = 0;for(i = 0; i < rows; i++) {for(j = 0; j < cols; j++) {if(MAT[i*cols+j] != 0) {t->data[k].r = i + 1;t->data[k].c = j + 1;t->data[k++].v = MAT[i*cols+j];}}}return t;
}//列优先建表
TSMatrix CFCreat(int *MAT, int rows, int cols) {TSMatrix t = InitTSM(MAT, rows, cols);int i, j, k = 0;for(j = 0; j < cols; j++) {for(i = 0; i < rows; i++) {if(MAT[i*cols+j] != 0) {t->data[k].r = i + 1;t->data[k].c = j + 1;t->data[k++].v = MAT[i*cols+j];}}}return t;
}//打印三元组表
void DispTraid(TSMatrix t) {printf("\n %d\t%d\t%d\n", t->rows, t->cols, t->nums);printf(" ----------------\n");int i;for(i = 0; i < t->nums; i++) {printf(" %d\t%d\t%d\n", t->data[i].r, t->data[i].c, t->data[i].v);}
}

总结:

主要是理解索引表是如何实现的,索引表有什么作用,和你需要索引表存储哪些内容。这种类型代码可能第一次写会比较费劲,写过一次之后就会比较简单了。

数据结构 实验14(1-2班):(深入理解索引存储结构)三元组存储的稀疏矩阵建立行列索引并求鞍点相关推荐

  1. 数据结构实验一,第2题:基于顺序存储结构的图书信息表的排序

    描述 定义一个包含图书信息(书号.书名.价格)的顺序表,读入相应的图书数据完成图书信息表的创建,然后将图书按照价格降序排序,逐行输出排序后每本图书的信息. 输入 输入n+1行,前n行是n本图书的信息( ...

  2. 数据结构实验一,第1题:基于顺序存储结构的图书信息表的创建和输出

    描述 定义一个包含图书信息(书号.书名.价格)的顺序表,读入相应的图书数据来完成图书信息表的创建,然后统计图书表中的图书个数,同时逐行输出每本图书的信息. 输入 输入n+1行,其中前n行是n本图书的信 ...

  3. 《数据结构》实验报告(一)——顺序表存储结构及实现

    顺序表存储结构及实现--学生信息管理 一.实验目的 (1) 掌握顺序表的概念及实现方式. (2) 掌握顺序表的存储结构及主要运算:建立.查找.插入.删除等. 二.实验环境 Windows 10,Mic ...

  4. 数据结构32:树存储结构

    之前介绍的所有的数据结构都是线性存储结构.本章所介绍的树结构是一种非线性存储结构,存储的是具有"一对多"关系的数据元素的集合. (A)                       ...

  5. 数据结构大致包含以下几种存储结构:

    数据结构大致包含以下几种存储结构: 线性表,还可细分为顺序表.链表.栈和队列: 树结构,包括普通树,二叉树,线索二叉树等: 图存储结构: 线性表 线性表结构存储的数据往往是可以依次排列的,就像小朋友手 ...

  6. python树结构_数据结构的树存储结构

    之前介绍的所有的数据结构都是线性存储结构.本章所介绍的树结构是一种非线性存储结构,存储的是具有"一对多"关系的数据元素的集合. (A)                       ...

  7. 数据结构和数据存储结构

    数据结构和数据存储结构 数据结构和数据存储结构是不同的:一个是逻辑概念上的一个是真实存储在计算机上的 数据的存储结构:顺序.链式.索引.散列 数据的存储结构是针对计算机来说的,指的是数据的逻辑结构在计 ...

  8. 数据结构(一)逻辑结构和存储结构

    文章目录 数据结构 逻辑结构 集合结构 线性结构 树状结构 网络结构(图形) 物理结构(存储) 顺序存储结构 链式存储结构 索引存储结构 散列存储结构(哈希) 数据结构对性能的影响 数据结构 数据结构 ...

  9. 树存储结构——数据结构

    文章目录 前言 一.树的定义 二.树的结点 三.子树和空树 四.结点的度和层次 四.有序树和无序树 五.森林 六.树的其他表示方式 总结 前言 数据的逻辑结构 线性结构 线性表 栈(特殊线性表) 队列 ...

最新文章

  1. adb.exe: device offline
  2. 7 自动开启网卡_淘汰的旧手机别扔掉,这样设置变身4G上网卡
  3. 织梦html仅动态,dede织梦系统后台发布文章时设置为默认动态浏览的方法
  4. 联想T460 Win10下“系统中断”CPU占用率高的解决办法
  5. 周期均方根和有效值的区别_买羊肉,“羔羊肉”和“羊肉”有啥区别?口感差别大,别再乱买了...
  6. 内容管理系统(CMS)的设计和选型
  7. 乐视赚钱的时候,贾跃亭特别喜欢找明星入股
  8. 动漫美少女生成神器、猫的门禁...2019 年十七大最佳机器学习项目 | 年度盘点...
  9. [转帖] IPsec相关知识 --未知来源
  10. 联通、华为双双辟谣:云计算不要太火
  11. ES6、7学习笔记(尚硅谷)-3-变量的解构赋值
  12. 《Redis设计与实现》知识点目录
  13. SQL语句查询:查询本周过生日的信息
  14. flink的流处理的source
  15. 用友t3系统打印机如何连接到服务器,用友T3打印设置方法
  16. 【算法分析】多个对比算法的统计检验方法
  17. 思考总结:REID实践 罗浩郑哲东 bag of tricks for person reid baseline运行记录
  18. Python将url转换作为合法文件名
  19. 美团运维SRE+运维开发一面面经汇总
  20. BT面板静态文件镜像库v7.1.0

热门文章

  1. 程序的CPU执行时间
  2. 我们是科幻迷(刘慈欣)
  3. ssm 一对多的映射关系
  4. docker swam 安装kafka集群以及kfakamanger
  5. SE 软件工程期末总结
  6. ES设置自定义分词器
  7. RaSa2.5.x行为之四:表单(Forms)
  8. 华润置地php面试题_华润置地有限公司面试攻略,面试题,面试技巧及流程(附笔试,评论,薪资)-金针菇企评网...
  9. 互联网寒冬,7面阿里,终获Offer,定级P6+
  10. IOS 图片点击放大不失真