数据结构——全篇1.1万字保姆级吃透串与数组(超详细)(二)

简介: 数据结构——全篇1.1万字保姆级吃透串与数组(超详细)(二)

4.模式匹配


4.1概述


串的查找定位操作,也称为串的模式匹配操作。


主串:当前串,长度用n表示。


模式串:在主串中需要寻找的子串,长度用m表示。


模式匹配特点:


匹配成功,返回模式串的首字母在主串中的位序号(索引号)。


匹配失败,返回-1


模式匹配的常见算法:


Brute-Force算法:蛮力算法,依次比较每一个,比较次数多,时间复杂度O(n×m)


KMP算法:滑动算法,比较的次数较少,时间复杂度O(n+m)


4.2Brute-Fore算法:分析


第一趟:运行后的结果

微信图片_20220531134504.png

第一趟过渡到第二趟微信图片_20220531134543.png

第二趟不匹配,直接过渡到第三趟微信图片_20220531134615.png

第三趟:  微信图片_20220531134640.png

第三趟过渡到第四趟微信图片_20220531134713.png

总结:核心算法(找主串的下一位)微信图片_20220531134751.png

 4.3Brute-Force算法:算法实现

/** this 主串
* @param t 模式串
* @param start 在主串中开始位置,例如:indexOf_BF("abcabc", 0)
*/
public int indexOf_BF(IString t, int start) {
    // 0.1 非空校验
    if(this == null || t == null) {       //0.1 主串或模式串为空
        return -1;
    }
    // 0.2 范围校验
    if(t.length() == 0 || this.length() < t.length()) { //0.2模式串为空或比主串长
        return -1;
    }
    int i = start , j = 0;            // 1 声明变量
    while( i<this.length() && j<t.length() ) {  // 2 循环比较,主串和模式串都不能超过长度
        if(this.charAt(i) == t.charAt(j)) {   // 2.1 主串和模式串依次比较每一个字符
            i++;
            j++;
        } else {                // 2.2 当前趟过渡到下一趟
            i = i - j + 1;            // 2.3 核心算法:主串中下一字符
            j = 0;                // 2.4 模式串归零
        }
    }
                          // 3 处理结果
    if(j >= t.length()) {           //3.1 模式串已经循环完毕
        return i - t.length();          //3.2 匹配成功,第一个字母的索引号
    } else {
        return -1;                //3.3 匹配失败
    }
}

 4.4KMP算法:动态演示

  • 核心思想:主串的指针i不会回退,通过滑动模式串进行匹配。

            滑动的原则:可以从最大公共前缀,直接跳到最大公共后缀。

  • 思考:ababa 最大公共前后缀是?

          最大公共前缀:==aba==ba

          最大公共后缀:ab==aba==

  • 第一趟:i 从 0-->2微信图片_20220531134949.png

遇到不匹配的数据时,需要移动模式串,当前公共部分是“ab”,没有最大公共前后缀。模式串从头开始  微信图片_20220531134955.png

第二趟:i 从 2 --> 7微信图片_20220531135104.png

  • 遇到不匹配的数据时,需要移动模式串,当前公共部分是“abcab”,有最大公共前后缀

微信图片_20220531135111.png

第三趟: i=7 位置数据不一致微信图片_20220531135208.png

遇到不匹配的数据时,需要移动模式串,当前公共部分是“ab”,没有最大公共前后缀。模式串从头开始  微信图片_20220531135314.png

第4趟:数据不一致,i 7 --> 8 , j 归零  微信图片_20220531135409.png

第五趟:i从8 --> 13

微信图片_20220531135433.png

4.5KMP:求公共前缀next数组--推导

当我们准备求公共前后缀时,主串和模式串具有相同的内容,所以只需要看模式串。


实例1:模式串:"abcabc"


提前将模式进行处理(预判):将每一个字符假设不匹配时,公共前后缀提前记录下来,形成一个表格。


第一个位置:-1


第二个位置:0


使用next数组,记录统计好的表格。微信图片_20220531135542.png

实例2:"ababaaa"微信图片_20220531135620.png

实例3:“ababaab”微信图片_20220531135647.png

4.6KMP算法:求公共前缀next数组--算法演示


实例1:模式串:"abcabc"

微信图片_20220531135829.png

第三位的数值

微信图片_20220531135834.png

第四位的数值微信图片_20220531135942.png

第五位的数值微信图片_20220531140019.png

第六位的数值微信图片_20220531140053.png

处理完成微信图片_20220531140130.png

实例2:"ababaaa"微信图片_20220531140213.png

