【数据结构】串,数组,广义表 | 笔记整理 | C/C++实现

文章目录

  • 前言
  • 一、串
    • 1.1、串的定义
    • 1.2、案例引入
    • 1.3、串的类型定义和存储结构
    • 1.4、串的模式匹配算法
      • 1.4.1、BF算法
      • 1.4.2、KMP算法
  • 二、数组
    • 2.1、数组的定义
    • 2.2、数组的抽象数据类型定义
    • 2.3、数组的顺序存储
    • 2.4、特殊矩阵的压缩存储
  • 三、广义表
  • 四、病毒案例


前言

参考视频:数据结构与算法基础(青岛大学-王卓)
KMP算法之求next数组代码讲解

一、串

回忆一下 之前学习的线性结构元素都是一一对应的关系。
栈和队列是操作受限的线性表
字符串依然是线性结构
不过和前面学习的顺序表,栈,队列,有一个不一样的地方,就是限定了元素:只能是字符,而顺序表,栈,队列可以有其他元素。
插一句,广义表这些可以看成是线性表的推广,但是严格意义上已经不是线性表了。
在这里插入图片描述

1.1、串的定义

字符串的定义:零个或多个任意字符组成的有限序列
定义部分还有串名,串值,串长。串长为0时就是空串。
在这里插入图片描述
字串:串中任意个连续字符组成的子序列(包括空串)称为该串的字串
真子串:不包含自身的子串
注意,空串也是子串

