【数据结构复习】第四章字符串和多维数组

字符串和多维数组

字符串

字符串的逻辑结构

1.字符串的定义

(1)字符串(string简称"串")是n(>=0)个字符组成的有限序列,串中包含字符个数称为串的长度

记作: S = “S1,S2……Sn”
S为串名,双引号为定界符,引起来的部分是串值。长度为0的串称为空串," "内不包含任何字符。

(2)字符串中任意个连续的字符组成的子序列称为该串的子串,包含字串的串称为主串
字串的第一个字符在主串中的序号即该字串在主串中的位置
例:
字串"str"在主串"primary string"中的位置是9 (注:空格在字符串中也占一位)。

(3)计算机能够表示的所有的字符称为系统字符集。常用标准字符集有ASCII码和Unicode等
字符集中每个字符都有一个唯一数值表示。
例:
字符a和字符b在ASCII码中分别为97、98,则"a">“b”。

2.字符串大小比较(ACSII值):
原则:
a。从左到右一位位比较。
b。比较到其中一位结束,字符串长度谁长谁大。
例:
“aa”<“aaaaa”(比较字符串的长度谁长谁大)
“ba”>“aaaaa”(第一为b的ASCII值就比a大所以剩下的不用比较,ba大)

字符串的存储结构

1.字符串的顺序存储
字符串是数据元素为单个的线性表,一般采用顺序存储,即数组来存储串的字符序列。
(1)用一变量表示串的长度。
在这里插入图片描述
(2)用数组的0号单元存放串的长度串值从1号单元开始存放
在这里插入图片描述
(3)在串尾添加一个不会在串中出现的特殊字符作为字符串的终结符,‘\0’表示串的结束,此方法不能直接得到串的长度,而是通过判断’\0’来确定串是否结束,而得到串的长度。
在这里插入图片描述

模式匹配

1.定义
两个字符串S="s1,s2……sn"和T="t1,t2……tm"在主串S中寻找字串T的过程称为模式匹配,T字串称为模式。若匹配成功返回T在S中的位置,若失败返回0。

2.BF算法
思想:蛮力匹配(遍历),即从主串S的第一个字符开始和模式T的第一个字符进行比较,若相等则继续比较后续字符,否则从主串S的第二个字符开始和模式T的第一个字符进行匹配。(成功接着比直到字串比完,不成功往后挪一个直到主串比完返回匹配失败0)
重复上述过程知到S或T中的所有字符比较完毕,T中字符全部比较完毕,则匹配成功,否则匹配失败,返回0

例:第一趟失败,从主串S的第二个字符开始,匹配失败,以此类推,字串再向后移动。
最终在i = 8,j = 5匹配成功。
在这里插入图片描述
BF(S,T)基本思想
输入:主串S模式T
输出:T在S中的位置
1.在串S和串T中设置比较的起始下标i = 0,j = 0
2.重复以下操作,直到S或T的所有字符比较完毕
(1)若S[i]等于T[i],继续比较S和T的下一对字符
(2)否则,下标i,j分别回溯,开始下一趟匹配
3.若T中字符比较完,则匹配成功,返回本趟匹配起始位置,否则失败,返回0

函数定义

//start记录主串S中每一趟比较的起始位置
int BF(char S[],char T[])
{
    int start = 0;          //主串从下标为0开始第一趟匹配
    int i = 0,j = 0;
    while ((S[i]!='\0')&&(T[j]!='\0'))       //T匹配结束,匹配成功。否则失败
    {
        if (S[i]==T[j]) {i++,j++;}
        else {start++;i = start;j = 0;}      //i、j分别回溯
    }
    if (T[j]=='\0') return start+1;          //返回本趟匹配的起始位置,注意位置是从1开始的数组从0开始
    else return 0;
}

时间复杂度
(1)最好情况
在这里插入图片描述
(2)最坏情况
在这里插入图片描述

3.KMP算法

某趟S[i]和T[j]匹配失败后,下标i不回溯,下标j回溯至某位置k,后移模式串。
找到公共前后缀,不匹配则前缀移到后缀位置。
最长公共前后缀长度 < 比较指针左端子串不等于子串。

(1)KMP算法匹配过程:
a。首先箭头左边(比较指针左边)模式串和主串上下匹配
在这里插入图片描述
b。其次,模式串中有两个子串相匹配是前后缀
在这里插入图片描述
c。若不匹配,则前缀移动到后缀位置
在这里插入图片描述
d。不匹配,则找出匹配失败位置之前的最长前后缀,此处最长前后缀就是A
在这里插入图片描述
e。此时移动前缀到后缀的位置,发现模式串长度超出主串长度匹配失败
在这里插入图片描述

