数据结构5---矩阵和广义表

一、矩阵的压缩存储

特殊矩阵:矩阵中很多值相同的元素并且它们的分布有一定的规律

稀疏矩阵:矩阵中有很多零元素。压缩存储的基本思想是:

        (1)为多个值相同的元素只分配一个存储空间;

        (2)对零元素不分配存储空间。

1、特殊矩阵的压缩存储

(1)对称矩阵

只存储下三角部分的元素

存储结构

对于下三角的元素aij(i>=j),在数组中的下标与i,j关系为 k = i*(i-1)/2 + j-1

上三角的元素aij(i<j),因为aij = aji,则访问它的元素aji即可,即:k = j*(j-1)/2+i - 1

 (2)三角矩阵

只存储上三角(或下三角)部分的元素

下三角矩阵

存储:

1.下三角元素        2.对角线上方的常数

矩阵中的任一元素aij在数组中的下标k与i,j的对应关系

当i>=j时        k=i*(i-1)/2+j-1

当i<j              k=n*(n+1)/2

上三角矩阵

存储:

1.上三角元素        2.对角线下方的常数

矩阵中的任一元素aij在数组中的下标k与i,j的对应关系

当i<=j时        k=(i-1)*(2n-i+2)/2+j-1

当i>j              k=n*(n+1)/2

 (3)对角矩阵

对角矩阵:所有非零元素都集中在以主对角线为中心的带状区域中,除了主对角线和它的上下方若干条对角线的元素外,所有其他元素都为零。

 (4)稀疏矩阵

 我们定义了一个三元组

struct Triple
{int row,col;    //行和列DataType item;    //值
};

三元组表:将稀疏矩阵的非零元素对应的三元组所构成的集合,按行优先的顺序排列成一个线性表。

采用顺序存储结构存储三元组表

三元顺序表的存储结构定义

const int MAX = 100;
struct SparseMatrix
{struct Triple data[MAX];    //存储非零元素int mu,nu,num;    //行数,列数,非零元素个数
};

这种存储方法的缺点是:进行矩阵加法、减法等操作时,非零元素的个数和位置都会发生变化,顺序存储法就非常不方便了。

2、十字链表

采用链接存储结构存储三元组表,每个非零元素对应的三元组存储为一个链表结点,结构为:

rowcolitem
downright

row:存储非零元素的行号

col:存储非零元素的列号

item:存储非零元素的值

right:指针域,指向同一行中的下一个三元组

down:指针域,指向同一列中的下一个三元组

定义结构体

#define ElementType inttypedef struct OLNode
{int row;//1~mint col;//1~nElementType value;struct OLNode *right, *down;//非零元素所在行,列后继链域
}OLNode, *OLink;typedef struct CrossList
{OLink* row_head, *col_head;//行,列链表的头指针向量int m;//行数int n;//列数int len;//非零元素个数
}CrossList;

代码实现

