串的表示和实现(串的堆分配存储、串的定长顺序存储结构)

来源:互联网 发布:win域名备案 编辑:程序博客网 时间:2024/06/01 10:25
在C 语言中,字符串存于字符型数组中。无论数组有多大,用数值0 表示串结束。图
41 表示了“but”字符串在C 语言中的存储结构。
其中数组a 的定义为
char a[10];
C 语言还在库函数string.h 中提供了许多串处理
的基本操作,如求串长函数strlen()、串拷贝函数
strcpy()等。
算法语言本身提供的字符串存储结构及其基本操作不一定能满足实际应用的需要,我

们往往还要根据具体情况另外定义字符串的存储结构及基于该存储结构的基本操作。


// c4-1.h 串的定长顺序存储结构(见图4.2)#define MAX_STR_LEN 40 // 用户可在255(1个字节)以内定义最大串长typedef char SString[MAX_STR_LEN+1]; // 0号单元存放串的长度

// bo4-1.cpp 串采用定长顺序存储结构(由c4-1.h定义)的基本操作(13个),包括算法4.2,4.3,4.5// SString是数组,故不需引用类型#define DestroyString ClearString // DestroyString()与ClearString()作用相同Status StrAssign(SString T,char *chars){ // 生成一个其值等于chars的串Tint i;if(strlen(chars)>MAX_STR_LEN)return ERROR;else{T[0]=strlen(chars);for(i=1;i<=T[0];i++)T[i]=*(chars+i-1);return OK;}}void StrCopy(SString T,SString S){ // 由串S复制得串Tint i;for(i=0;i<=S[0];i++)T[i]=S[i];}Status StrEmpty(SString S){ // 若S为空串,则返回TRUE;否则返回FALSEif(S[0]==0)return TRUE;elsereturn FALSE;}int StrCompare(SString S,SString T){// 初始条件:串S和T存在。操作结果:若S>T,则返回值>0;若S=T,则返回值=0;若S<T,则返回值<0int i;for(i=1;i<=S[0]&&i<=T[0];++i)if(S[i]!=T[i])return S[i]-T[i];return S[0]-T[0];}int StrLength(SString S){ // 返回串S的元素个数return S[0];}void ClearString(SString S){ // 初始条件:串S存在。操作结果:将S清为空串(见图4.3)S[0]=0; // 令串长为零}Status Concat(SString T,SString S1,SString S2) // 算法4.2改{ // 用T返回S1和S2联接而成的新串。若未截断,则返回TRUE;否则返回FALSEint i;if(S1[0]+S2[0]<=MAX_STR_LEN){ // 未截断for(i=1;i<=S1[0];i++)T[i]=S1[i];for(i=1;i<=S2[0];i++)T[S1[0]+i]=S2[i];T[0]=S1[0]+S2[0];return TRUE;}else{ // 截断S2for(i=1;i<=S1[0];i++)T[i]=S1[i];for(i=1;i<=MAX_STR_LEN-S1[0];i++)T[S1[0]+i]=S2[i];T[0]=MAX_STR_LEN;return FALSE;}}Status SubString(SString Sub,SString S,int pos,int len){ // 用Sub返回串S的自第pos个字符起长度为len的子串。算法4.3int i;if(pos<1||pos>S[0]||len<0||len>S[0]-pos+1)return ERROR;for(i=1;i<=len;i++)Sub[i]=S[pos+i-1];Sub[0]=len;return OK;}int Index(SString S,SString T,int pos){ // 返回子串T在主串S中第pos个字符之后的位置。若不存在,则函数值为0。// 其中,T非空,1≤pos≤StrLength(S)。算法4.5int i,j;if(1<=pos&&pos<=S[0]){i=pos;j=1;while(i<=S[0]&&j<=T[0])if(S[i]==T[j]) // 继续比较后继字符{++i;++j;}else // 指针后退重新开始匹配{i=i-j+2;j=1;}if(j>T[0])return i-T[0];elsereturn 0;}elsereturn 0;}Status StrInsert(SString S,int pos,SString T){ // 初始条件:串S和T存在,1≤pos≤StrLength(S)+1// 操作结果:在串S的第pos个字符之前插入串T。完全插入返回TRUE,部分插入返回FALSEint i;if(pos<1||pos>S[0]+1)return ERROR;if(S[0]+T[0]<=MAX_STR_LEN){ // 完全插入for(i=S[0];i>=pos;i--)S[i+T[0]]=S[i];for(i=pos;i<pos+T[0];i++)S[i]=T[i-pos+1];S[0]+=T[0];return TRUE;}else{ // 部分插入for(i=MAX_STR_LEN;i>=pos+T[0];i--)S[i]=S[i-T[0]];for(i=pos;i<pos+T[0]&&i<=MAX_STR_LEN;i++)S[i]=T[i-pos+1];S[0]=MAX_STR_LEN;return FALSE;}}Status StrDelete(SString S,int pos,int len){ // 初始条件:串S存在,1≤pos≤StrLength(S)-len+1// 操作结果:从串S中删除自第pos个字符起长度为len的子串int i;if(pos<1||pos>S[0]-len+1||len<0)return ERROR;for(i=pos+len;i<=S[0];i++)S[i-len]=S[i];S[0]-=len;return OK;}Status Replace(SString S,SString T,SString V) // 此函数与串的存储结构无关{ // 初始条件:串S,T和V存在,T是非空串// 操作结果:用V替换主串S中出现的所有与T相等的不重叠的子串int i=1; // 从串S的第一个字符起查找串TStatus k;if(StrEmpty(T)) // T是空串return ERROR;do{i=Index(S,T,i); // 结果i为从上一个i之后找到的子串T的位置if(i) // 串S中存在串T{StrDelete(S,i,StrLength(T)); // 删除该串Tk=StrInsert(S,i,V); // 在原串T的位置插入串Vif(!k) // 不能完全插入return ERROR;i+=StrLength(V); // 在插入的串V后面继续查找串T}}while(i);return OK;}void StrPrint(SString T){ // 输出字符串T。另加int i;for(i=1;i<=T[0];i++)printf("%c",T[i]);printf("\n");}