第三位的值: k == 0微信图片_20220531140220.png

第四位的值:字符相等微信图片_20220531140321.png 第五位的值: 字符相等微信图片_20220531140328.png

第六位的值:字符相等微信图片_20220531140432.png

第七位的值:字符不相等,且k!=0

  • 字符不相等,k!=0,移动k
    微信图片_20220531140437.png 字符不相等,k!=0,再移动k微信图片_20220531141201.png

字符相等微信图片_20220531141209.png

处理完成微信图片_20220531141318.png

4.7KMP算法:求公共前后缀next数组--算法

/** 获得next数组
* @param T 模式串
* return 返回next数组
*/
public int[] getNext(IString T) {
    //1. 创建数组,与模式串字符个数一致
    int[] next = new int[T.length()];     
    int j = 1;                // 串的指针
    int k = 0;                // 模式串的指针(相同字符计数器)
    // 2 默认情况
    next[0] = -1;             
    next[1] = 0;
    // 3 准备比较
    while( j < T.length() -1 ) {      // 比较倒数第二个字符
        if(T.charAt(j) == T.charAt(k)) {  // 连续有字符相等
            next[j+1] = k+1;
            j++;
            k++;
        } else if (k == 0) {
            next[j+1] = 0;
            j++;
        } else {    //k不是零
            k = next[k];        //p119 数学推导
        }
    }
    // 4 处理完成,返回数组
    return next;
}
  • 处理字符相同微信图片_20220531141509.png
  • 处理字符不相等,且k==0


微信图片_20220531141555.png

4.8KMP算法:next数组使用


  • 主串:ababababaaa
  • 模式串:ababaaa
  • next数组
/** this 当前串,也就是主串 (this.length() 主串长度)
* @param T 模式串 (T.length() 模式串长度)
* @param start 从主串中开始位置,例如:"abaababaaa".index_KMP("ababaaa",0);
*/
public int index_KMP(IString T, int start) {
    //1 准备工作:next数组、指针
    int[] next = getNext(T);      //1.1 获得模式的next数组
    int i = start;            //1.2 主串指针
    int j = 0;              //1.3 模式串的指针
    //2 字符比较移动
    while(i<this.length() && j<T.length()) {  //2.1 串小于长度
        if(j == -1 ||               //2.2.1 第一个字符不匹配,直接跳过
              this.charAt(i) == T.charAt(j)) {//2.2.2 字符匹配
            i++;
            j++;
        } else {
            j = next[j];            //2.3 移动模式串
        }
    }
    //3 处理结果
    if(j < T.length()) {        //3.1 移动位置没有模式串长,不匹配
        return -1;
    } else {
        return i - T.length();      //3.2 匹配,目前在串后面,需要移动首字符
    }
}

 4.9KMP算法微信图片_20220531141713.png

/** this 当前串,也就是主串 (this.length() 主串长度)
* @param T 模式串 (T.length() 模式串长度)
* @param start 从主串中开始位置,例如:"abaababaaa".index_KMP("ababaaa",0);
*/
public int index_KMP(IString T, int start) {
    //1 准备工作:next数组、指针
    int[] next = getNext(T);      //1.1 获得模式的next数组
    int i = start;            //1.2 主串指针
    int j = 0;              //1.3 模式串的指针
    //2 字符比较移动
    while(i<this.length() && j<T.length()) {  //2.1 串小于长度
        if(j == -1 ||               //2.2.1 第一个字符不匹配,直接跳过
              this.charAt(i) == T.charAt(j)) {//2.2.2 字符匹配
            i++;
            j++;
        } else {
            j = next[j];            //2.3 移动模式串
        }
    }
    //3 处理结果
    if(j < T.length()) {        //3.1 移动位置没有模式串长,不匹配
        return -1;
    } else {
        return i - T.length();      //3.2 匹配,目前在串后面,需要移动首字符
    }
}

5.数组


5.1概述


数组:一组具有相同数据类型的数据元素的集合。数组元素按某种次序存储在一个地址连续的内存单元空间中。


一维数组:一个顺序存储结构的线性表。[a0,a1,a2, ....]


二维数组:数组元素是一维数组的数组。[ [] , [] , [] ] 。二维数组又称为矩阵。

微信图片_20220531141951.png

 

5.2数组的顺序存储(一维)


多维数组中,存在两种存储方式:


以行序为主序列的存储方式(行优先存储)。大部分程序都是按照行序进行存储的。


以列序为主序列的存储方式(列优先存储)


一维数组内存地址