在这里插入图片描述主串:包含子串的串叫主串
字符位置:字符在序列中的序号成为该字符在串中的位置
子串位置:子串第一个字符在串中的位置
空格串:由一个或多个空格组成的串(与空串的概念不同哈

来个PPT中的例题看看:(这里位置默认是从1开始
在这里插入图片描述

注意最后一个小问PPT里写错了:应该是b在d中的位置是5

串相等:长度相等,且各个对应位置的字符都相同,才是相等
所有的空串都是相等的

1.2、案例引入

案例一:病毒是否出现(病毒相当于就是子串,检测是否在主串中出现)
在这里插入图片描述
在这里插入图片描述
患者1看起来好像没感染,是因为忽视了病毒是环状这个前提条件
baa还可以是aab aba 这样就能匹配上了
当然像我视力比较好的情况 我一眼也看出来倒数两个字符和第一个字符组成了病毒串

这种案例引入的问题有:一个字符串是否在另外一个字符串中出现过?如果出现过,出现的位置在哪里?这就是字符串的匹配问题

1.3、串的类型定义和存储结构

字符串的数据关系依然是这种前驱后继的关系:一对一的关系
在这里插入图片描述
字符串依然有顺序存储结构和链式存储结构:(逻辑结构是线性结构)
在这里插入图片描述
字符串的顺序存储结构:
在这里插入图片描述
这里字符串和前面线性表不一样的地方是:线性表定义中的元素类型没有限定死,可以是其他类型的(比如int)

字符串的链式存储结构:
在这里插入图片描述
链式存储的存储密度是非常低的,对于一个结点来说,一个字符占的是1个字节,指针占4个字节(32位系统),这样存储密度就是20%

解决方案:可以把多个字符放到同一个结点中:
在这里插入图片描述
对于一个结点来说,一个字符占的是1个字节,图中是4个,就是4个字节,指针占4个字节(32位系统),这样存储密度就是50%。通常称这些连在一起的字符叫做。块中可以多放一些元素,这样存储密度就上去了。

字符串的链式存储结构常用块链结构
在这里插入图片描述
实际情况里,字符串的顺序存储我们用得更多一些,字符串中的字符插入删除比较少,更多还是匹配操作等。后面的内容也是基本围绕着顺序存储结构来的。

1.4、串的模式匹配算法

算法:确定主串中所含有的子串(模式串)第一次出现的位置(定位)
应用举例:查找某个文章里是否出现关键字
在这里插入图片描述

有两种经典的算法:BF和KMP
在这里插入图片描述

1.4.1、BF算法

称为简单匹配法。思路是穷举法
算法思路:从正文串第一个字符开始依次和模式串进行匹配

BF算法的图解如下所示:
在这里插入图片描述
在这里插入图片描述

BF算法的C++实现:

int index_BF(SString Source,SString Target){//Source是主串,Target是子串int i,j=1;while(i<=Source.length&&j<=Target.length){if(Source.ch[i]==Target.ch[j]){//匹配成功,开始匹配下一个字符++i;++j;}else{//匹配失败,时空回溯i=i-j+2;//i-j+1+1(前面+1是因为字符串默认从1开始,没有0)j=1;//退回子串的原点}}if(j>=Target.length){return i-Target.length;}else{return 0;}
}

完整运行代码如下:

#include<iostream>
#include<string.h>
using namespace std;
#define MAXLEN 255
typedef struct{char ch[MAXLEN+1];int length;
}SString;int index_BF(SString Source,SString Target){//Source是主串,Target是子串int i,j=1;while(i<=Source.length&&j<=Target.length){if(Source.ch[i]==Target.ch[j]){//匹配成功,开始匹配下一个字符++i;++j;}else{//匹配失败,时空回溯i=i-j+2;//i-j+1+1(前面+1是因为字符串默认从1开始,没有0)j=1;//退回子串的原点}}if(j>=Target.length){return i-Target.length;}else{return 0;}
}int main(){SString mainStr, subStr;int ret;// 初始化主串cout << "请输入主串:";cin >> (mainStr.ch + 1);  // 从位置1开始存储mainStr.length = strlen(mainStr.ch + 1);// 初始化子串cout << "请输入子串:";cin >> (subStr.ch + 1);  // 从位置1开始存储subStr.length = strlen(subStr.ch + 1);ret=index_BF(mainStr,subStr);if(ret==0){cout<<"字符串匹配失败!"<<endl;}else{cout<<"字符串匹配成功,字符开始的位置是:"<<ret<<endl;}
}

注意,我们这里cin >> (mainStr.ch + 1); 是从位置1开始存储,而不是位置0
我们试验一下:
在这里插入图片描述

BF算法的时间复杂度:
在这里插入图片描述

最好情况下是比较m次(第一次比较就找到了),为O(m)
最坏情况下是比较了(n-m+1)*m次(找到最后一组再找到,甚至没找到),O(mn)
时间复杂度平均下来就是O(mn)/2,依然是O(mn)

1.4.2、KMP算法

KMP算法的核心就是i和j的回溯方式不同:
1)主串的指针i不需要回溯
2)模式串的指针j回溯有一套自己的规则:查看next[j]数组:next数组也就是模式串与主串“失配”时,模式串中重新和主串开始比较的初始位置

这样能提速到O(m+n)

而里面最难的就是如何找j应该回到哪个位置,也就是求next[j]数组,以下是求它的规则:
在这里插入图片描述
这里的第一条规则很难懂,我也看不懂,我写了两条笔记如下,比较形象:
请添加图片描述

请添加图片描述

然后是实现KMP函数的C++代码:

void get_next(SString Target,int next[]){int i=1;int j=0;next[1]=0;while(i<=Target.length){if(j==0||Target.ch[i]==Target.ch[j]){++i;++j;next[i]=j;}else{j=next[j];}  }
}
int KMP(SString Source,SString Target,int next[]){//Source是主串,Target是子串int i=1;int j=1;while(i<=Source.length&&j<=Target.length){if(j==0||Source.ch[i]==Target.ch[j]){//匹配成功,开始匹配下一个字符++i;++j;}else{//匹配失败,查看next[j]数组进行回溯,i不用回溯j=next[j];//退回子串的原点}}if(j>=Target.length){return i-Target.length;}else{return 0;}
}

然而这里的求next数组的函数其实非常难去理解。

实现的完整代码:

#include<iostream>
#include<string.h>
using namespace std;
#define MAXLEN 255
typedef struct{char ch[MAXLEN+1];int length;
}SString;
void get_next(SString Target,int next[]){int i=1;int j=0;next[1]=0;while(i<=Target.length){if(j==0||Target.ch[i]==Target.ch[j]){++i;++j;next[i]=j;}else{j=next[j];}  }
}
int KMP(SString Source,SString Target,int next[]){//Source是主串,Target是子串int i=1;int j=1;while(i<=Source.length&&j<=Target.length){if(j==0||Source.ch[i]==Target.ch[j]){//匹配成功,开始匹配下一个字符++i;++j;}else{//匹配失败,查看next[j]数组进行回溯,i不用回溯j=next[j];//退回子串的原点}}if(j>=Target.length){return i-Target.length;}else{return 0;}
}int main(){SString mainStr, subStr;int ret;int next[1024];// 初始化主串cout << "请输入主串:";cin >> (mainStr.ch + 1);  // 从位置1开始存储mainStr.length = strlen(mainStr.ch + 1);// 初始化子串cout << "请输入子串:";cin >> (subStr.ch + 1);  // 从位置1开始存储subStr.length = strlen(subStr.ch + 1);get_next(subStr,next);ret=KMP(mainStr,subStr,next);if(ret==0){cout<<"字符串匹配失败!"<<endl;}else{cout<<"字符串匹配成功,字符开始的位置是:"<<ret<<endl;}
}

二、数组

2.1、数组的定义

数组定义:按照一定格式排列起来的,具有相同类型的数据元素的集合

一位数组的定义:若线性表中的数据元素为非结构的简单元素,则为一维数组。

一维数组的逻辑结构:线性结构,定长的线性表

二维数组既可以看作非线性结构也可以看作线性结构
1)线性结构:二维数组中的每一个元素都可以看作是定长的线性表
2)非线性结构:二维数组中的每一个元素既在一个行表中也在一个列表中(不止有一个前驱和一个后继:从行看,有一个前驱后继,从列看,也有一个前驱后继,这就不是一对一的关系了):
在这里插入图片描述
我们可以把数组看成是一种特殊的线性结构,是线性结构的扩展。
二维数组的几种定义方式:(有一种是套娃,先定义列再定义行)
在这里插入图片描述
关于三维和更高维度:
三维数组:二维数组中的元素又是一个一维数组,那么这就叫做三维数组。
n维数组:n-1维数组中的元素又是一个一维数组,那么这就叫做n维数组。

数组和线性表的关系:
线性表结构是数组结构的一个特例,数组结构是线性表结构的扩展。

数组特点:
结构是固定的,定义之后,维数和维界(每一维的长度是多少)不会再改变

数组操作:
数组结构是固定的,所以一般没有插入和删除的操作。一般只有初始化,销毁,修改元素等操作。

2.2、数组的抽象数据类型定义

代码如下(示例):

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
warnings.filterwarnings('ignore')
import  ssl
ssl._create_default_https_context = ssl._create_unverified_context

2.3、数组的顺序存储

数组很少使用链式存储结构,也不插入和删除运算(因为结构固定)
数组一般使用顺序存储结构
数组是多维的,但是存储元素的内存单元地址一维的,在存储之前,需要将多维映射到一维

一维数组的存储运算:
在这里插入图片描述
(这里a+i*L为什么不是(i-1)呢,因为i是从0开始的)

二维数组的存储运算:
一共两种存储方式:
1)行优先(java,c等):先把行存满
2)列优先:先把列存满
在这里插入图片描述
行优先:
在这里插入图片描述
列优先:
在这里插入图片描述
所以最终二维数组的存储计算方式
在这里插入图片描述
三维数组的存储运算:
在这里插入图片描述

在这里插入图片描述
n维数组的存储运算:
在这里插入图片描述
例题:
在这里插入图片描述
2n+2+644=676
n=15
A[3][3]放在 644+3*15+3=692

2.4、特殊矩阵的压缩存储

