ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

第四章 串,数组和广义表

2022-02-06 16:03:55  阅读:119  来源: 互联网

标签:存储 ch 数组 int length 广义 SString 第四章


目录

第四章 串,数组和广义表

4.1串的定义

  • (s,或者说是字符串)是由零个或者是多个字符组成的有限序列,零个字符的串称为空串
  • 子串,主串:子串主串中连续的一段字符串
  • 串相等:长度和各个元素相等
  • 空格串:由一个或者多个空格组成的串

4.3 串的类型定义,存储结构及其运算

4.3.1 串的抽象类型定义

串是特殊的线性表,串和线性表不同的是,串操作的是多个元素,而线性表操作的是单个元素

抽象数据类型的定义如下

4.3.2 串的存储结构(顺序,和链式存储)

定义1(定长)

typedef struct
{
    char ch[MAXLEN+1];//存储串的一维数组
    int length;//串的当前长度
}SString;

为了显示方便,这里存储的数据都是从下标1开始的,而不是0

定义2(堆式)

typedef struct
{
    char *ch;//类似数组
    int length;
}HString;

定义3(链式)

#define CHUNKSIZE 80
typedef struct Chunk//串块
{
    char ch[CHUNKSIZE];
    struct Chunk *next;
}Chunk;
typedef struct
{
    Chunk *head,*tail;//串的头尾指针
    int length;//串的当前长度
}LString;

顺序串的几个基本算法

//这里的字符串都是从下标0开始的,以后从下标1开始
void StrAssign(SString &S, char chars[])//chars的值插入到S中
{
    int i=0;
    while(chars[i])//开始复制
    {
        S.ch[i]=chars[i];
        i++;
    }
    S.length=i;
}



void StrCopy(SString &T,SString S)//S复制到T
{
    int i=0;
    while(i<S.length)//开始复制
    {
        T.ch[i]=S.ch[i];
        i++;
    }
    T.ch[++i]='\0';
    T.length=S.length;
}



bool StrEmpty(SString S)//是否为空串
{
    if(S.length==0)
        return true;
    else return false;
}



int StrCompar(SString S,SString T)//判断大小
{
    int i=0;
    int flag=0;
    while((i<S.length)&&(i<T.length))
    {
        if(S.ch[i]>T.ch[i])
        {
            return 1;
        }
        else if(S.ch[i]<T.ch[i])
            return -1;
        else continue;
    }
    return 0;
}



int StrLength(SString S)//输出字符串长度
{
    int i=0;
    while(i<S.length)
    {
        i++;
    }
    return i;
}



void ClearString(SString &S)//清空字符串
{
    S.ch[0]='\0';//这种方法...
}



void Concat(SString &T,SString S1,SString S2)//链接S1,S2
{
    int i = 0;
    while (i<S1.length)//插入第一个
    {
        T.ch[i] = S1.ch[i];
        i++;
    }
    int j = 0;
    while ((j<S2.length) && (i < MAXLEN))//插入第二个
    {
        T.ch[i] = S2.ch[j];
        i++;
        j++;
    }
}



void SubString(SString &Sub,SString S,char pos,int len)//用Sub返回pos起的后面len个字符串
{
    int i=0;
    while(i<S.length)
    {
        i++;
        if(pos==S.ch[i])//找到位置
            break;
    }
    int j=0;
    while(len)//开始复制
    {
        len--;
        Sub.ch[j++]=S.ch[i];
    }
}




void display(SString S)
{
    int i=0;
    while(i<S.length)
    {
        cout << S.ch[i++] << " ";
    }
}

4.3.3 串的模式匹配算法

  • 串的模式匹配串匹配:子串的定位运算,比如用在搜索引擎,拼写检查,语言翻译,数据压缩

著名的模式匹配算法有BF算法和KMP算法

BF算法

算法实现

