数据结构--串

137 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第6天

定义

串 字符串 由零个或多个字符组成的有序数列

记为: S = ′a1 a2 … an′( n ≥ 0 )

串名 值 长度 空串 子串 主串 位置 空格串

类型定义

ADT String { 数据对象: D={ ai | ai∈ CharacterSet,记为 V,i=1 ,2 ,…, n,n≥ 0 } 结构关系: R={< ai,ai + 1 >| ai,ai + 1 ∈ V,i=1 ,…, n-1 ; n-1 ≥ 0 } 基本操作:

( 1 ) StrAsign( S,chars) 操作前提: chars 是字符串常量。 操作结果:生成一个值等于 chars 的串 S。 ( 2 ) StrInsert( S,pos,T) 操作前提:串 S 存在,1 ≤ pos≤ StrLength( S)+ 1 。 操作结果:在串 S 的第 pos 个字符之前插入串 T。 ( 3 ) StrDelete( S,pos,len) 操作前提:串 S 存在,1 ≤ pos≤ StrLength( S)+ 1 。 操作结果:从串 S 中删除第 pos 个字符起长度为 len 的子串。 ( 4 ) StrCopy( S,T) 操作前提:串 S 存在。 操作结果:由串 T 复制得串 S。 ( 5 ) StrEmpty( S) 操作前提:串 S 存在。 操作结果:若串 S 为空串,则返回 TRUE,否则返回 FALSE。 ( 6 ) StrCompare( S,T) 操作前提:串 S 和 T 存在。 操作结果:若 S>T,则返回值>0 ;如 S=T,则返回值=0 ;若 S<T,则返回值<0 。 ( 7 ) StrLength( S) 操作前提:串 S 存在。 操作结果:返回串 S 的长度,即串 S 中的字符个数。 ( 8 ) StrClear( S) 操作前提:串 S 存在。 操作结果:将 S 清为空串。 ( 9 ) StrCat( S,T) 操作前提:串 S 和 T 存在。 操作结果:将串 T 的值连接在串 S 的后面。 ( 10 ) SubString( Sub,S,pos,len) 操作前提:串 S 存在,1 ≤ pos≤ StrLength( S)且 1 ≤ len≤ StrLength( S)- pos+1 操作结果:用 Sub 返回串 S 的第 pos 个字符起长度为 len 的子串。 ( 11 ) StrIndex( S,pos,T) 操作前提:串 S 和 T 存在,T 是非空串,1 ≤ pos≤ StrLength( S)。 操作结果:若串 S 中存在和串 T 相同的子串,则返回它在串 S 中第 pos 个字符 之后第一次出现的位置;否则返回 0 。 ( 12 ) StrReplace( S,T,V) 操作前提:串 S、 T 和 V 存在且 T 是非空串。 操作结果:用 V 替换串 S 中出现的所有与 T 相等的不重叠的子串。 ( 13 ) StrDestroy( S) 操作前提:串 S 存在。 操作结果:销毁串 S。 }ADT string

存储结构

顺序存储

用一组地址连续的存储单元存储串值的字符序列

堆分配存储表示仍然以一组地址连续的存储单元存放串值的字符序列,但它们的存储空间是在程序执行过程中动态分配得到的。

typedef struct{
    char *ch;   //按串长分配存储区,ch指向串的基地址
    int length; //串的长度
}HString;

“堆”,C语言中的自由存储区,并用malloc()和free()函数来完成。

若分配成功则返回一个指向起始地址的指针,作为串的基地址,这个串由ch指针来指示;若分配失败,则返回NULL。已分配的空间可用free()释放掉。

链式存储

  1. 采用链表方式存储串值
  2. 串的特殊性:每个元素只有一个字符
  3. 具体实现:每个结点既可以存放一个字符, 也可以存放多个字符。每个结点称为块,整个链表称为块链结构

基本操作

StrAssign(&T, chars): 赋值操作。把串T赋值为 chars Strcopy(&T, S): 复制操作。由串S复制得到串T。 StrEmpty(S): 判空操作。若S为空串,则返回TRUE,否则返回 FALSE StrCompare(S,T): 比较操作。若S>T,则返回值>0;若S=T,则返回值=0;若S<T,则返回值<0。 StrEngth(S): 求串长。返回串S的元素个数 Substring(&Sub,S,pos,1en):求子串。用Sub返回串S的第pos个字符起长度为len的子串。 Concat(&T,S1,S2): 串联接。用T返回由S1和S2联接而成的新串。 Index(S,T): 定位操作。若主串S中存在与串T值相同的子串,则返回它在主串S中第一次出现的位置;否则函数值为0 Clearstring(&S): 清空操作。将S清为空串 Destroystring(&S): 销毁串。将串S销毁

匹配模式算法

1.定义:子串的定位操作,即求子串(常称模式串)在主串中的位置

2.简单的模式匹配算法

int Index(SString S, SString T){
    int i = 1, 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;
    }
}

算法的最坏时间复杂度为O ( n m ) O(nm)O(nm),其中n nn和m mm分别为主串和模式串的长度

3.KMP算法

从分析模式本身的结构着手,如果已匹配相等的前缀序列中有某个后缀正好是模式的前缀,那么就可以将模式向后滑动到与这些相等字符对齐的位置,主串i指针无须回溯,并继续从该位置开始进行比较。而模式向后滑动位数的计算仅与模式本身的结构有关,与主串无关。仅仅后移模式串,比较指针不回溯

void get_nextval(String T, int *nextval){
    int i = 1, j = 0;
    nextval[1] = 0;
    while (i < T.length){
        if(j==0 || T.ch[i]==T.ch[j]){   //ch[i]表示后缀的单个字符,ch[j]表示前缀的单个字符
            ++i; ++j;

            if(T.ch[i] != T.ch[j]){ //若当前字符与前缀字符不同
                nextval[i] = j; //则当前的j为nextval在i位置的值
            }else{
                //如果与前缀字符相同
                //则将前缀字符的nextval值给nextval在i位置上的值
                nextval[i] = nextval[j];
            }
        }else{
            j = nextval[j]; //否则令j = next[j],j值回溯,循环继续
        }
    }
}