// main4-1.cpp 检验bo4-1.cpp的主程序#include"c1.h"#include"c4-1.h"#include"bo4-1.cpp"void main(){int i,j;Status k;char s,c[MAX_STR_LEN+1];SString t,s1,s2;printf("请输入串s1: ");gets(c);k=StrAssign(s1,c);if(!k){printf("串长超过MAX_STR_LEN(=%d)\n",MAX_STR_LEN);exit(0);}printf("串长为%d 串空否?%d(1:是0:否)\n",StrLength(s1),StrEmpty(s1));StrCopy(s2,s1);printf("拷贝s1生成的串为");StrPrint(s2);printf("请输入串s2: ");gets(c);k=StrAssign(s2,c);if(!k){printf("串长超过MAX_STR_LEN(%d)\n",MAX_STR_LEN);exit(0);}i=StrCompare(s1,s2);if(i<0)s='<';else if(i==0)s='=';elses='>';printf("串s1%c串s2\n",s);k=Concat(t,s1,s2);printf("串s1联接串s2得到的串t为");StrPrint(t);if(k==FALSE)printf("串t有截断\n");ClearString(s1);printf("清为空串后,串s1为");StrPrint(s1);printf("串长为%d 串空否?%d(1:是0:否)\n",StrLength(s1),StrEmpty(s1));printf("求串t的子串,请输入子串的起始位置,子串长度: ");scanf("%d,%d",&i,&j);k=SubString(s2,t,i,j);if(k){printf("子串s2为");StrPrint(s2);}printf("从串t的第pos个字符起,删除len个字符,请输入pos,len: ");scanf("%d,%d",&i,&j);StrDelete(t,i,j);printf("删除后的串t为");StrPrint(t);i=StrLength(s2)/2;StrInsert(s2,i,t);printf("在串s2的第%d个字符之前插入串t后,串s2为\n",i);StrPrint(s2);i=Index(s2,t,1);printf("s2的第%d个字母起和t第一次匹配\n",i);SubString(t,s2,1,1);printf("串t为");StrPrint(t);Concat(s1,t,t);printf("串s1为");StrPrint(s1);k=Replace(s2,t,s1);if(k) // 替换成功{printf("用串s1取代串s2中和串t相同的不重叠的串后,串s2为");StrPrint(s2);}DestroyString(s2); // 销毁操作同清空}

代码的运行结果如下:

/*请输入串s1: ABCD串长为4 串空否?0(1:是0:否)拷贝s1生成的串为ABCD请输入串s2: 123456串s1>串s2串s1联接串s2得到的串t为ABCD123456清为空串后,串s1为串长为0 串空否?1(1:是0:否)求串t的子串,请输入子串的起始位置,子串长度: 3,7子串s2为CD12345从串t的第pos个字符起,删除len个字符,请输入pos,len: 4,4删除后的串t为ABC456在串s2的第3个字符之前插入串t后,串s2为CDABC45612345s2的第3个字母起和t第一次匹配串t为C串s1为CC用串s1取代串s2中和串t相同的不重叠的串后,串s2为CCDABCC45612345Press any key to continue*/