#include<iostream>
using namespace std;
#define OK "OK"
#define ERROR "ERROR"
#define MAXLEN 255
typedef string Status;
typedef struct
{
    char ch[MAXLEN+1];//存储串的一维数组
    int length;//串的当前长度
}SString;
void StrAssign(SString &S, char chars[])//chars的值插入到S中
{
    int i=1;
    while(chars[i])//开始复制
    {
        S.ch[i]=chars[i];
        i++;
    }
    S.length=i-1;
}
int Index_BF(SString S,SString T,int pos)//BF算法
{
    int i=pos,j=1;
    while(i<=S.length&&j<=T.length)
    {
        if(S.ch[i]==T.ch[j]) {i++;j++;}//若比较成功则继续比较
        else {i=i-j+2;j=1;}//比较不成功则模式串后移一位重新比较
    }
    if(j>T.length) return i-T.length;//匹配成功返回
    else return 0;//匹配不成功
}
int main()
{
    SString S;
    SString T;
    StrAssign(S,(char*)"scacscdaef");
    StrAssign(T,(char*)"cscdae");
    cout << Index_BF(S,T,1);
    return 0;
}

算法思路(S是主串,T是模式串,称为模式T)

  1. 如图,将i指针放在开始匹配的地方,j指针也放在这个地方,两个串开始比较
  2. 比较一直比较到模式串T走完,若中途匹配不成功,则i返回头部并向下移动一位,继续开始比较
  3. 直到最后匹配完成,若一直没有匹配成功的,则return 0
  4. 此种算法最好情况时间复杂度为O(m+n),最坏情况O(m*n)

KMP算法

算法实现

#include<iostream>
using namespace std;
#define OK "OK"
#define ERROR "ERROR"
#define MAXLEN 255
typedef string Status;
typedef struct
{
    char ch[MAXLEN+1];//存储串的一维数组
    int length;//串的当前长度
}SString;
void StrAssign(SString &S, char chars[])//chars的值插入到S中
{
    int i=0;
    while(chars[i])//开始复制
    {
        S.ch[i+1]=chars[i];
        i++;
    }
    S.length=i;
}
int next(SString T,int j)//模式串滑动位数函数
{
    if(j==1)//第一种情况
        return 0;
    int flag1=0;
    int k=2;
    int Max=2;

    while(k<j)
    {
        int flag=1;//判断当前k是否满足条件
        int i=1;
        while(i<k)
        {
            if(T.ch[i]!=T.ch[j-k+i])//不满足条件
            {
                flag=0;
                break;
            }
            i++;
        }
        if(flag==1) //满足条件,k更新
        {
            flag1=1;//有匹配子串
            Max = k++;
        }
        else k++;
    }
    if(flag1==1)//第二种情况
    return Max;
    else return 1;//第三种情况
}
int Index_KMP(SString S,SString T,int pos)
{
    int i=pos,j=1;
    while(i<=S.length&&j<=T.length)//两串未到末尾
    {
        if(j==0||S.ch[i]==T.ch[j]) {i++;j++;}//匹配成功继续匹配
        else j=next(T,j);//匹配失败则滑动模式串
    }
    if(j>T.length) return i-T.length;
    else return 0;
}
int main()
{
    SString S;
    SString T;
    int i=1;
    StrAssign(S,(char*)"ababcabcacbab");
    StrAssign(T,(char*)"abcac");
    cout << Index_KMP(S,T,1);
    return 0;
}

思路

主串从指定位置开始匹配,匹配失败则i不移动,j移动到next()函数指定的位置处,逐步向后匹配,其余思路和第一个差不多

next函数(书本)

void get_next(SString T,int next[])
{
    int i=0;next[1]=0;j=0;
    while(i<T[0])
    {
        if(j==0||T.ch[i]==T.ch[j]) {i++;j++;next.ch[i]=j;}//匹配开始
        else j=next[j];
    }
}

next函数

