字符串
串的定义
串,即字符串(String)是由零个或多个字符组成的有限序列,一般记为S=‘a1a2a3a4’ (n≥0)
其中,S是串名,单引号括起来的是字符序列是串的值;ai可以是字母、数字或是其他字符;串中字符的个数n称为串的长度。n=0时的串称为空串(用 ∅ 表示)。
例:(不同语言可能使用的边界符不同,Java、c等使用双引号(“ ”)Python等使用单引号(’ ‘))
S = "HelloWorld!"
T = 'Hello World!'
(空格也占一字符)
常用术语:
子串:串中任意个连续的字符组成的子序列(也可以是零个)
主串:包含子串的串
字符在主串中的位置:字符在串中的序号(一般来说讨论的是第一次出现的位置)
子串在主串中的位置:子串的第一个字符在主串中的位置
空串 V.S 空格串
空串:M = ''
空格串:N = ' '
N是由三个空格字符组成的空格串,每个空格字符占1B
串 V.S 线性表
串是一种特殊的线性表,数据元素之间呈线性关系
串的数据对象限定为字符集(如中文字符、英文字符、数字字符、标点字符等)
串的基本操作,如增删改查等通常以子串为操作对象
基本操作
假设有串T = ""
,S = "Hello World!"
,W = "llo "
StrAssign(&T,char)
赋值操作。把串T赋值为chars。
StrCopy(&T,S)
复制操作。由串S复制得到串T。
StrEmpty(S)
判空操作。若S为空串,则返回TRUE,否则返回FALSE。
StrLength(S)
求串长。返回串S的元素个数。
ClearString(&S)
清空操作。将S清空为空串。
DestroyString(&S)
销毁串。将串S销毁(回收存储空间)。
注:清空操作和销毁操作的区别:
清空串:将S清空为空串,后续还可以再存放数据
销毁串:将S原本分配的空间回收,后续想要再次存放数据需要重新分配内存空间
Concat(&T,S1,S2)
串联接。用T返回由S1和S2联接而成的新串,放入T中。
eg. 执行Concat(&T,S,W)后,T = "Hello World!llo "
//如果经常进行联接操作,需要考虑使用容易进行存储空间扩展的数据结构进行串的定义
SubString(&Sub,S,pos,len)
求子串。用Sub返回串S的第pos个字符起长度为len的子串。
eg. 执行SubString(&T,S,4,4)后,T = “lo w”
Index(S,T)
定位操作。若主串S中存在与串T值相同的子串,则返回它在主串S中第一次出现的位置;否则函数值为0。
eg. 执行Index(S,W)后,返回值为3
StrCompare(S,T)
比较操作。若S > T,则返回值 > 0;若S = T,则返回值 = 0;若S < T,则返回值 < 0 。
字符集编码
任何数据存到计算机中一定是二进制数。需要确定一个字符和二进制数的对应规则 – ”编码“
”字符集“:
英文字符 – ASCII字符集
中英文字符 – Unicode字符集 (基于同一个字符集,可以有多种编码方案,如:UTF-16,UTF-8)
[!IMPORTANT]
采用不用的编码方式,每个字符所占的空间不同,考研中只需要默认每个字符占1B即可
[拓展] 乱码问题
产生原因:解码方式错误
在文件中,原本采用了一个编码方案进行映射并保存了映射结果,但是在使用另一个软件进行打开时,软件认为采用的是另一种编码规则并使用该规则进行解码,所以会显示乱码
存储结构
顺序存储
ElemType→char
//静态数组实现(定长顺序存储)
#define MAXLEN 255 //预定义最大串长255
typedef struct{
char ch[MAXLEN]; //每个分量存储一个字符
int length; //串的实际长度
}SString;
//动态数组实现(堆分配存储)
typedef struct{
char *ch; //按串长分配存储区,ch指向串的基地址
int lenth; //串的长度
}HString;
bool InitHString(HString &S){
S.ch = (char*)malloc(MAXLEN * sizeof(char)); //空间回收时需要进行free操作
if(ch == NULL) return false;
S.length = 0;
return true;
}
方案一:
使用变量length存放字符串长度
方案二:
使用 ch[0] 充当 length
优点:各字符的位序和数组下标相同,长度读取方便
缺点:ch 中每个元素只占 1B 大小,只能表示数字 0~255 ,因此使用这种方法要求字符串长度不超过255
方案三:
没有 length 变量,以字符 ’\0‘ 表示结尾(对应 ASCII 码的 0 )
缺点:长度读取不便(每次都需要遍历)
方案四:
(方案一和方案二的结合)
ch[0] 废弃不用,声明一个专门的 int 型变量 length 记录字符串长度
链式存储
typedef struct StringNode{
char ch; //每个结点存一个字符(占1B)
struct StringNode * next; //指向下一个字符(每个指针占4B)
}StringNode,* String;
缺点:存储密度低
优化:
typedef struct StringNode{
char ch[4]; //每个结点存四个字符(占4B)(也可以更多)
struct StringNode * next; //指向下一组字符(每个指针占4B)
}StringNode,* String;
注意:如果最后一个结点存不满,可以使用一些特殊的字符进行填充
基于顺序存储实现的基本操作
StrAssign(&T,char)
赋值操作。把串T赋值为chars。
StrCopy(&T,S)
复制操作。由串S复制得到串T。
StrEmpty(S)
判空操作。若S为空串,则返回TRUE,否则返回FALSE。
StrLength(S)
求串长。返回串S的元素个数。
ClearString(&S)
清空操作。将S清空为空串。
DestroyString(&S)
销毁串。将串S销毁(回收存储空间)。
Concat(&T,S1,S2)
串联接。用T返回由S1和S2联接而成的新串,放入T中。
SubString(&Sub,S,pos,len)
求子串。用Sub返回串S的第pos个字符起长度为len的子串。
Index(S,T)
定位操作。若主串S中存在与串T值相同的子串,则返回它在主串S中第一次出现的位置;否则函数值为0。
StrCompare(S,T)
比较操作。若S > T,则返回值 > 0;若S = T,则返回值 = 0;若S < T,则返回值 < 0 。
//使用顺序存储中的第四种方案进行存储:ch[0]不存放数据,并使用length进行长度存储
#define MAXLEN 255 //预定义最大串长255
typedef struct{
char ch[MAXLEN]; //每个分量存储一个字符
int length; //串的实际长度
}SString;
bool SubString(SString &Sub,SString S,int pos,int len){
//子串范围越界
if(pos+len-1 > S.length) return false;
for(int i = pos;i < pos+len;i++){
Sub.ch[i-pos+1] = S.ch[i];
}
Sub.length = len;
return true;
}
int StrCompare(SString S,SString T){
for(int i = 1;i<=S.length&&i<=T.length;i++){
if(S.ch[i]!=T.ch[i]) return S.ch[i] - T.ch[i];
}
//扫描过的字符都相同,则长度长的串更大
return S.length - T.length;
}
//定位操作,每次从主串中取与长度与子串长度相同的子串
int Index(SString S,SString T){
int i = 1;n = StrLength(S),m = StrLength(T);
SString sub; //用于暂存当前取出的子串
while(i<=n-m+1){
SubString(sub,S,i,m);
if(StrCompare(sub,T)!=0) ++i;
else return i; //返回子串在主串中的位置
}
return 0; //S中不存在与T相等的子串
}