//稀疏矩阵的链式存储结构:十字链表
#include <stdio.h>
#include <stdlib.h>
#include <string.h>#define ElementType inttypedef struct OLNode
{int row;//1~mint col;//1~nElementType value;struct OLNode *right, *down;//非零元素所在行,列后继链域
}OLNode, *OLink;typedef struct CrossList
{OLink* row_head, *col_head;//行,列链表的头指针向量int m;//行数int n;//列数int len;//非零元素个数
}CrossList;void CreatCrossList(CrossList* M, int m, int n, int len)
{M->m = m;M->n = n;M->len = len;if(!(M->row_head = (OLink*)malloc((m + 1) * sizeof(OLink)))){perror("row_head");exit(-1);}if(!(M->col_head = (OLink*)malloc((n + 1) * sizeof(OLink)))){perror("col_head");exit(-1);}memset(M->row_head, 0, (m + 1) * sizeof(OLink));memset(M->col_head, 0, (n + 1) * sizeof(OLink));//初始化为空链表int i = 0, j = 0, e = 0;//结点的行,列,值printf("请输入结点的行号,列号,值:\n");while(len){scanf("%d %d %d", &i, &j, &e);if(i < 1||i > m||j < 1||j > n){printf("行列号不合法,请重新输入:\n");continue;}int flag = 0;//检查位置是否重复for(OLNode* k = M->row_head[i]; k != NULL; k = k->right){if(k->col == j)flag = 1;}if(flag){printf("结点位置重复,请重新输入:\n");continue;}OLNode* p = (OLNode*)malloc(sizeof(OLNode));//申请结点if(p == NULL){perror("Node application");exit(-1);}p->row = i; p->col = j; p->value = e;p->right = p->down = NULL;//行链表插入if(M->row_head[i] == NULL)//该行还没有结点{M->row_head[i] = p;}else if(p->col < M->row_head[i]->col)//新建结点列号<该行首结点列号{p->right = M->row_head[i];M->row_head[i] = p;}else//寻找插入位置{OLNode* q;for(q = M->row_head[i]; q->right && 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 if(p->row < M->col_head[j]->row)//新建结点行号<改行首节点行号{p->down = M->col_head[j];M->col_head[j] = p;}else//寻找插入位置{OLNode* q;for(q = M->col_head[j]; q->down && q->down->row < i; q = q->down)p->down = q->down;p->down = p;}len--;printf("插入成功!\n");printf("请输入结点的行号,列号,值:\n");}}void PrintCrossList(CrossList* M)//打印十字链表
{for(int row = 0; row < M->m; row++){OLNode* p = M->row_head[row];while(p != NULL){printf("(%d, %d)=%d ", p->row, p->col, p->value);p = p->right;}printf("\n");}
}int main()
{CrossList M;CreatCrossList(&M, 10, 10, 10);PrintCrossList(&M);return 0;
}

3、快速转置

在顺序结构里转置

(1)简单转置

矩阵source的列序进行转置

算法实现

SparseMatrix source,dest;
int q = 1;
for(col=1;col<=source.nu;col++)
{for(p=1;p<=source.num;p++){if(source.data[p].col==col){dest.data[q].row = source.data[p].col;dest.data[q].col = source.data[p].row;dest.data[q].e = source.data[p].e;q++;}}}

(2)快速转置

矩阵source的行序进行转置

方法:增加2个辅助向量

这里的cPos[col]为:1 3 5 7 8 8 9

-3一定是第一个,12一定是第三个....

cPos算法:

cPos[1] = 1;
for(col = 2;col <= source.nu; col++)
{cPos[col] = cPos[col-1]+cNum[col-1];
}

cNum算法:

for(col = 1;col <= source.nu; ++col) cNum[col]=0;    //初始化全为0
for(sPos = 1;sPos <= source.number;++sPos)++cNum[source.data[sPos].col];        //就是按列遍历整个三元组,列号为几,就在对应的cNum上++

就是找位置,找到对应位置后,其相应的cPs+1

二、广义表

1、定义

 广义表又称列表,也是一种线性存储结构,通数组类似,广义表中即可存储不可再分的元素也能存储可在分元素。

        例如:数组中可以存储‘a’、3这样的字符或数字,也能存储数组,比如二维数组、三维数组,数组都是可在分成子元素的。广义表也是如此,但与数组不同的是,在广义表中存储的数据是既可以再分也可以不再分的,形如:{1,{1,2,3}}。

    广义表记作:

LS = (a1,a2,…,an)

2、原子和子表

    广义表中存储的单个元素称为 "原子",而存储的广义表称为 "子表"。

例如创建一个广义表 LS = {1,{1,2,3}},我们可以这样解释此广义表的构成:广义表 LS 存储了一个原子 1 和子表 {1,2,3}。

以下是广义表存储数据的一些常用形式:

  • A = ():A 表示一个广义表,只不过表是空的。
  • B = (e):广义表 B 中只有一个原子 e。
  • C = (a,(b,c,d)) :广义表 C 中有两个元素,原子 a 和子表 (b,c,d)。
  • D = (A,B,C):广义表 D 中存有 3 个子表,分别是A、B和C。这种表示方式等同于 D = ((),(e),(b,c,d)) 。
  • E = (a,E):广义表 E 中有两个元素,原子 a 和它本身。这是一个递归广义表,等同于:E = (a,(a,(a,…)))。


注意,A = () 和 A = (()) 是不一样的。前者是空表,而后者是包含一个子表的广义表,只不过这个子表是空表。

3、表头和表尾

当广义表不是空表时,称第一个数据(原子或子表)为"表头",剩下的数据构成的新广义表为"表尾"。

强调一下,除非广义表为空表,否则广义表一定具有表头和表尾,且广义表的表尾一定是一个广义表。

例如在广义表中 LS={1,{1,2,3},5} 中,表头为原子 1,表尾为子表 {1,2,3} 和原子 5 构成的广义表,即 {{1,2,3},5}。

再比如,在广义表 LS = {1} 中,表头为原子 1 ,但由于广义表中无表尾元素,因此该表的表尾是一个空表,用 {} 表示。

4、广义表的存储结构

使用顺序表实现广义表结构,不仅需要操作 n 维数组(例如 {1,{2,{3,4}}} 就需要使用三维数组存储),还会造成存储空间的浪费。

typedef struct GLNode{int tag;//标志域union{char atom;//原子结点的值域struct{struct GLNode * hp,*tp;}ptr;//子表结点的指针域,hp指向表头;tp指向表尾}subNode;
}*Glist;

 这里用到了 union 共用体,因为同一时间此节点不是原子节点就是子表节点,当表示原子节点时,就使用 atom 变量;反之则使用 ptr 结构体。

Glist creatGlist(Glist C) {//广义表CC = (Glist)malloc(sizeof(Glist));C->tag = 1;//表头原子‘a’C->subNode.ptr.hp = (Glist)malloc(sizeof(Glist));C->subNode.ptr.hp->tag = 0;C->subNode.ptr.hp->subNode.atom = 'a';//表尾子表(b,c,d),是一个整体C->subNode.ptr.tp = (Glist)malloc(sizeof(Glist));C->subNode.ptr.tp->tag = 1;C->subNode.ptr.tp->subNode.ptr.hp = (Glist)malloc(sizeof(Glist));C->subNode.ptr.tp->subNode.ptr.tp = NULL;//开始存放下一个数据元素(b,c,d),表头为‘b’,表尾为(c,d)C->subNode.ptr.tp->subNode.ptr.hp->tag = 1;C->subNode.ptr.tp->subNode.ptr.hp->subNode.ptr.hp = (Glist)malloc(sizeof(Glist));C->subNode.ptr.tp->subNode.ptr.hp->subNode.ptr.hp->tag = 0;C->subNode.ptr.tp->subNode.ptr.hp->subNode.ptr.hp->subNode.atom = 'b';C->subNode.ptr.tp->subNode.ptr.hp->subNode.ptr.tp = (Glist)malloc(sizeof(Glist));//存放子表(c,d),表头为c,表尾为dC->subNode.ptr.tp->subNode.ptr.hp->subNode.ptr.tp->tag = 1;C->subNode.ptr.tp->subNode.ptr.hp->subNode.ptr.tp->subNode.ptr.hp = (Glist)malloc(sizeof(Glist));C->subNode.ptr.tp->subNode.ptr.hp->subNode.ptr.tp->subNode.ptr.hp->tag = 0;C->subNode.ptr.tp->subNode.ptr.hp->subNode.ptr.tp->subNode.ptr.hp->subNode.atom = 'c';C->subNode.ptr.tp->subNode.ptr.hp->subNode.ptr.tp->subNode.ptr.tp = (Glist)malloc(sizeof(Glist));//存放表尾dC->subNode.ptr.tp->subNode.ptr.hp->subNode.ptr.tp->subNode.ptr.tp->tag = 1;C->subNode.ptr.tp->subNode.ptr.hp->subNode.ptr.tp->subNode.ptr.tp->subNode.ptr.hp = (Glist)malloc(sizeof(Glist));C->subNode.ptr.tp->subNode.ptr.hp->subNode.ptr.tp->subNode.ptr.tp->subNode.ptr.hp->tag = 0;C->subNode.ptr.tp->subNode.ptr.hp->subNode.ptr.tp->subNode.ptr.tp->subNode.ptr.hp->subNode.atom = 'd';C->subNode.ptr.tp->subNode.ptr.hp->subNode.ptr.tp->subNode.ptr.tp->subNode.ptr.tp = NULL;return C;
}

4、长度 

 广义表的长度,指的是广义表中所包含的数据元素的个数。
由于广义表中可以同时存储原子和子表两种类型的数据,因此在计算广义表的长度时规定,广义表中存储的每个原子算作一个数据,同样每个子表也只算作是一个数据。
例如,在广义表 {a,{b,c,d}} 中,它包含一个原子和一个子表,因此该广义表的长度为 2。
再比如,广义表 {{a,b,c}} 中只有一个子表 {a,b,c},因此它的长度为 1。
前面我们用 LS={a1,a2,...,an} 来表示一个广义表,其中每个 ai 都可用来表示一个原子或子表,其实它还可以表示广义表 LS 的长度为 n。

广义表规定,空表 {} 的长度为 0。

 5、深度

由于这个不做考试内容,分享一个博客

数据结构-广义表-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/qq_51701007/article/details/126352030?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522171894059816800211521274%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=171894059816800211521274&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_positive~default-1-126352030-null-null.142%5Ev100%5Epc_search_result_base5&utm_term=%E5%B9%BF%E4%B9%89%E8%A1%A8&spm=1018.2226.3001.4187

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/31412.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【机器学习】必会降维算法之:随机投影(Random Projection)

随机投影&#xff08;Random Projection&#xff09; 1、引言2、随机投影&#xff08;Random Projection&#xff09;2.1 定义2.2 核心原理2.3 应用场景2.4 实现方式2.5 算法公式2.6 代码示例 3、总结 1、引言 小屌丝&#xff1a;鱼哥&#xff0c;降维算法还没讲完呢。 小鱼&a…

浙江保融科技2025实习生校招校招笔试分享

笔试算法题一共是有4道&#xff0c;第一道是手搓模拟实现一个ArrayList&#xff0c;第二道是判断字符串是否回文&#xff0c;第三道是用代码实现1到2种设计模式。 目录 一.模拟实现ArrayList 二.判断字符串是否回文 ▐ 解法一 ▐ 解法二 ▐ 解法三 三.代码实现设计模式 一…

网络协议安全:TCP/IP协议栈的安全问题和解决方案

「作者简介」:北京冬奥会网络安全中国代表队,CSDN Top100,就职奇安信多年,以实战工作为基础对安全知识体系进行总结与归纳,著作适用于快速入门的 《网络安全自学教程》,内容涵盖Web安全、系统安全等12个知识域的一百多个知识点,持续更新。 这一章节我们需要知道TCP/IP每…

大模型扫盲系列——大模型实用技术介绍_大模型底层技术是哪些

Gemma模型架构和参数计算 近期&#xff0c;大模型相关的技术和应用层出不穷&#xff0c;各个方向的论文百花齐放&#xff0c;底层的核心技术是大家公认的精华部分。本文从技术的角度聚焦大模型的实战经验&#xff0c;总结大模型从业者关注的具体方向以及相关发展&#xff0c;帮…

干货 | 如何进行群体DNA甲基化分析

目前&#xff0c;针对群体的研究基本上还是以重测序为主&#xff0c;基于对遗传多样性丰富的自然群体中的个体进行全基因组重测序&#xff0c;研究物种遗传进化多样性&#xff0c;结合准确的目标性状的表型数据及统计方法进行全基因组关联分析&#xff0c;可对动植物复杂农艺性…

网络性能测试工具 iperf

简介 Iperf 是一个网络性能测试工具&#xff0c;可以测试最大TCP和UDP带宽性能&#xff0c;具有多种参数和UDP特性&#xff0c;可以根据需要调整&#xff0c;可以报告带宽、延迟抖动和数据包丢失。 官网地址&#xff1a;iPerf - Download iPerf3 and original iPerf pre-comp…

shell 脚本批量导入、导出docker images

目录&#xff1a; 需要将运行中的docker 容器批量出&#xff0c;并在新的主机上批量导入。 创建批量导出脚本 export_docker_images.sh #!/bin/bash# 检查 Docker 是否在运行 if ! docker info > /dev/null 2>&1; thenecho "Docker 似乎没有运行。请确保 Dock…

MEME使用-motif分析(生物信息学工具-24)

01 背景 Motif分析是一种在生物信息学和计算生物学中广泛应用的技术&#xff0c;用于识别DNA、RNA或蛋白质序列中具有生物学功能的短保守序列模式&#xff08;motif&#xff09;。这些motif通常与特定的生物学功能相关&#xff0c;如DNA中的转录因子结合位点、RNA中的剪接位点…

最新Springboot小程序医院核酸检测服务系统

采用技术 最新Springboot小程序医院核酸检测服务系统的设计与实现~ 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringBootMyBatis 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 页面展示效果 管理员页面 医护人员管理 普通管理员管理 接种进…

稳定安全生产设备日志采集工具

免费试用下载: Gitee下载 最新版本 优势: A. 开箱即用. 解压直接运行.不需额外安装. B. 批管理设备. 设备配置均在后台管理. C. 无人值守 客户端自启动,自更新. D. 稳定安全. 架构简单,内存占用小,通过授权访问.

vector oj题 和 位运算

知识点1&#xff1a; lowbit(x) 简介&#xff1a;众所周知&#xff0c;lowbit()操作是算法竞赛中的高级技巧&#xff0c;特别是高级数据结构&#xff0c;线段树的核心&#xff0c;还有什么二进制与位运算题目&#xff0c;而本文就用最通俗易懂的话&#xff0c;来教会大家lowbi…

mysql手工命令备份与自动备份

1、手工备份数据库数据 进入命令目录&#xff1a;cd /usr/local/mysql/bin&#xff0c;确保该目录下有mysqldump 然后在命令行 输入 mysqldump -u root -p dbname > dbname_backup_20240612.sql 注意&#xff0c;这里的dbname 是指你想要备份的数据库的名字。 通过这个命…

go-zero 安装

1.安装goctl 工具 go install github.com/zeromicro/go-zero/tools/goctllatest 2.安装protoc goctl env check --install --verbose --force 3.创建项目总目录 gozero_stusy &#xff08;此目录为总目录&#xff0c;下面有多个服务&#xff0c;每个服务一个目录 比如 use…

【MySQL系列】MySQL 字符集的演变与选择

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

[信号与系统]有关滤波器的一些知识背景

前言 最近在看FIR和IIR&#xff0c;本文作为前置&#xff0c;需要在理解这两种滤波器之前阅读。 本文内容会详细讲述一下有关滤波器的技术要求。 选频滤波器的频率响应 选频滤波器&#xff08;Selective Frequency Filter&#xff09;的频率响应是描述该滤波器在不同频率下…

配置yum本地源脚本

在应对无网络或者其它特殊情况下&#xff0c;使用本地yum源 ①查看Linux系统版本&#xff0c;不同的版本yum仓库的挂载路径有所不同 > 以repodata所在目录为准 ②将光盘挂载到手动挂载点/mnt下面 ③将原有yum源仓库进行迁移备份 ④配置本地yum源仓库信息并初始化 #!/us…

中国企业数字化转型现状、趋势和挑战

一、来自不同行业、不同所有制的145家企业的调查 为了了解中国企业数字化转型的现状、趋势和挑战&#xff0c;2022年我们完成了一次在线问卷调查。 受访企业达145家&#xff0c;国内企业111家&#xff0c;占比77%&#xff08;其中央企占总比例51%&#xff09;&#xff0c;民营…

【数据结构与算法(C语言)】离散事件模拟- 单链表和队列的混合实际应用

目录 1. 前言2. 流程图3. 数据结构3.1 单链表3.2 链式队列 4. 核心函数4.1 银行业务模拟 void BankSimulation()4.2 初始化 void OpenForDay()4.3 客户到达 void CustomerArrived(Event en)4.4 客户离开 void CustomerArrived(Event en) 5. 非核心函数5.1 新建客户 NewCustomer…

滑动窗口(单调栈)

239. 滑动窗口最大值 给你一个整数数组 nums&#xff0c;有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。 返回 滑动窗口中的最大值 。 示例 1&#xff1a; 输入&#xff1a;nums [1,3,-1…

Redis精要

一、什么是缓存击穿、缓存穿透、缓存雪崩&#xff1f; 缓存穿透 【针对大量非法访问的请求&#xff0c;缓存中没有&#xff0c;直接访问DB】 缓存穿透指的查询缓存和数据库中都不存在的数据&#xff0c;这样每次请求直接打到数据库&#xff0c;就好像缓存不存在 一样。 对于系…