int next(SString T,int j)//模式串滑动位数函数
{
    if(j==1)//第一种情况
        return 0;
    int flag1=0;
    int k=2;
    int Max=2;

    while(k<j)
    {
        int flag=1;//判断当前k是否满足条件
        int i=1;
        while(i<k)
        {
            if(T.ch[i]!=T.ch[j-k+i])//不满足条件
            {
                flag=0;
                break;
            }
            i++;
        }
        if(flag==1) //满足条件,k更新
        {
            flag1=1;//有匹配子串
            Max = k++;
        }
        else k++;
    }
    if(flag1==1)//第二种情况
    return Max;
    else return 1;//第三种情况
}

上面函数根据以下得来

两种算法的比较

BF算法在很多情况下时间复杂度O(n+m),至今任然被采用,KMP则对比较庞大数据更有效,无需重头回读

4.4数组

4.4.1数组的类型定义

  • 数组是由类型相同的数据袁术构成的有序集合
  • 数组的元素属于同一个数据类型
  • 数组也是线性表
  • 数组被定义其维数和维界就不再改变
  • 数组被定义后只能进行存储元素和修改元素值的行为

数组的抽象数据类型定义如下

初始化


销毁数组


返回指定下标数值


录入指定元素


4.4.2数组的顺序存储

  • 数组的存储结构分为以行为主序(C,Basic,Pascal,Java)和以列为主序(FORTRAN)两种,显而易见的区别就是两种行列标互换
  • 二维数组的存储位置:LOC(i,j)=LOC(0,0)+(n*i+j)L;,其中L是单个数据元素占得存储单元大小,该数组是m行n列
  • 二维数组的起始存储地址称为基地址或者基址
  • 数组存取数组中任意元素的时间相等,数组是一种随机存取结构

4.4.3特殊矩阵的压缩存储

数组可用矩阵表示

特殊矩阵:值相同的元素或者零元素在矩阵中的分布有一定的规律

以下是几种特殊矩阵

对称矩阵

定义:满足aij=aji

压缩方法:为每一对称元分配一个存储空间,可将n²个元压缩存储到n(n+1)/2个元的空间中,相当于存储为下三角或者上三角

三角矩阵

定义:上三角矩阵是下三角区域为常数,下三角矩阵类似

压缩方法:与对称矩阵类似,除存三角区域外,加一空间存储常数

  1. 上三角矩阵

  1. 下三角矩阵

对角矩阵

定义:元素都分布在以主对角线为中心的带状区域中,其余元素全部都为零

压缩方法:可按某个原则(或以行为主,或以对角线的顺序)将其压缩存储到一维数
组上。

4.5广义表

4.5.1广义表的定义

  • 广义表是线性表的推广,也称为列表,广泛用于人工智能等领域的表处理语言
  • 记作:LS=(a1,a2,a3,an);其中LS是广义表的名称,n是长度,ai可以是单个元素(原子)也可以是广义表(子列)
  • 习惯上用大写字母表示广义表的名称,小写字母表示原子

结论

  1. 广义表的元素也可以是广义表,是一个多层次的结构,如图圆圈表示广义表,方块表示原子
  2. 广义表可以为其他广义表共享,上图中,D中不必列出A,B,C的元素,而是通过子表的名称来引用
  3. 广义表是一个递归的表,其本身也是一个子表

注意:广义表()和广义表(())不同,一个是空表,长度为0,一个不是空表,长度为1,可以分解其表头表尾均为空表()

4.5.2广义表的存储结构

因为广义表中的数据元素可以有不同的结构,一次通常采用链式存储结构,有两种链式存储结构,一个是头尾链表的存储结构和扩展线性链表的存储结构

头尾链表的存储结构

  • 表结点:标志域,指示表头的指针域,指示表尾的指针域
  • 原子结点:标志域,值域

形式定义说明如下

typedef enum{ATOM,LIST} ElemTag;//ATOM==0原子,LIST==1子表
typedef struct GLNode
{
    ElemTag tag;//区分原子结点和表结点,标志域
    union//原子结点和表结点联合部分
    {
        AtomType atom;//原子结点的值域
        struct//指针域定义
        {
            struct GLNode *hp,*tp;//表头指针和表尾指针
        }ptr;
    };
}*GList;//广义表类型

