数据结构 实验14(1-2班):(深入理解索引存储结构)三元组存储的稀疏矩阵建立行列索引并求鞍点
目录
前言:
需求分析:
难点分析:
代码和思路详解:
三元组表的头文件:
建立索引思路:
什么是三元组表的索引:
结合题意实现索引表:
建立索引表的代码实现:
索引表的结构体定义:
索引表的创建思路:
索引表创建的代码实现:
打印索引表:
求鞍点的思路:
基本思路:
注意事项:
求鞍点的代码实现:
程序测试:
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班):(深入理解索引存储结构)三元组存储的稀疏矩阵建立行列索引并求鞍点相关推荐
- 数据结构实验一,第2题:基于顺序存储结构的图书信息表的排序
描述 定义一个包含图书信息(书号.书名.价格)的顺序表,读入相应的图书数据完成图书信息表的创建,然后将图书按照价格降序排序,逐行输出排序后每本图书的信息. 输入 输入n+1行,前n行是n本图书的信息( ...
- 数据结构实验一,第1题:基于顺序存储结构的图书信息表的创建和输出
描述 定义一个包含图书信息(书号.书名.价格)的顺序表,读入相应的图书数据来完成图书信息表的创建,然后统计图书表中的图书个数,同时逐行输出每本图书的信息. 输入 输入n+1行,其中前n行是n本图书的信 ...
- 《数据结构》实验报告(一)——顺序表存储结构及实现
顺序表存储结构及实现--学生信息管理 一.实验目的 (1) 掌握顺序表的概念及实现方式. (2) 掌握顺序表的存储结构及主要运算:建立.查找.插入.删除等. 二.实验环境 Windows 10,Mic ...
- 数据结构32:树存储结构
之前介绍的所有的数据结构都是线性存储结构.本章所介绍的树结构是一种非线性存储结构,存储的是具有"一对多"关系的数据元素的集合. (A) ...
- 数据结构大致包含以下几种存储结构:
数据结构大致包含以下几种存储结构: 线性表,还可细分为顺序表.链表.栈和队列: 树结构,包括普通树,二叉树,线索二叉树等: 图存储结构: 线性表 线性表结构存储的数据往往是可以依次排列的,就像小朋友手 ...
- python树结构_数据结构的树存储结构
之前介绍的所有的数据结构都是线性存储结构.本章所介绍的树结构是一种非线性存储结构,存储的是具有"一对多"关系的数据元素的集合. (A) ...
- 数据结构和数据存储结构
数据结构和数据存储结构 数据结构和数据存储结构是不同的:一个是逻辑概念上的一个是真实存储在计算机上的 数据的存储结构:顺序.链式.索引.散列 数据的存储结构是针对计算机来说的,指的是数据的逻辑结构在计 ...
- 数据结构(一)逻辑结构和存储结构
文章目录 数据结构 逻辑结构 集合结构 线性结构 树状结构 网络结构(图形) 物理结构(存储) 顺序存储结构 链式存储结构 索引存储结构 散列存储结构(哈希) 数据结构对性能的影响 数据结构 数据结构 ...
- 树存储结构——数据结构
文章目录 前言 一.树的定义 二.树的结点 三.子树和空树 四.结点的度和层次 四.有序树和无序树 五.森林 六.树的其他表示方式 总结 前言 数据的逻辑结构 线性结构 线性表 栈(特殊线性表) 队列 ...
最新文章
- adb.exe: device offline
- 7 自动开启网卡_淘汰的旧手机别扔掉,这样设置变身4G上网卡
- 织梦html仅动态,dede织梦系统后台发布文章时设置为默认动态浏览的方法
- 联想T460 Win10下“系统中断”CPU占用率高的解决办法
- 周期均方根和有效值的区别_买羊肉,“羔羊肉”和“羊肉”有啥区别?口感差别大,别再乱买了...
- 内容管理系统(CMS)的设计和选型
- 乐视赚钱的时候,贾跃亭特别喜欢找明星入股
- 动漫美少女生成神器、猫的门禁...2019 年十七大最佳机器学习项目 | 年度盘点...
- [转帖] IPsec相关知识 --未知来源
- 联通、华为双双辟谣:云计算不要太火
- ES6、7学习笔记(尚硅谷)-3-变量的解构赋值
- 《Redis设计与实现》知识点目录
- SQL语句查询:查询本周过生日的信息
- flink的流处理的source
- 用友t3系统打印机如何连接到服务器,用友T3打印设置方法
- 【算法分析】多个对比算法的统计检验方法
- 思考总结:REID实践 罗浩郑哲东 bag of tricks for person reid baseline运行记录
- Python将url转换作为合法文件名
- 美团运维SRE+运维开发一面面经汇总
- BT面板静态文件镜像库v7.1.0