// c4-2.h 串的堆分配存储(见图4.4)struct HString{char *ch; // 若是非空串,则按串长分配存储区;否则ch为NULLint length; // 串长度};

// bo4-2.cpp 串采用堆分配存储结构(由c4-2.h定义)的基本操作(14个)。包括算法4.1,4.4#define DestroyString ClearString // DestroyString()与ClearString()作用相同void StrAssign(HString &T,char *chars){ // 生成一个其值等于串常量chars的串T(见图4.5)int i,j;if(T.ch)free(T.ch); // 释放T原有空间i=strlen(chars); // 求chars的长度iif(!i){ // chars的长度为0T.ch=NULL;T.length=0;}else{ // chars的长度不为0T.ch=(char*)malloc(i*sizeof(char)); // 分配串空间if(!T.ch) // 分配串空间失败exit(OVERFLOW);for(j=0;j<i;j++) // 拷贝串T.ch[j]=chars[j];T.length=i;}}void StrCopy(HString &T,HString S){ // 初始条件:串S存在。操作结果:由串S复制得串Tint i;if(T.ch)free(T.ch); // 释放T原有空间T.ch=(char*)malloc(S.length*sizeof(char)); // 分配串空间if(!T.ch) // 分配串空间失败exit(OVERFLOW);for(i=0;i<S.length;i++) // 拷贝串T.ch[i]=S.ch[i];T.length=S.length;}Status StrEmpty(HString S){ // 初始条件:串S存在。操作结果:若S为空串,则返回TRUE;否则返回FALSEif(S.length==0&&S.ch==NULL)return TRUE;elsereturn FALSE;}int StrCompare(HString S,HString T){ // 若S>T,则返回值>0;若S=T,则返回值=0;若S<T,则返回值<0int i;for(i=0;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 StrLength(HString S){ // 返回S的元素个数,称为串的长度return S.length;}void ClearString(HString &S){ // 将S清为空串(见图4.6)free(S.ch);S.ch=NULL;S.length=0;}void Concat(HString &T,HString S1,HString S2){ // 用T返回由S1和S2联接而成的新串int i;if(T.ch)free(T.ch); // 释放旧空间T.length=S1.length+S2.length;T.ch=(char *)malloc(T.length*sizeof(char));if(!T.ch)exit(OVERFLOW);for(i=0;i<S1.length;i++)T.ch[i]=S1.ch[i];for(i=0;i<S2.length;i++)T.ch[S1.length+i]=S2.ch[i];}Status SubString(HString &Sub, HString S,int pos,int len){ // 用Sub返回串S的第pos个字符起长度为len的子串。// 其中,1≤pos≤StrLength(S)且0≤len≤StrLength(S)-pos+1int i;if(pos<1||pos>S.length||len<0||len>S.length-pos+1)return ERROR;if(Sub.ch)free(Sub.ch); // 释放旧空间if(!len) // 空子串{Sub.ch=NULL;Sub.length=0;}else{ // 完整子串Sub.ch=(char*)malloc(len*sizeof(char));if(!Sub.ch)exit(OVERFLOW);for(i=0;i<=len-1;i++)Sub.ch[i]=S.ch[pos-1+i];Sub.length=len;}return OK;}void InitString(HString &T){ // 初始化(产生空串)字符串T。另加T.length=0;T.ch=NULL;}int Index(HString S,HString T,int pos) // 算法4.1{ // T为非空串。若主串S中第pos个字符之后存在与T相等的子串,// 则返回第一个这样的子串在S中的位置;否则返回0int n,m,i;HString sub;InitString(sub);if(pos>0){n=StrLength(S);m=StrLength(T);i=pos;while(i<=n-m+1){SubString(sub,S,i,m);if(StrCompare(sub,T)!=0)++i;elsereturn i;}}return 0;}Status StrInsert(HString &S,int pos,HString T) // 算法4.4{ // 1≤pos≤StrLength(S)+1。在串S的第pos个字符之前插入串Tint i;if(pos<1||pos>S.length+1) // pos不合法return ERROR;if(T.length) // T非空,则重新分配空间,插入T{S.ch=(char*)realloc(S.ch,(S.length+T.length)*sizeof(char));if(!S.ch)exit(OVERFLOW);for(i=S.length-1;i>=pos-1;--i) // 为插入T而腾出位置S.ch[i+T.length]=S.ch[i];for(i=0;i<T.length;i++)S.ch[pos-1+i]=T.ch[i]; // 插入TS.length+=T.length;}return OK;}Status StrDelete(HString &S,int pos,int len){ // 从串S中删除第pos个字符起长度为len的子串int i;if(S.length<pos+len-1)return ERROR;for(i=pos-1;i<=S.length-len;i++)S.ch[i]=S.ch[i+len];S.length-=len;S.ch=(char*)realloc(S.ch,S.length*sizeof(char));return OK;}Status Replace(HString &S,HString T,HString V) // 此函数与串的存储结构无关{ // 初始条件:串S,T和V存在,T是非空串// 操作结果:用V替换主串S中出现的所有与T相等的不重叠的子串int i=1; // 从串S的第一个字符起查找串Tif(StrEmpty(T)) // T是空串return ERROR;do{i=Index(S,T,i); // 结果i为从上一个i之后找到的子串T的位置if(i) // 串S中存在串T{StrDelete(S,i,StrLength(T)); // 删除该串TStrInsert(S,i,V); // 在原串T的位置插入串Vi+=StrLength(V); // 在插入的串V后面继续查找串T}}while(i);return OK;}void StrPrint(HString T){ // 输出T字符串。另加int i;for(i=0;i<T.length;i++)printf("%c",T.ch[i]);printf("\n");}