矩阵:一个m*n元素排成的m行n列的表
矩阵的常规存储: 用二维数组

用数组来存储矩阵(常规存储) 的好处:
1)可以随机存取
2)矩阵运算很方便:一般就是存取
3)存储密度为1(不需要存储其他东西,比如地址等)

不适合常规存储的矩阵:
1)零元素多
2)值相同的元素很多且呈某种规律
这样常规存储太浪费空间,这里我们就可以考虑矩阵的压缩存储了。
可以压缩存储的矩阵:对称矩阵,对角矩阵,三角矩阵,稀疏矩阵

1)对称矩阵:(只存上三角或者下三角)
在这里插入图片描述
我们一般是拿一位数组去存储这个对称矩阵的,那一般是怎么算的呢:
这里以下图的an1为例子:
在这里插入图片描述
(1+(n-1)) (n-1)/2 = n*(n-1)/2 就是用高斯公式求就行了
2)三角矩阵:
三角矩阵定义:对角线以下或者以上部分的数据元素全部为常数C(这时我们不用把所有元素都存起来)

这里的存储方式和前面堆对称矩阵很像,也是把所有元素放到一个一位数组中。

存储方式:
在这里插入图片描述
3)对角矩阵:
在这里插入图片描述
在这里插入图片描述
存储方法:先把对角线的元素先存起来,然后再存其它的,这里从36个元素压缩到了30个元素,这里是五对角矩阵,压缩的情况还不是很明显,如果换成三对角还是什么就明显了

4)稀疏矩阵:
稀疏矩阵就是0特别多的矩阵
在这里插入图片描述

超过95%的元素都是0。

稀疏矩阵可以通过三元组的方式来存储:(三元组就是i,j,aij)
在这里插入图片描述
这里例子中的矩阵存储密度是40%左右,其实看起来存储的还挺多的,但是我们还是能看出来有很大的浪费了

三元组法:一般还需要在第0行存储总行数,总列数,非零元素个数(三元组法一般又称为有序的双下标法
在这里插入图片描述
同理我们可以根据三元组还原出稀疏矩阵:在这里插入图片描述
三元组的优缺点:
1)优点:非零元素是按行存储,便于依行进行处理的矩阵运算
2)缺点:不能随机存取,必须从头开始
这里的缺点其实是可以克服的,所以有了十字链表

5)十字链表
三元组表的插入和删除是比较麻烦的
但是十字链表的插入和删除就比较简单

十字链表有五个部分:
在这里插入图片描述
十字链表的例子:(因为有十字交叉,所以被成为十字链表)
在这里插入图片描述
在这里插入图片描述

三、广义表

广义表是一个递归定义:用自己定义自己
在这里插入图片描述
广义表也是线性表的推广,和线性表不同的是,线性表中的元素类型都是一致的,广义表不一定。

举例子:
在这里插入图片描述
注意广义表的表尾和线性表的表尾有点不太一样
在这里插入图片描述
一般用大写字母表示广义表,小写字母表示原子,下面是一个例子:
在这里插入图片描述
(5)是共享广义表
(6)是递归广义表

广义表的性质:
1)广义表的元素有顺序,一个直接前驱和一个直接后继
2)广义表的长度
3)广义表的深度
原子的深度为0,空表的深度为1
4)广义表的共享
5)广义表的递归
6)广义表的多层次结构
在这里插入图片描述
(5)这里长度为2,深度无穷
在这里插入图片描述
广义表和线性表的区别:
广义表是线性表的推广,线性表是广义表的特例
在这里插入图片描述
广义表的基本运算:
在这里插入图片描述
广义表的存储:
没法用数组存储(广义表的元素不是一样大小)
一般用链式存储

在这里插入图片描述

四、病毒案例

这个案例的特殊点在于病毒是环状的,我们的模式串就有很多种可能
案例实现:(我们生成多一倍存储空间的2m,进行扫描检测,每次扫m个数据,这样就行了)
在这里插入图片描述

在这里插入图片描述

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

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

相关文章