Loc(0) :数组的首地址


i : 第i个元素


L :每一个数据元素占用字节数

微信图片_20220531142108.png

5.3数组的顺序存储(二维)


 5.3.1行序


行序:使用内存中一维空间(一片连续的存储空间),以行的方式存放二维数组。先存放第一行,在存放第二行,依次类推存放所有行。微信图片_20220531142213.png

二维数组(n×m)内存地址(以==行序==为主序列)

  • Loc(0,0) :二维数组的首地址
  • i : 第i个元素
  • L : 每一个数据元素占用字节数
  • m:矩阵中的列数
    微信图片_20220531142258.png微信图片_20220531142303.png

注意:

  • 如果索引号不是从0开始,不能使用此公式。
  • 如果索引号不是从0开始的,需要先将索引号归零,再使用公式。
    微信图片_20220531142350.png

5.3.2列序


列序:使用内存中一维空间(一片连续的存储空间),以列的方式存放二维数组。先存放第一列,再存放第二列,依次类推,存放所有列微信图片_20220531142357.png

二维数组(n×m)内存地址(以==列序==为主序列)微信图片_20220531142518.png

微信图片_20220531142527.png

5.3.3练习


实例1:


有一个二维数组A[1..6,0..7],每一个数组元素用相邻的6个字节存储,存储器按字节编址,那么这个数组占用的存储空间大小是( ==D== )个字节。


A. 48


B. 96


C. 252


D. 288

微信图片_20220531142715.png

实例2:


设有数组A[1..8,1..10],数组的每个元素占3字节,数组从内存首地址BA开始以==列序==为主顺序存放,则数组元素A[5,8]的存储首地址为( )。


A. BA + 141


B. BA + 180


C. BA + 222


D. BA + 225


A[1..8,1..10]  --> A[8×10]        

微信图片_20220531142814.png

  • 例如3:

设有数组A[0..8,1..10],数组的每个元素占5字节,数组从内存首地址BA开始以==列序==为主顺序存放,则数组元素A[7,8]的存储首地址为( BA + 350 )。

A[0..8,1..10]   --> A[9×10]微信图片_20220531142911.png

5.4特殊矩阵概述


特殊矩阵:具有相同的数据或0元素,且数据分布具有一定规律。


分类:


对称矩阵


三级矩阵


对角矩阵


特殊矩阵只有部分有数据,其他内容为零,使用内存中一维空间(一片连续的存储空间)进行存储时,零元素没有必要进行存储,通常都需要进行压缩存储。


压缩存储:多个值相同的矩阵元素分配同一个存储空间,零元素不分配存储空间。

  • 存储有效数据,零元素和无效数据不需要存储。
  • 不同的举证,有效和无效定义不同。


5.5对称矩阵压缩存储


5.5.1定义及其压缩方式


什么是对称矩阵:a(i,j) = a(j,i)微信图片_20220531143219.png

对称矩阵的压缩方式:共4种

下三角部分以行序为主序存储的压缩【学习,掌握】

微信图片_20220531143153.png

下三角部分以列序为主序存储的压缩

微信图片_20220531143201.png

上三角部分以行序为主序存储的压缩

微信图片_20220531143408.png

上三角部分以列序为主序存储的压缩

微信图片_20220531143450.png

n×n对称矩阵压缩 n (n+1) / 2 个元素,求 1+2+3+...+n的和,只需要计算三角中的数据即可

微信图片_20220531143552.png

5.5.2压缩存放及其公式


压缩后存放到一维空间(连续的存放空间中)

微信图片_20220531143643.png

对称矩形 A(i,j) 对应 一维数组 s[k] , k与i和j 公式微信图片_20220531143648.png

5.5.3练习


练习1:微信图片_20220531143831.png


a(8,5)  -->索引库1,1表示方式

需要将1,1转化成0,0方式,从而可以使用公式,i和j同时-1

a(7,4)  -->索引库0,0表示方式

因为:i >= j

k= i(i+1)/2 +j = 7 * 8 / 2 + 4 = 32

32为索引为0的一维数组的下标

数据b下标是从1开始,对应的下标 32+1=33


练习2:

微信图片_20220531143922.png

b[13] 下标从1开始,归零

b[12] 下标从0开始,k=12

i*(i+1)/2 , 如果i=4,结果为10

12-10 = j

下标0,0时,a(4,2)

下标1,1时,a(5,3)


5.6三角矩阵


5.6.1概述&存储方式


三角矩阵分为:上三角矩阵、下三角矩阵

