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;
}
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。
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)三元组表的顺序存储叫三元组顺序表。
要唯一表示一个稀疏矩阵,需要存储三元组表的同时存储该矩阵的行数、列数、非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]++; //预置同一列下一个三元组的下标
}
}