(2)next数组求解过程
a。模式串从数组下标1开始存,下标0不存数据
1号位置发生不匹配,让1号位置上的字符与主串下一个位置的字符进行比较(模式串前移一位)
在这里插入图片描述
b。2号位发生不匹配,因为公共前后缀的长度要求 < 子串的长度,此时长度前后缀长度与字串长度相等,因此公共前后缀的长度为0,向后移一位。在这里插入图片描述
在这里插入图片描述
c。3号位与2号等同前后缀长度为0,往后移一位
d。4号位公共前后缀为1,前缀移到后缀,此时比较指针在2号位,2号位与主串当前位进行比较
在这里插入图片描述
在这里插入图片描述
e。5号位最大公共前后缀为AB,前缀移动到后缀,此时比较指针指向3号,因此3号位与主串当前位置进行比较。
在这里插入图片描述
在这里插入图片描述
后面等同
在这里插入图片描述
根据模式串每位出错后与主串–比较得到next数组
在这里插入图片描述

规律:每次开始比较的位置编号其值等于当前最大公共前后缀长度+1

KMP(S,T,next)基本思想
输入:主串S,模式串T,模式T的next值输出:T在S中的位置
1.在串S和T中分别设置比较起始下标i = 0,j = 0
2.重复以下操作,直到S或T的所有字符均比较完成
(1)若S[i]等于T[j],继续比较S和T的下一对字符
(2)否则,下标j回溯到next[j]位置,即j = next[j]
(3)若j = -1,则将下标i和j分别加1,准备下一趟比较
3.若T中均比较完成,返回本趟匹配开始的位置,否则返回0

在这里插入图片描述

多维数组

1.数组的定义

数组是由同类型的数据元素(=数组元素=元素)构成的有序集合
每个元素在n个线性关系中的序号i1,i2,……in称为该元素的下标,并称该数组为n维数组。

数组的特点:(1)元素本身可以具有某种结构(2)数据元素属于同一数据类型
例:一维数组可看作是一个线性表,二维数组可看作线性表的线性表。
在这里插入图片描述

2.数组中的操作
(1)读操作:给定一组下标,读取相应的数组元素。
(2)写操作:给定一组下标,存储或修改相应的数组元素。
注:本质操作都是寻址,即根据下标定位数组元素。

3.数组的存储结构与寻址
(1)数组一般不进行插入和删除操作,一旦建立数组,元素个数和元素之间关系就不再发生变化。
(2)数组要求能随机存取,因此数组采用顺序存储,内存单元是一维结构,多维数组是多维结构,需要将多维结构映射到一维。
(3)常用的映射方法:有以行序为主序的按行优先按列优先两类。
二维数组按行优先:先行后列,先存储行号较小的元素,行号相同先存储列号较小的。
二维数组按列优先:先列后行,先存储列号较小的元素,列号相同先存储行号较小的。

4.按行优先aij的存储地址
在这里插入图片描述
(i-l1行数)*(h2-l2+1每行个数)+(j-l2剩下aij前的元素个数)
在这里插入图片描述

在这里插入图片描述
按行优先存储
在这里插入图片描述
按列优先与按行优先类似

矩阵的压缩存储

阶数高的、矩阵中很多值相同的、分布有规律的——特殊矩阵,有很多0元素的——稀疏矩阵
可对这类矩阵进行压缩,节省存储空间。
基本思想:
(1)多个值相同的元素只分配一个存储空间
(2)对0元素不分配存储空间。
(3)存储矩阵的一维数组下标从0开始。

1.对称矩阵的压缩存储
设对称矩阵为n阶方阵,则有aij = aji
在这里插入图片描述只需存储下三角部分(包括对角线)
原来需要 n * n个存储单元现在需要 n * (n+1)/2个存储单元。

将这些元素按行存储到数组SA[n(n+1)/2]中,数组SA中下标k与i、j的关系为:k = ix(i-1)/2+j-1。
对于上三角则访问与下三角对应的元素即:k = jx(j-1)/2+i-1。

对称矩阵压缩存储:
对称矩阵压缩存储
在这里插入图片描述
计算方法
在这里插入图片描述


2.三角矩阵的压缩存储
在这里插入图片描述
只需存储上、下三角矩阵和对角线(包括对角线)上(下)方的常数c(只存一次)

共存储nx(n+1)/2+1个元素比对称矩阵多存储一个常数c
将这些元素按行优先存储到数组SA[n(n+1)/2+1]中,数组SA中下标k:k = (i-1)x(2n=i+2)/2+j-i。

下三角矩阵压缩存储:
在这里插入图片描述


3.对角矩阵的压缩存储
在这里插入图片描述
只需存储对角线上的非0元素