上三角矩阵:主对角线(不含主对角线)下方的元素值均为0。只在上三角的位置进行数据存储

微信图片_20220531144122.png

下三角矩阵:主对角线(不含主对角线)上方的元素值均为0。只在下三角的位置进行数据存储


微信图片_20220531144248.png

存储方式:三角矩阵的存放方式,与对称矩阵的存放方式相同。


5.6.2上三角矩阵


上三角矩阵实例微信图片_20220531144417.png

上三角矩阵对应一维数组存放下标,计算公式  微信图片_20220531144433.png


5.6.3下三角矩阵


下三角矩阵实例`微信图片_20220531144854.png

下三角矩阵对应一维数组存放下标,计算公SH式微信图片_20220531144903.png

 5.7对角矩阵


 5.7.1定义&名词


对角矩阵:矩阵的所有非零元素都集中在以主对角线为中心的带状区域中,即除主对角线上和直接在主对角线上、下方若干条对角线上的元素之外,其余元素皆为零。

微信图片_20220531145043.png

名词:


半带宽:主对角线一个方向对角线的个数,个数为d。


带宽:所有的对角线的个数。个数为 2d+1


n阶2d+1对角矩阵非零元素个数:n(2d+1) - d(d+1)


n(2d+1) :下图中所有颜色的个数


d(d+1)/2 :右下方浅蓝色三角的个数


d(d+1) :2个三级的个数(右下方、左上方)微信图片_20220531145049.png


一维数组存储个数:n(2d+1) ,若某行没有2d+1个元素,则0补足。


5.7.2压缩存储


微信图片_20220531145239.png


压缩后存放一维数组,第一行和最后一行不够2d+1,所以需要补零。微信图片_20220531145246.png

相关文章
|
1月前
|
存储 Java 程序员
数据结构之 - 深入了解数组数据结构
数据结构之 - 深入了解数组数据结构
36 6
|
1月前
|
存储 算法 搜索推荐
探索常见数据结构:数组、链表、栈、队列、树和图
探索常见数据结构:数组、链表、栈、队列、树和图
95 64
|
1月前
|
算法 程序员 索引
数据结构与算法学习七:栈、数组模拟栈、单链表模拟栈、栈应用实例 实现 综合计算器
栈的基本概念、应用场景以及如何使用数组和单链表模拟栈,并展示了如何利用栈和中缀表达式实现一个综合计算器。
27 1
数据结构与算法学习七:栈、数组模拟栈、单链表模拟栈、栈应用实例 实现 综合计算器
|
1月前
|
存储 算法 定位技术
数据结构与算法学习二、稀疏数组与队列,数组模拟队列,模拟环形队列
这篇文章主要介绍了稀疏数组和队列的概念、应用实例以及如何使用数组模拟队列和环形队列的实现方法。
20 0
数据结构与算法学习二、稀疏数组与队列,数组模拟队列,模拟环形队列
|
2月前
|
存储 Java
java数据结构,线性表顺序存储(数组)的实现
文章介绍了Java中线性表顺序存储(数组)的实现。线性表是数据结构的一种,它使用数组来实现。文章详细描述了线性表的基本操作,如增加、查找、删除、修改元素,以及其他操作如遍历、清空、求长度等。同时,提供了完整的Java代码实现,包括MyList接口和MyLinearList实现类。通过main函数的测试代码,展示了如何使用这些方法操作线性表。
|
4月前
|
存储
【数据结构OJ题】轮转数组
力扣题目——轮转数组
42 2
【数据结构OJ题】轮转数组
|
3月前
|
存储 Java 程序员
"揭秘HashMap底层实现:从数组到链表,再到红黑树,掌握高效数据结构的秘密武器!"
【8月更文挑战第21天】HashMap是Java中重要的数据结构,采用数组+链表/红黑树实现,确保高效查询与更新。构造方法初始化数组,默认容量16,负载因子0.75触发扩容。`put`操作通过计算`hashCode`定位元素,利用链表或红黑树处理冲突。`get`和`remove`操作类似地定位并返回或移除元素。JDK 1.8优化了链表转红黑树机制,提升性能。理解这些原理能帮助我们更高效地应用HashMap。
42 0
|
5月前
|
存储 算法 调度
【数据结构与算法】详解循环队列:基于数组实现高效存储与访问
【数据结构与算法】详解循环队列:基于数组实现高效存储与访问
|
4月前
|
机器学习/深度学习 存储 算法
数据结构与算法:数组的操作
数据结构与算法:数组的操作
下一篇
无影云桌面