// main4-2.cpp 检验bo4-2.cpp的主程序#include"c1.h"#include"c4-2.h"#include"bo4-2.cpp"void main(){int i;char c,*p="God bye!",*q="God luck!";HString t,s,r;InitString(t); // HString类型必须初始化InitString(s);InitString(r);StrAssign(t,p);printf("串t为");StrPrint(t);printf("串长为%d 串空否?%d(1:空0:否)\n",StrLength(t),StrEmpty(t));StrAssign(s,q);printf("串s为");StrPrint(s);i=StrCompare(s,t);if(i<0)c='<';else if(i==0)c='=';elsec='>';printf("串s%c串t\n",c);Concat(r,t,s);printf("串t联接串s产生的串r为");StrPrint(r);StrAssign(s,"oo");printf("串s为");StrPrint(s);StrAssign(t,"o");printf("串t为");StrPrint(t);Replace(r,t,s);printf("把串r中和串t相同的子串用串s代替后,串r为");StrPrint(r);ClearString(s);printf("串s清空后,串长为%d 空否?%d(1:空0:否)\n",StrLength(s),StrEmpty(s));SubString(s,r,6,4);printf("串s为从串r的第6个字符起的4个字符,长度为%d 串s为",s.length);StrPrint(s);StrCopy(t,r);printf("复制串t为串r,串t为");StrPrint(t);StrInsert(t,6,s);printf("在串t的第6个字符前插入串s后,串t为");StrPrint(t);StrDelete(t,1,5);printf("从串t的第1个字符起删除5个字符后,串t为");StrPrint(t);printf("%d是从串t的第1个字符起,和串s相同的第1个子串的位置\n",Index(t,s,1));printf("%d是从串t的第2个字符起,和串s相同的第1个子串的位置\n",Index(t,s,2));DestroyString(t); // 销毁操作同清空}

代码的运行结果如下:

串t为God bye!串长为8 串空否?0(1:空0:否)串s为God luck!串s>串t串t联接串s产生的串r为God bye!God luck!串s为oo串t为o把串r中和串t相同的子串用串s代替后,串r为Good bye!Good luck!串s清空后,串长为0 空否?1(1:空0:否)串s为从串r的第6个字符起的4个字符,长度为4 串s为bye!复制串t为串r,串t为Good bye!Good luck!在串t的第6个字符前插入串s后,串t为Good bye!bye!Good luck!从串t的第1个字符起删除5个字符后,串t为bye!bye!Good luck!1是从串t的第1个字符起,和串s相同的第1个子串的位置5是从串t的第2个字符起,和串s相同的第1个子串的位置Press any key to continue

串的堆分配存储结构(由c4-2.h 定义)根据串的长度,动态地分配存储空间。这样既
保证满足需要,又不浪费空间,对于串长也没有限制。串的定长存储结构(由c4-1.h 定义)
就没有这样灵活了。在Concat()、StrInsert()和Replace()中,总要检查串是否被截断,且
对于短串的情况,空间浪费较大。故堆分配存储结构较好。

0 0