在这里插入图片描述
在这里插入图片描述

4.稀疏矩阵的压缩存储(只存有效的)
定义:稀疏矩阵是0元素居多的矩阵,非0元素分布无规律。

非0元素分布无规律,因此需要存储非0元素的行号列号和元素的值,即将非0元素表示为三元组(行号、列号、非零元素值)。

三元组结构定义:

template <typename DataType>
struct element
{
    int row,col;			//行号、列号
    DataType item;			//元素的值
};

将稀疏矩阵的非0元素对应的三元组所构成的集合排列成一个线性表,称为三元组表
例:如图稀疏矩阵A对应的三元组表是
在这里插入图片描述
((1,1,3),(1,4,7),(2,3,-1),(3,1,2),(5,4,-8))

(1)三元组表的顺序存储叫三元组顺序表

矩阵A的三元组顺序表:5行、4列、5个非0元素
在这里插入图片描述

要唯一表示一个稀疏矩阵,需要存储三元组表的同时存储该矩阵的行数、列数、非0元素个数

const int MaxTerm = 100;
struct SparseMatrix
{
	element data[MaxTerm];		//存储非0元素
	int mu,nu,tu;				//行数、列数、非0元素个数
};

(2)十字链表
采用三元组存储稀疏矩阵,加法、乘法等操作会改变非0元素个数及位置,则三元组顺序表中就要进行插入、删除操作,用顺序表存储十分不便。
稀疏矩阵的链接存储结构叫十字链表

基本思想:将每个非0元素对应的三元组存储为一个链表结点,结点由5个域构成。
在这里插入图片描述
data存放非0元素对应的三元组,right为指向同行下一个三元组结点
down为指向同列下一个三元组结点。

十字链表结点结构定义

struct OrthNode
{
    element data;
    struct OrthNode * right, *down;
};

将稀疏矩阵的每一行非0元素按其列号从小到大由right域构成一个行链表,每列非0元素按其行号的从小到大down域构成一个列链表,每个元素既是元素aij的行链表的一个结点又是列链表的一个结点,故称十字链表

为了实现对某一行某一列的快速查找,将这些行链表、列链表的头指针存储到数组HA、HB中
要会画
在这里插入图片描述

扩展与提高

稀疏矩阵的转置运算

行——>列 列——>行

1.转置算法1——直接取顺序存
设对稀疏矩阵Aij(1<=i<=n,1<=j<=m)进行转置运算

算法1的基本思想:在A的三元组顺序表中依次查找第1列、第2列……最后1列,并将找到的三元组行、列交换后顺序存储到B的三元组顺序表中。

void Trans1(SparseMatrix &A,SparseMatrix &B)
{
    int pa,pb,col;
    B.mu = A.mu;B.mu = A.mu;B.tu = A.tu;
    pb = 0;
    for (col = 1;col<=A.nu;col++)                   //依次考察每一列
        for (pa = 0;pa<A.tu;pa++)                   //扫描A的三元组顺序表
            if (A.data[pa],col==col){               //处理col列元素
                B.data[pb].row=A.data[pa].col;
                B.data[pb].col=A.data[pa].row;      //将A列换成行,行换成列,存到B中
                B.data[pb].item=A.data[pa].item;
                pb++;
            }
}

2.转置算法2——顺序取,直接存

转置算法2 Trans2(A,B)基本思想
输入:三元组顺序表存储的稀疏矩阵A
输出:矩阵A的转置矩阵B,矩阵B采用三元组顺序表存储
1.设置转置后矩阵B的行数、列数和非0元素的个数
2.计算A中每一列的非0元素个数
3.计算A中每一列的第一个非0元素在B中的下标
4.依次取A中的每一个非0元素对应的三元组
(1)确定该元素在B中的下标pb
(2)将该元素的行号、列号交换后存入B中pb的位置
(3)预置该元素所在列的下一个元素的存放位置

void Trans2(SparseMatrix &A,SparseMatrix &B)
{
    int i,j,k,num[MaxTerm] = {0},copt[n];
    B.mu = A.nu; B.nu = A.mu; B.tu = A.tu;
    for (i = 0;i<A.tu;i++)                  //求每一列非0元素的个数
    {
        j = A.data[i].col;                  //取三元组的列号
        num[j]++;
    }
    copt[i] = 0;
    for (i = 2;i<=A.nu;i++)                 //扫描三元组表A
    {
        j = A.data[i].col;                  //取当前三元组列号
        k = cpot[j];                        //当前三元组在B中的下标
        B.data[k].row = A.data[i].col;
        B.data[k].col = A.data[i].row;
        B.data[k].item = A.data[i].item;
        copt[j]++;                          //预置同一列下一个三元组的下标
    }
}
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值