【C++】wxWidgets库实现窗体程序

一、安装wxWidgets库 在Debian系统上使用wxWidgets库来创建一个基本的窗体程序&#xff0c;首先需要确保已经安装了wxWidgets相关的库和开发工具。下面是安装wxWidgets的步骤&#xff1a; 打开终端&#xff0c;使用下述命令安装wxWidgets库及其开发文件&#xff1a; sudo ap…

MySQL之导入、导出远程备份

一、Navicat工具导入、导出 1.1 导入 第一步&#xff1a; 右键&#xff0c;点击运行SQL文件 第二步&#xff1a; 选择要运行的SQL&#xff0c;点击开始 第三步&#xff1a; 关闭即可 1.2 导出 第一步&#xff1a; 右键选择&#xff0c;导出向导 第二步&#xff1a; 选择SQL脚…

1.3MATLAB变量及其操作

变量 变量是内存单元的一个抽象&#xff0c;在MATLAB中&#xff0c;变量以字母开头&#xff0c;后接数字下划线构成&#xff0c;MATLAB中变量名最多占据 63 个字符。变量区分大小写标准函数及命令一般使用小写字母 赋值语句 变量 表达式(;)表达式(;)总结&#xff1a;加分号&…

C++ 实现游戏(例如MC)键位显示

效果&#xff1a; 是不是有那味儿了&#xff1f; 显示AWSD&#xff0c;空格&#xff0c;Shift和左右键的按键情况以及左右键的CPS。 彩虹色轮廓&#xff0c;黑白填充。具有任务栏图标&#xff0c;可以随时关闭字体是Minecraft AE Pixel&#xff0c;如果你没有装&#xff08;大…

使用numpy处理图片——灰阶影像

大纲 载入图像灰阶处理lightnessaverageluminosity 灰阶&#xff08;Gray scale&#xff09;影像是每个像素只有一个采样颜色的图像。 载入图像 import numpy as np import PIL.Image as Imageimg Image.open(lena.png) data np.array(img)灰阶处理 我们有三种方法来生成这…

Linux中常使用的命令之ls、cd、pwd、mkdir、rmdir

ls: 列出目录 cd&#xff1a;切换目录 pwd&#xff1a;显示目前的目录 mkdir&#xff1a;创建一个新的目录 -m &#xff1a;配置文件的权限-p &#xff1a;帮助你直接将所需要的目录(包含上一级目录)递归创建起来&#xff01; rmdir&#xff1a;删除一个空的目录 注意这…

基于springboot时间管理系统源码和论文

在Internet高速发展的今天&#xff0c;我们生活的各个领域都涉及到计算机的应用&#xff0c;其中包括时间管理系统的网络应用&#xff0c;在外国时间管理系统已经是很普遍的方式&#xff0c;不过国内的管理系统可能还处于起步阶段。时间管理系统具有时间管理功能的选择。时间管…

Unity Shader 属性的定义

Unity Shader 属性的定义 什么是材质球 人的衣服 什么是shader 决定材质跟灯光的作用 Property 若是把shader看作class&#xff0c;那么Property就可以看成成员变量 属性定义的通用格式 Properites{ Property[Property…] } ep:定义一个int&#xff1a; name("dis…

ODBC 在指定的DSN中,驱动程序和应用程序之间的体系结构不匹配

常规办法就是64位或32位匹配&#xff0c;如果解决不了&#xff0c;往下看。 ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓解决方案↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ 解压AccessDatabaseEngine_X64.exe&#xf…

为什么选择Go语言编写网络应用程序

关注公众号【爱发白日梦的后端】分享技术干货、读书笔记、开源项目、实战经验、高效开发工具等&#xff0c;您的关注将是我的更新动力&#xff01; 作为一名后端开发者&#xff0c;你一定对选择合适的编程语言来编写网络应用程序非常重视。在众多的编程语言中&#xff0c;Go语言…

我在代码随想录|写代码Day7之454.四数相加II ,​ 383. 赎金信​,​ 15. 三数之和​

