【数据结构】串,数组,广义表 | 笔记整理 | 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,一经查实,立即删除!

相关文章

Spring Security实现详解

一、WebSecurityConfigurerAdapter 总配置类&#xff1a; 1、介绍&#xff1a;配置类 2、主要方法&#xff1a; &#xff08;1&#xff09;configure&#xff08;HttpSecurity http&#xff09; protected void configure(HttpSecurity http) throws Exception {this.logge…

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

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

etcd官方docker镜像及dockerfile问题处理

解决下我之前etcd使用docker镜像启动的坑 1、问题镜像docker-file&#xff1a; 这个dockerfile看着看不出来问题&#xff0c;但如果有人真的执行我之前两篇文章的文件&#xff0c;就会有问题&#xff0c;什么问题呢&#xff0c;无法连接到etcd&#xff0c;由于我是刚装上dock…

11k+star 开源笔记应用真香 centos部署教程

leanote binary installation on Mac and Linux (En) life edited this page on Jul 21, 2017 10 revisions Pages 26 Home How to develop leanote 如何开发leanote How to install leanote on Ubuntu? How to Upgrade Leanote Install Mongodb leanote api leanote …

js页面输出的方式

JavaScript 可以通过以下方式在页面中输出内容&#xff1a; 使用 document.write() 方法&#xff0c;将文本字符串直接写入 HTML 文档中。 document.write("Hello World!");使用 innerHTML 属性&#xff0c;向元素的内部插入 HTML 代码。 document.getElementById(&…

开源监控服务一瞥:Prometheus、Grafana、Zabbix、Nagios、Icinga和Open-Falcon

前言 随着信息技术的发展&#xff0c;监控服务在维护系统稳定性和性能方面变得越来越重要。本文将比较一些流行的开源监控服务&#xff0c;以帮助你选择适合你需求的解决方案。 监控服务对比 监控服务特点优势不足性能扩展性安全性Prometheus- 多维度数据模型- 监控容器化环…

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;大…

约瑟夫环问题解决

链表 struct List {int data;struct List* next; }创建链表 单链表 实现 struct List* listCreate() {int data;struct List* head NULL;struct List* pre NULL;struct List* current NULL;while(scanf("%d",&data) && data ! -1){current (stru…

使用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;删除一个空的目录 注意这…

基本数据结构 | 并查集

基本介绍 并查集主要实现两个操作&#xff1a; 合并两个集合查询某个元素的祖宗节点 并查集的两个优化&#xff1a; 路径压缩&#xff1a; O ( l o g n ) O(logn) O(logn)按秩合并&#xff1a; O ( l o g n ) O(logn) O(logn)&#xff0c;代码比较复杂&#xff0c;一般不单…

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

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

基于强化学习的航线规划算法

基于Q-learning的无人机三维路径规划&#xff08;含完整C代码&#xff09;_q-learning 无人机路径规划代码-CSDN博客 基于Q-Learing的路径规划MATLAB仿真系统_强化学习MATLAB资源-CSDN文库

Vue-路由-配置

1. VueRouter 的 使用 (5 2) 参考官网 5个基础步骤 (固定) 下载 VueRouter 模块到当前工程&#xff0c;这里指定版本&#xff1a;3.6.5 yarn add vue-router3.6.5引入 vue-router import VueRouter from vue-router安装注册 Vue.use(VueRouter) // VueRouter插件初始化创…

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…