图4.14存储结构示例

  1. 上图中矩形的三个框分别是标志域,表头指针,表尾指针,两个框的分别表示标志域和值域
  2. 除了空表的表头指针为空外,对于任何的非空表结点,其表头指针均指向一个表结点
  3. 容易分清列表中原子和子表所在的层次
  4. 最高层的表结点个数即为广义表的长度

扩展线性链表的存储结构

这种结构均由三个域组成

其定义同上

其他知识:

4.6案例分析与实现

病毒感染检测

#include<iostream>
#include <fstream>
using namespace std;
#define MAXLEN 255
typedef struct
{
    string ch;//存储串的一维数组
    int length=0;//串的当前长度
}SString;
int Index_BF(SString S,SString T,int pos)//BF算法
{
    int i=pos,j=1;
    while(i<=S.length&&j<=T.length)
    {
        if(S.ch[i]==T.ch[j]) {i++;j++;}//若比较成功则继续比较
        else {i=i-j+2;j=1;}//比较不成功则模式串后移一位重新比较
    }
    if(j>T.length) return i-T.length;//匹配成功返回
    else return 0;//匹配不成功
}
void Virus_detection()//BF算法实现病毒检测
{
    int flag=0;//匹配标识变量
    SString Virus;//病毒
    SString Person;//人
    string Vir;//临时存入病毒变量
    string Per;
    int num;//待检测任务数
    int m;//字符串长度
    SString temp;//临时变量
    ifstream inFile("病毒感染检测输入数据.txt");
    ofstream outFile("病毒感染检测输出结果.txt");
    inFile >> num;
    while(num--)//循环每一个任务
    {
        inFile >> Virus.ch;//输入病毒,可能有问题
        inFile >> Person.ch;//输入人DNA,可能有问题
        Virus.length=(int)Virus.ch.length();//存入字符串长度
        Person.length=(int)Person.ch.length();
        Vir=Virus.ch;//病毒DNA存入临时变量
        Per=Person.ch;//存入临时变量
        Virus.ch='0'+Virus.ch+Virus.ch;//字符串后移一位
        Person.ch='0'+Person.ch;
        flag=0;
        m=Virus.length;
        for(int i=0;i<m;i++)//一次取长度为m的病毒DNA
        {
            temp.ch='0';//重新初始化
            temp.length=m;
            for(int j=1;j<=m;j++) temp.ch.push_back(Virus.ch[i+j]);//将这m个字符串存入临时变量
            flag= Index_BF(Person,temp,1);//BF算法判断
            if(flag) break;
        }
        if(flag) outFile << Vir << "     " << Per << "      YES" << endl;
        else outFile << Vir << "     " << Per << "      NO" << endl;
    }
}
int main()
{
    Virus_detection();
    cout << "检测完成!" << endl;
    system("pause");
    return 0;
}

字符统计

#include<iostream>
#include <fstream>
#include<unordered_map>
using namespace std;
int main()
{
    ifstream inFile("输入数据.txt");
    ofstream outFile("计算输出结果.txt");
    string str;
    int sum[36];
    inFile >> str;//读入字符串数据
    unordered_map<char,int>hash_map;
    for(int i=65;i<91;i++)//初始化哈希表
        hash_map[(char)i]=0;
    for(int i=48;i<58;i++)//初始化哈希表
        hash_map[(char)i]=0;
    for(char it:str)//录入数据
        hash_map[it]++;
    int j=0;
    for(int i=65;i<91;i++)//输出字母数据到文件
        outFile << (char)i << ":" << hash_map[(char)i] << endl;
    for(int i=48;i<58;i++)//输出数字数据到文件
        outFile << (char)i << ":" << hash_map[(char)i] << endl;
    cout << "完成" << endl;
    system("pause");
    return 0;
}

标签:存储,ch,数组,int,length,广义,SString,第四章
来源: https://www.cnblogs.com/mhduiy/p/15865746.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有