454.四数相加II 题目 解题思路 四个数字相加的和为0,我们要选俩数组,让他们的笛卡尔积储存在哈希表中,然后我们要找的是这俩数和的相反数,然后就是将后面俩数组相加在后面的数组和中找相反数. 383. 赎金信 解题思路 题目意思是让在字符串1中找到字母组成字符串2所以找字符串1…

【无标题】关于异常处理容易犯的错

一般项目是方法打上 try…catch…捕获所有异常记录日志&#xff0c;有些会使用 AOP 来进行类似的“统一异常处理”。 其实&#xff0c;这种处理异常的方式非常不可取。那么今天&#xff0c;我就和你分享下不可取的原因、与异常处理相关的坑和最佳实践。 捕获和处理异常容易犯…

(南京观海微电子)——色温介绍

色温是表示光线中包含颜色成分的一个计量单位。从理论上说&#xff0c;黑体温度指绝对黑体从绝对零度&#xff08;&#xff0d;273℃&#xff09;开始加温后所呈现的颜色。黑体在受热后&#xff0c;逐渐由黑变红&#xff0c;转黄&#xff0c;发白&#xff0c;最后发出蓝色光。当…

im6ull学习总结(三-五)freetype显示正行字

知识补充 笛卡尔坐标系 这里笛卡尔坐标系就是初高中学的直角坐标系的第一象限 lcd坐标系则不同 这两个坐标系如何转换 观察两个坐标系 点&#xff08;x,y&#xff09;的x坐标在两个坐标系中相同&#xff0c;纵坐标&#xff08;y&#xff09;存在着yV-yV V是整个屏幕的行数的像…

MYSQL的操作

1.库的操作 1.1创建数据库 语法&#xff1a; CREATE DATABASE [IF NOT EXISTS] db_name [create_specification [, create_specification] ...] create_specification: [DEFAULT] CHARACTER SET charset_name [DEFAULT] COLLATE collation_name 说明&#xff1a; #…

python flask学生管理系统

预览 前端 jquery css html bootstrap: 4.x 后端 python: 3.6.x flask: 2.0.x 数据库 mysql: 5.7 学生管理模块 登录、退出查看个人信息、修改个人信息成绩查询查看已选课程选课、取消选课搜索课程课程列表分页功能 教师模块 登录、退出查看个人信息、修改个人信息录入…

leetcode 每日一题 2024年01月14日 删除排序链表中的重复元素

题目 83. 删除排序链表中的重复元素 给定一个已排序的链表的头 head &#xff0c; 删除所有重复的元素&#xff0c;使每个元素只出现一次 。返回 已排序的链表 。 示例 1&#xff1a; 输入&#xff1a;head [1,1,2] 输出&#xff1a;[1,2]示例 2&#xff1a; 输入&#xff…

解决Unexpected record signature 0X9maven 资源过滤

解决Unexpected record signature: 0X9|maven 资源过滤 记录问题&#xff1a;我们有个需求是根据excel模版导出一个excel表。我们的项目是SpringBoot&#xff0c;所以理所当然的把这个模版文件放到了&#xff0c;resources文件夹中。但是在导出文件的时候却遇到了invalid code …

二极管限幅电路理论分析,工作原理+作用

一、限幅是什么意思&#xff1f; 限幅也就是&#xff0c;将电压限制在某个范围内&#xff0c;去除交流信号的一部分但不会对波形的剩余部分造成影响。通常来说&#xff0c;限幅电路主要是由二极管构成&#xff0c;波形的形状取决于电路的配置和设计。二、限幅电路工作原…

场效应管在电路中如何控制电流大小

场效应管的概念 场效应晶体管&#xff08;FieldEffectTransistor缩写&#xff08;FET&#xff09;&#xff09;简称场效应管。主要有两种类型&#xff08;juncTIonFET—JFET&#xff09;和金属-氧化物半导体场效应管&#xff08;metal-oxidesemiconductorFET&#xff0c;简称M…