串、数组与广义表


计算机上的非数值处理的对象大部分是字符串数据,字符串一般简称为串。串是一种特殊的线性表,其特殊性体现在数据元素是一个字符,也就是说,串是一种内容受限的线性表。由于现今使用的计算机硬件结构是面向数值计算的需要而设计的,在处理字符串数据时比处理整数和浮点数要复杂得多。而且,在不同类型的应用中,所处理的字符串具有不同的特点,要有效地实现字符串的处理,就必须根据具体情况使用合适的存储结构。

1.串的基本概念

1.1串的定义

串(string)(或字符串) 是由零个或多个字符组成的有限序列, 一般记为:
s= "a1 a2 … an " (n>=0)
其中,s是串的名, 用双引号括起来的字符序列是串的值;ai(1<=i<=n)可以是字母、数字或其他字符;串中字符的数目n称为串的长度。零个字符的串称为空串(null string) , 其长度为零。串中任意个连续的字符组成的子序列称为该电的子串。

1.2其他概念

概念定义
子串串中任意个连续字符组成的子序列称为该串的子串
主串包含子串的串相应地称为主串
字符位置字符在序列中的序号为该字符在串中的位置
子串位置子串第个字符在主串中的位置(注:原文中此处可能有遗漏)
空格串由一个或多个空格组成的串,与空串不同
串相等当且仅当两个串的长度相等并且各个对应位置上的字符都相同时,这两个串才是相等的。

2.串的类型定义、存储结构及运算

2.1串的类型定义

2.1.1串的顺序存储结构

#define Max_Size 255
typedef int ElemType;  
typedef struct {  
    ElemType data[Max_Size + 1]; //这里是方便理解从1开始存储  
    int length;  
}SString;

2.1.2串的链式存储结构

//链式存储  
#define CHUCKSIZE 100 //存储容量可以由用户自己定义  
typedef struct Chuck {  
    char ch[CHUCKSIZE];  
    struct Chuck* next;  
}Chuck;  
  
typedef struct {  
    Chuck* head;  
    Chuck* tail;    //串的头指针和尾指针  
    int curlen;     //串的当前长度  
}LString;

2.2串的模式匹配算法

2.2.1算法目的:

确定主串中所含子串(模式串)第一次出现的位置(定位)

2.2.2算法应用:

搜索引擎、拼写检查、语言翻译、数据压缩

2.2.3算法种类:

BF算法(朴素的、穷举的)(Brute-Force,又称古典的、经典的)
KMP算法(特点:速度快)

2.2.4串的模式匹配算法----BF算法

Brute-Force简称为BF算法,亦称简单匹配算法。采用穷举法的思想。
BF算法设计思想——Index(S,T,pos)

  • 将主串的第pos个字符和模式串的第一个字符比较
  • 若相等,继续逐个比较后续字符,
  • 若不等,从主串的下一字符起,重新与模式串的第一个字符比较
    代码实现
//BF算法  
int Index_BF(SString S,SString T,int pows) {  
    int i = pows;  
    int j = 1;  
    while (i <= S.length && T.length) {  
        if(S.data[i] == T.data[j]) {  
            i++;  
            j++;  
        }  
        else {  
            i = i - j + 2;  
            j = 1;  
        }  
        if(j > T.length) {  
            return i - T.length;  
        }  
        else {  
            return 0;  
        }  
    }  
}

2.2.5KMP(Knuth Morris Pratt)算法

KMP算法是D.E.Knuth、J.H.Morris和V.R.Pratt共同提出的,简称KMP算法。
该算法较BF算法有较大改进,从而使算法效率有了某种程度的提高。
KMP算法的设计思想:

利用已经部分匹配的结果而加快模式串的滑动速度?且主串S的指针i不必回溯!可提速到O(n+m)!
为此,定义next[]函数,表明当模式中第个字符与主串中相应字行“失配”时,在模式中需重新和主串中该字符进行比较的字符的位置。

代码实现

//求next数组  
void GetNext(SString T, int *next) {  
    next[1] = 0;  
    int j = 0;  
    int i = 1;  
    while (i < T.length) {  
        if (0 == j || T.data[i] == T.data[j]) {  
            i++, j++;  
            next[i] = j;  
        } else {  
            j = next[j];  
        }  
    }  
}  
int Index_KMP(SString S, SString T, int pos) {  
    int *next = (int *)malloc(sizeof(int) * (T.length + 1));  
    GetNext(T, next);  
    int i = pos;  
    int j = 1;  
    while (i <= S.length && j <= T.length) {  
        if(j == 0 || S.data[i] == T.data[j]) {  
            i++;  
            j++;  
        }  
        else {  
            j = next[j];  
        }  
    }  
    if(j > T.length) {  
        return i - T.length;  
    }  
    else {  
        return 0;  
    }  
}

3.数组的基本概念

概念描述
数组按一定格式排列起来的具有相同类型的数据元素的集合。
维数组若线性表中的数据元素为非结构的简单元素,则称为一维数组。
一维数组的逻辑结构线性结构。定长的线性表
声明格式数据类型 变量名称[长度];

4.广义表的基本概念

广义表(又称列表 Lists)是 n≥0 个元素 a, a.,… an-1的有限序列,其中每一个 a;或者是原子,或者是一个广义表。

概念描述
表头若线性表LS非空(n≥1),则其第一个元素a就是表头。记作head(LS):
表头注解表头可以是原子,也可以是子表。
表尾除表头之外的其他元素组成的表。记作tail(LS) = (a2… an)。
表尾注解表尾不是最后一个元素,而是一个子表。

5.广义表的性质

  1. 元素次序:广义表中的元素有相对次序,每个元素有一个直接前驱和一个直接后继。
  2. 长度定义:广义表的长度定义为最外层所包含的元素个数。
  3. 深度定义
    • 广义表的深度定义为该广义表展开后所含括号的层数。
    • 例如:
      • A = (b, ℃) 深度为 1
      • B = (A, d) 深度为 2
      • C = (f, B, h) 深度为 3
    • 注意:原子的深度为 0,空表的深度为 1。
  4. 共享性:在广义表中,一个元素可以为其他广义表共享,如上述广义表 B 就共享了 A。
  5. 递归性
    • 广义表可以是一个递归的表,如上述的 F 广义表。
    • 注意:递归表的长度是有限的,但是深度是无限的。

6.广义表的基本运算

广义表基本运算结果
DGetHeadE
DGetTail(F)
EGetHeadα
EGetTail((b, c))
((b, c))GetHead(b, c)
((b, c))GetTail()
(b, c)GetHeadb
(b, c)GetTail©
cGetHeadc
cGetTail()
posted @ 2024-08-13 09:27  写代码的大学生  阅读(11)  评论(0编辑  收藏  举报  来源